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>
2392 lines
83 KiB
C++
2392 lines
83 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 "bitfield.hh"
|
|
#include "funcdata.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
BitFieldNodeState::BitFieldNodeState(const BitRange &used,Varnode *vn,const TypeBitField *fld)
|
|
: bitsUsed(used), bitsField(fld->bits,used.byteOffset,used.byteSize)
|
|
{
|
|
node = vn;
|
|
field = fld;
|
|
origLeastSigBit = bitsField.leastSigBit;
|
|
isSignExtended = (field->type->getMetatype() == TYPE_INT) && bitsField.isMostSignificant();
|
|
}
|
|
|
|
BitFieldNodeState::BitFieldNodeState(const BitRange &used,Varnode *vn,int4 leastSig,int4 numBits)
|
|
: bitsUsed(used), bitsField(used.byteOffset,used.byteSize,leastSig,numBits,used.isBigEndian)
|
|
{
|
|
node = vn;
|
|
field = (const TypeBitField *)0;
|
|
origLeastSigBit = bitsField.leastSigBit;
|
|
isSignExtended = false;
|
|
}
|
|
|
|
/// Copy another state, but replace \b bitsField
|
|
/// \param copy is the state to copy
|
|
/// \param newField is the new range for \b bitsField
|
|
/// \param vn is the Varnode holding the new range
|
|
/// \param sgnExt is the new state of sign extension
|
|
BitFieldNodeState::BitFieldNodeState(const BitFieldNodeState ©,const BitRange &newField,Varnode *vn,bool sgnExt)
|
|
: bitsUsed(copy.bitsUsed), bitsField(newField)
|
|
{
|
|
node = vn;
|
|
field = copy.field;
|
|
origLeastSigBit = copy.origLeastSigBit;
|
|
isSignExtended = sgnExt;
|
|
}
|
|
|
|
/// A BitFieldNodeState is constructed for each bitfield that the Varnode overlaps.
|
|
/// Holes between bitfields can also have a BitFieldNodeState.
|
|
/// \param vn is the given Varnode
|
|
/// \param followHoles is \b true if a record for each hole should be created
|
|
void BitFieldTransform::establishFields(Varnode *vn,bool followHoles)
|
|
|
|
{
|
|
int4 vnBitSize = vn->getSize() * 8;
|
|
BitRange bitrange(initialOffset,vn->getSize(),0,vnBitSize,isBigEndian);
|
|
vector<BitFieldTriple> overlap;
|
|
parentStruct->collectBitFields(0, overlap, initialOffset, vn->getSize());
|
|
sort(overlap.begin(),overlap.end(),BitFieldTriple::compare);
|
|
int4 pos = 0;
|
|
for(int4 i=0;i<overlap.size();++i) { // Iterate from least significant to most
|
|
BitFieldTriple &triple(overlap[i]);
|
|
int4 fieldPos = bitrange.translateLSB(triple.bitfield->bits);
|
|
int4 fieldEnd = fieldPos + triple.bitfield->bits.numBits;
|
|
if (fieldPos > vnBitSize)
|
|
fieldPos = vnBitSize;
|
|
if (fieldEnd > vnBitSize)
|
|
fieldEnd = vnBitSize;
|
|
if (fieldPos > pos) { // We have a hole
|
|
if (followHoles)
|
|
workList.emplace_back(bitrange,vn,pos,(fieldPos - pos));
|
|
pos = fieldPos;
|
|
}
|
|
int4 code = bitrange.overlapTest(triple.bitfield->bits);
|
|
if (code == 0 || code == 3)// Note if field is properly contained in vn
|
|
workList.emplace_back(bitrange,vn,triple.bitfield); // Field is properly contained in vn
|
|
else {
|
|
if (followHoles)
|
|
workList.emplace_back(bitrange,vn,pos,(fieldEnd-pos));
|
|
}
|
|
pos = fieldEnd;
|
|
}
|
|
if (pos < vnBitSize && followHoles) {
|
|
workList.emplace_back(bitrange,vn,pos,vnBitSize-pos); // Final hole
|
|
}
|
|
}
|
|
|
|
/// \param f is the containing function
|
|
/// \param dt is the bitfield data-type
|
|
/// \param off is any initial byte offset into the data-type for the root Varnode
|
|
BitFieldTransform::BitFieldTransform(Funcdata *f,Datatype *dt,int4 off)
|
|
|
|
{
|
|
func = f;
|
|
parentStruct = (TypeStruct *)0;
|
|
containerSize = -1;
|
|
initialOffset = -1;
|
|
if (dt->getMetatype() == TYPE_STRUCT) {
|
|
parentStruct = (TypeStruct *)dt;
|
|
initialOffset = off;
|
|
}
|
|
else if (dt->getMetatype() == TYPE_PARTIALSTRUCT) {
|
|
TypePartialStruct *part = (TypePartialStruct *)dt;
|
|
dt = part->getParent();
|
|
if (dt->getMetatype() == TYPE_STRUCT) {
|
|
parentStruct = (TypeStruct *)dt;
|
|
initialOffset = off + part->getOffset();
|
|
}
|
|
}
|
|
isBigEndian = f->getArch()->getDefaultDataSpace()->isBigEndian();
|
|
}
|
|
|
|
/// If the state is for a partial field whose storage location is overwritten
|
|
/// later in the same basic block, return \b true
|
|
/// \param state is the field
|
|
/// \return \b true if a partial field has been overwritten
|
|
bool BitFieldInsertTransform::isOverwrittenPartial(const BitFieldNodeState &state)
|
|
|
|
{
|
|
if (state.field != (const TypeBitField *)0)
|
|
return false; // Field is not partial
|
|
if (state.bitsField.byteSize > sizeof(uintb))
|
|
return false;
|
|
if (finalWriteOp->code() != CPUI_STORE) {
|
|
// Reconstruct the original bit range
|
|
BitRange curRange(initialOffset,mappedVn->getSize(),state.origLeastSigBit,state.bitsField.numBits,isBigEndian);
|
|
return findOverwrite(mappedVn,finalWriteOp->getParent(),curRange);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BitFieldInsertTransform::checkPulledOriginalValue(BitFieldNodeState &state)
|
|
|
|
{
|
|
if (!state.node->isWritten()) return false;
|
|
PcodeOp *op = state.node->getDef();
|
|
OpCode opc = op->code();
|
|
if (opc != CPUI_ZPULL && opc != CPUI_SPULL) return false;
|
|
int4 pos = (int4)op->getIn(1)->getOffset();
|
|
int4 numbits = (int4)op->getIn(2)->getOffset();
|
|
if (pos != state.bitsField.leastSigBit) return false;
|
|
if (numbits != state.bitsField.numBits) return false;
|
|
return checkOriginalBase(op->getIn(0));
|
|
}
|
|
|
|
/// If the Varnode is a the initial value of the storage being inserted into, return \b true.
|
|
/// This can be either the result of the initial LOAD or the mapped storage location being read directly.
|
|
/// \param vn is the given Varnode to check
|
|
/// \return \b true if it is the original value
|
|
bool BitFieldInsertTransform::checkOriginalBase(Varnode *vn)
|
|
|
|
{
|
|
if (finalWriteOp->code() == CPUI_STORE) {
|
|
if (!vn->isWritten()) return false;
|
|
PcodeOp *loadOp = vn->getDef();
|
|
if (loadOp->code() != CPUI_LOAD) return false;
|
|
if (!pointerEquality(loadOp->getIn(1), finalWriteOp->getIn(1))) return false;
|
|
if (loadOp->getParent() != finalWriteOp->getParent()) return false;
|
|
}
|
|
else {
|
|
if (mappedVn == vn) return false;
|
|
if (mappedVn->getAddr() != vn->getAddr() || mappedVn->getSize() != vn->getSize())
|
|
return false;
|
|
if (!vn->isAddrTied()) return false;
|
|
}
|
|
originalValue = vn;
|
|
return true;
|
|
}
|
|
|
|
/// \param state is the given Varnode
|
|
/// \return \b true if the Varnode contains the \e original \e value for the bitfield(s)
|
|
bool BitFieldInsertTransform::isOriginalValue(BitFieldNodeState &state)
|
|
|
|
{
|
|
if (state.bitsField.leastSigBit != state.origLeastSigBit) return false;
|
|
if (state.node == originalValue) return true;
|
|
if (checkPulledOriginalValue(state))
|
|
return true;
|
|
return checkOriginalBase(state.node);
|
|
}
|
|
|
|
/// If the state is not following a specific field, \b false is returned.
|
|
/// \param state gives the constant Varnode and field
|
|
/// \return \b true if an InsertRecord was created
|
|
bool BitFieldInsertTransform::addConstantWrite(BitFieldNodeState &state)
|
|
|
|
{
|
|
uintb value = state.node->getOffset();
|
|
state.node = (Varnode *)0;
|
|
if (state.field == (const TypeBitField *)0) {
|
|
return false;
|
|
}
|
|
if (state.bitsField.byteSize > sizeof(uintb)) return false;
|
|
uintb mask = state.bitsField.getMask();
|
|
value = value & mask;
|
|
value >>= state.bitsField.leastSigBit;
|
|
if (state.field->type->getMetatype() == TYPE_INT) {
|
|
value = extend_signbit(value, state.bitsField.numBits, state.bitsField.byteSize);
|
|
}
|
|
insertList.emplace_back(value,state.field->type,state.origLeastSigBit,state.field->bits.numBits);
|
|
return true;
|
|
}
|
|
|
|
/// If the state is not following a specific field, \b false is returned.
|
|
/// The state will no longer be followed.
|
|
/// \param state describes the field
|
|
/// \return \b true if an InsertRecord was created
|
|
bool BitFieldInsertTransform::addZeroOut(BitFieldNodeState &state)
|
|
|
|
{
|
|
state.node = (Varnode *)0;
|
|
if (state.field == (const TypeBitField *)0) {
|
|
return false;
|
|
}
|
|
insertList.emplace_back(0,state.field->type,state.origLeastSigBit,state.field->bits.numBits);
|
|
return true;
|
|
}
|
|
|
|
/// \param state is the specific Varnode and field
|
|
void BitFieldInsertTransform::addFieldWrite(BitFieldNodeState &state)
|
|
|
|
{
|
|
Datatype *dt = state.field->type;
|
|
if (dt->getSize() != state.node->getSize())
|
|
dt = (Datatype *)0;
|
|
insertList.emplace_back(state.node,dt,state.origLeastSigBit,state.field->bits.numBits,state.bitsField.leastSigBit);
|
|
state.node = (Varnode *)0;
|
|
}
|
|
|
|
/// The second input must be a constant mask.
|
|
/// If the mask zeroes out the field, create a zero InsertRecord.
|
|
/// If the mask fully contains the field, follow the field through the first input.
|
|
/// Otherwise return \b false.
|
|
/// \param state is the field being followed
|
|
/// \param op is the INT_AND
|
|
/// \return \b true if the field is followed or zeroed out
|
|
bool BitFieldInsertTransform::handleAndBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *cvn = op->getIn(1);
|
|
if (!cvn->isConstant()) return false;
|
|
if (state.bitsField.byteSize > sizeof(uintb)) return false;
|
|
uintb val = state.bitsField.getMask();
|
|
uintb res = val & cvn->getOffset();
|
|
if (res == val) {
|
|
state.node = op->getIn(0);
|
|
state.bitsUsed.intersectMask(cvn->getOffset()); // Update bitsUsed to indicate a bit range was masked
|
|
return true; // This field is contained in mask
|
|
}
|
|
if (res == 0) { // The field is zeroed out
|
|
return addZeroOut(state);
|
|
}
|
|
return false; // Partial zeroing
|
|
}
|
|
|
|
/// Follow the field through the input that has not masked off its bitrange.
|
|
/// If neither input has mased off the bitrange, or if both have, return \b false;
|
|
/// \param state is the field being followed
|
|
/// \param op is the INT_OR
|
|
/// \return \b true if the field is followed through a single input
|
|
bool BitFieldInsertTransform::handleOrBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (state.bitsField.byteSize > sizeof(uintb)) return false;
|
|
uintb mask = state.bitsField.getMask();
|
|
Varnode *vn0 = op->getIn(0);
|
|
Varnode *vn1 = op->getIn(1);
|
|
bool isMasked0 = (vn0->getNZMask() & mask) == 0;
|
|
bool isMasked1 = (vn1->getNZMask() & mask) == 0;
|
|
if (isMasked0 == isMasked1) {
|
|
if (vn1->isConstant()) {
|
|
if ((vn1->getNZMask() & mask) == mask) { // Or-ing constant that sets all bits of field to 1
|
|
state.node = vn1; // Follow the constant
|
|
return true;
|
|
}
|
|
}
|
|
return false; // Both inputs are unmasked (or both masked), can't follow field
|
|
}
|
|
state.node = isMasked0 ? vn1 : vn0; // Follow the unmasked Varnode
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BitFieldInsertTransform::handleAddBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (state.bitsField.byteSize > sizeof(uintb)) return false;
|
|
Varnode *vn0 = op->getIn(0);
|
|
Varnode *vn1 = op->getIn(1);
|
|
uintb mask0 = vn0->getNZMask();
|
|
uintb mask1 = vn1->getNZMask();
|
|
if ((mask0 & mask1) != 0)
|
|
return false; // Inputs are mixed, can't follow
|
|
uintb mask = state.bitsField.getMask();
|
|
bool isMasked0 = (mask0 & mask) == 0;
|
|
bool isMasked1 = (mask1 & mask) == 0;
|
|
if (isMasked0 == isMasked1) // If both unmasked (or both masked), can't follow field
|
|
return false;
|
|
state.node = isMasked0 ? vn1 : vn0;
|
|
return true;
|
|
}
|
|
|
|
/// Update the state to reflect the shift. If the field has been completely filled with
|
|
/// zeroes by the shift, create a zero InsertRecord. If the field is only partially filled,
|
|
/// return \b false.
|
|
/// \param state is the field being followed
|
|
/// \param op is the INT_LEFT
|
|
/// \return \b true if the field is followed or been zeroed out
|
|
bool BitFieldInsertTransform::handleLeftBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *cvn = op->getIn(1);
|
|
if (!cvn->isConstant()) return false;
|
|
int4 sa = cvn->getOffset();
|
|
if (sa < 0 || sa >= sizeof(uintb)*8) return false;
|
|
BitRange newRange(state.bitsField);
|
|
newRange.shift(-sa);
|
|
if (state.bitsField.numBits == newRange.numBits) { // All the bits are still present
|
|
state.bitsField = newRange;
|
|
state.bitsUsed.shift(-sa);
|
|
state.node = op->getIn(0);
|
|
return true;
|
|
}
|
|
else if (newRange.numBits == 0) { // Zero bits shifted into field
|
|
return addZeroOut(state);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Update the state to reflect the shift.
|
|
/// \param state is the field being followed
|
|
/// \param op is the INT_RIGHT
|
|
/// \return \b true if the field is followed
|
|
bool BitFieldInsertTransform::handleRightBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *cvn = op->getIn(1);
|
|
if (!cvn->isConstant()) return false;
|
|
int4 sa = cvn->getOffset();
|
|
if (sa < 0 || sa >= sizeof(uintb)*8) return false;
|
|
BitRange newRange(state.bitsField);
|
|
newRange.shift(sa);
|
|
if (state.bitsField.numBits == newRange.numBits) { // All the bits are still present
|
|
state.bitsField = newRange;
|
|
state.bitsUsed.shift(sa);
|
|
state.node = op->getIn(0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Follow the field to the input, and update the state to reflect the smaller byte container.
|
|
/// If the extension puts zero bits in field, return \b false.
|
|
/// \param state is the field being followed
|
|
/// \param op is the INT_ZEXT
|
|
/// \return \b true if field is followed
|
|
bool BitFieldInsertTransform::handleZextBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *vn = op->getIn(0);
|
|
int4 truncAmount = op->getOut()->getSize() - vn->getSize();
|
|
BitRange newRange(state.bitsField);
|
|
newRange.truncateMostSigBytes(truncAmount);
|
|
if (state.bitsField.numBits == newRange.numBits) {
|
|
state.bitsField = newRange;
|
|
state.bitsUsed.truncateMostSigBytes(truncAmount);
|
|
state.node = vn;
|
|
}
|
|
else if (state.bitsField.numBits == 0)
|
|
return addZeroOut(state); // Extended zeroes fill out the bitfield
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/// Treat INT_MULT by a power of 2 like INT_LEFT.
|
|
/// \param state is the field being followed
|
|
/// \param op is the INT_MULT
|
|
/// \return \b true if field is followed or zeroed out
|
|
bool BitFieldInsertTransform::handleMultBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *vn1 = op->getIn(1);
|
|
if (!vn1->isConstant()) return false;
|
|
uintb val = vn1->getOffset();
|
|
if (popcount(val) != 1) return false;
|
|
int4 sa = leastsigbit_set(val);
|
|
BitRange newRange(state.bitsField);
|
|
newRange.shift(-sa);
|
|
if (state.bitsField.numBits == newRange.numBits) { // All the bits are still present
|
|
state.bitsField = newRange;
|
|
state.bitsUsed.shift(-sa);
|
|
state.node = op->getIn(0);
|
|
return true;
|
|
}
|
|
else if (state.bitsField.numBits == 0) { // Zero bits shifted into field
|
|
return addZeroOut(state);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Follow the field into the input of the SUBPIECE, which may have shifted it
|
|
/// \param state is the field being followed
|
|
/// \param op is the SUBPIECE
|
|
/// \return \b true if field is followed
|
|
bool BitFieldInsertTransform::handleSubpieceBack(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *inVn = op->getIn(0);
|
|
int4 extendAmount = inVn->getSize() - state.node->getSize();
|
|
int4 sa = (int4)op->getIn(1)->getOffset() * 8;
|
|
BitRange newRange(state.bitsField);
|
|
newRange.extendBytes(extendAmount);
|
|
newRange.shift(-sa);
|
|
if (state.bitsField.numBits == newRange.numBits) { // All the bits are still present
|
|
state.bitsField = newRange;
|
|
state.bitsUsed.extendBytes(extendAmount);
|
|
state.bitsUsed.shift(-sa);
|
|
state.node = op->getIn(0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// If the call produces the bitfield structure directly, we can treat the return value
|
|
/// as the original value, even though the storage is not address tied.
|
|
/// \param state is the field being followed
|
|
/// \param op is the call
|
|
/// \return \b true if the return value can be treated as the \e original \e value
|
|
bool BitFieldInsertTransform::testCallOriginal(BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (!op->isCall()) return false;
|
|
if (finalWriteOp->code() == CPUI_STORE) return false; // If value is being STOREd, original value is not a call
|
|
if (state.bitsField.leastSigBit != state.origLeastSigBit) return false;
|
|
if (mappedVn->isAddrTied()) return false; // If value is address tied, cannot have different storage
|
|
if (originalValue != (Varnode *)0) return false; // Already found an original value
|
|
Datatype *dt = op->getOut()->getTypeDefFacing();
|
|
int4 off;
|
|
if (dt->getMetatype() == TYPE_STRUCT) {
|
|
off = 0;
|
|
}
|
|
else if (dt->getMetatype() == TYPE_PARTIALSTRUCT) {
|
|
TypePartialStruct *part = (TypePartialStruct *)dt;
|
|
off = part->getOffset();
|
|
dt = part->getParent();
|
|
}
|
|
else
|
|
return false;
|
|
if (dt != parentStruct) return false; // Check if data-type matches
|
|
if (off != initialOffset) return false;
|
|
originalValue = op->getOut();
|
|
return true;
|
|
}
|
|
|
|
/// \param state is the field to follow backward
|
|
/// \return \b true if there was no conflicting information
|
|
bool BitFieldInsertTransform::processBackward(BitFieldNodeState &state)
|
|
|
|
{
|
|
while(state.node != (Varnode *)0) {
|
|
if (state.node->isConstant()) {
|
|
return addConstantWrite(state);
|
|
}
|
|
if (isOriginalValue(state)) {
|
|
state.node = (Varnode *)0;
|
|
return true;
|
|
}
|
|
if (state.field != (const TypeBitField *)0) {
|
|
if (state.isFieldAligned()) {
|
|
addFieldWrite(state);
|
|
return true;
|
|
}
|
|
}
|
|
if (!state.node->isWritten()) return false;
|
|
PcodeOp *op = state.node->getDef();
|
|
bool liftRes;
|
|
switch(op->code()) {
|
|
case CPUI_COPY:
|
|
state.node = op->getIn(0);
|
|
liftRes = true;
|
|
break;
|
|
case CPUI_INT_ADD:
|
|
liftRes = handleAddBack(state, op);
|
|
break;
|
|
case CPUI_INT_AND:
|
|
liftRes = handleAndBack(state, op);
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
liftRes = handleLeftBack(state, op);
|
|
break;
|
|
case CPUI_INT_ZEXT:
|
|
liftRes = handleZextBack(state, op);
|
|
break;
|
|
case CPUI_INT_OR:
|
|
liftRes = handleOrBack(state, op);
|
|
break;
|
|
case CPUI_INT_MULT:
|
|
liftRes = handleMultBack(state, op);
|
|
break;
|
|
case CPUI_SUBPIECE:
|
|
liftRes = handleSubpieceBack(state, op);
|
|
break;
|
|
case CPUI_INT_SRIGHT:
|
|
liftRes = handleRightBack(state, op);
|
|
break;
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
case CPUI_CALLOTHER:
|
|
liftRes = testCallOriginal(state, op);
|
|
if (liftRes) {
|
|
state.node = (Varnode *)0; // We can treat this as if it matched the original value
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
liftRes = false;
|
|
break;
|
|
}
|
|
if (!liftRes) {
|
|
if (state.field == (const TypeBitField *)0)
|
|
return false;
|
|
if (state.bitsField.byteSize > sizeof(uintb))
|
|
return false;
|
|
BitRange nonZeroBits(state.bitsField);
|
|
nonZeroBits.intersectMask(state.node->getNZMask()); // Apply what we know about zero bits
|
|
if (nonZeroBits.numBits == 0)
|
|
return addZeroOut(state); // All bits in the field are zero
|
|
state.bitsUsed.intersectMask(state.node->getNZMask());
|
|
if (nonZeroBits.numBits == state.bitsUsed.numBits) { // Only used non-zero bits are in the field
|
|
addFieldWrite(state);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// \return the bitfield data-type
|
|
Datatype *BitFieldTransform::buildPartialType(void)
|
|
|
|
{
|
|
if (containerSize == parentStruct->getSize())
|
|
return parentStruct;
|
|
return func->getArch()->types->getTypePartialStruct(parentStruct, initialOffset, containerSize);
|
|
}
|
|
|
|
/// \brief Return \b true if specified bits in a Varnode are overwritten in the same basic block
|
|
///
|
|
/// This assumes other unspecified bits within the given Varnode are preserved at the point of overwrite.
|
|
/// \param vn is the given Varnode
|
|
/// \param bl is the given block
|
|
/// \param range specifies the bits within the Varnode
|
|
/// \return \b true if the bits are used
|
|
bool BitFieldTransform::findOverwrite(Varnode *vn,BlockBasic *bl,const BitRange &range)
|
|
|
|
{
|
|
Varnode *cvn;
|
|
BitRange minRange = range;
|
|
minRange.minimizeContainer();
|
|
Address addr = vn->getAddr() + (minRange.byteOffset - range.byteOffset);
|
|
list<PcodeOp *>::const_iterator iter;
|
|
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
|
Varnode *curVn = vn;
|
|
PcodeOp *op = *iter;
|
|
BitRange curRange = range;
|
|
do {
|
|
if (op->getParent() != bl) {
|
|
if (curRange.numBits != 0)
|
|
return false; // Bits are used outside the block
|
|
break;
|
|
}
|
|
switch(op->code()) {
|
|
case CPUI_PIECE:
|
|
if (op->getIn(0) == curVn) {
|
|
int4 sz = op->getIn(1)->getSize();
|
|
curRange.extendBytes(sz);
|
|
curRange.shift(sz * 8);
|
|
}
|
|
else {
|
|
curRange.extendBytes(op->getIn(0)->getSize());
|
|
}
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
cvn = op->getIn(1);
|
|
if (cvn->isConstant())
|
|
curRange.shift((int4)cvn->getOffset());
|
|
else
|
|
return false;
|
|
break;
|
|
case CPUI_INT_RIGHT:
|
|
cvn = op->getIn(1);
|
|
if (cvn->isConstant())
|
|
curRange.shift(-(int4)cvn->getOffset());
|
|
else
|
|
return false;
|
|
break;
|
|
case CPUI_COPY:
|
|
case CPUI_INT_OR:
|
|
case CPUI_INT_XOR:
|
|
case CPUI_INT_NEGATE:
|
|
break; // Remaining range continues to be used
|
|
case CPUI_INT_AND:
|
|
cvn = op->getIn(1);
|
|
if (cvn->isConstant())
|
|
curRange.intersectMask(cvn->getOffset());
|
|
break;
|
|
case CPUI_INSERT:
|
|
curRange.intersectMask(~InsertExpression::getRangeMask(op));
|
|
break;
|
|
case CPUI_INDIRECT:
|
|
curVn = op->getOut();
|
|
if (addr.containedBy(minRange.byteSize, curVn->getAddr(), curVn->getSize()))
|
|
return (curRange.numBits == 0);
|
|
return false;
|
|
break;
|
|
default:
|
|
if (curRange.numBits != 0)
|
|
return false; // Bits are actively used, not overwritten
|
|
op = (PcodeOp *)0; // No overlap yet, but don't follow this path further
|
|
break;
|
|
}
|
|
if (op == (PcodeOp *)0) break;
|
|
curVn = op->getOut();
|
|
if (addr.containedBy(minRange.byteSize, curVn->getAddr(), curVn->getSize())) {
|
|
if (curRange.numBits == 0)
|
|
return true;
|
|
}
|
|
if (curVn->hasNoDescend()) break;
|
|
op = curVn->loneDescend();
|
|
} while(op != (PcodeOp *)0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// If the given op is null, a new INSERT is created, otherwise, op is redefined to be an INSERT.
|
|
/// All the INSERT inputs are set based on the InsertRecord. The output is not set or modified.
|
|
/// \param op is a preexisting p-code op to reconfigure, or null
|
|
/// \param rec is the record describing the INSERT
|
|
/// \return the configured INSERT op
|
|
PcodeOp *BitFieldInsertTransform::setInsertInputs(PcodeOp *op,const InsertRecord &rec)
|
|
|
|
{
|
|
if (op == (PcodeOp *)0) {
|
|
op = func->newOp(4,finalWriteOp->getAddr());
|
|
}
|
|
else {
|
|
while(op->numInput() < 4)
|
|
func->opInsertInput(op, (Varnode *)0, op->numInput());
|
|
}
|
|
func->opSetOpcode(op, CPUI_INSERT);
|
|
func->opSetInput(op,originalValue,0);
|
|
Varnode *valVn = rec.vn;
|
|
if (valVn == (Varnode *)0) {
|
|
if (rec.dt != (Datatype *)0) {
|
|
valVn = func->newConstant(rec.dt->getSize(), rec.constVal);
|
|
valVn->updateType(rec.dt);
|
|
}
|
|
else {
|
|
valVn = func->newConstant(containerSize, rec.constVal);
|
|
}
|
|
}
|
|
func->opSetInput(op,valVn,1);
|
|
func->opSetInput(op,func->newConstant(4,rec.pos),2);
|
|
func->opSetInput(op,func->newConstant(4,rec.numBits),3);
|
|
func->opMarkSpecialPrint(op); // Not printed as normal operator with output
|
|
return op;
|
|
}
|
|
|
|
/// If necessary, a INT_RIGHT is performed on the (insertion value) input to INSERT.
|
|
/// \param insertOp is the INSERT
|
|
/// \param rec is the given InsertRecord
|
|
void BitFieldInsertTransform::addFieldShift(PcodeOp *insertOp,const InsertRecord &rec)
|
|
|
|
{
|
|
if (rec.shiftAmount == 0) return;
|
|
Varnode *valVn = insertOp->getIn(1);
|
|
PcodeOp *shiftOp = func->newOp(2, insertOp->getAddr());
|
|
func->opSetOpcode(shiftOp, CPUI_INT_RIGHT);
|
|
Varnode *newOut = func->newUniqueOut(valVn->getSize(),shiftOp);
|
|
func->opSetInput(insertOp, newOut, 1);
|
|
func->opSetInput(shiftOp, valVn, 0);
|
|
func->opSetInput(shiftOp, func->newConstant(4, rec.shiftAmount),1);
|
|
func->opInsertBefore(shiftOp, insertOp);
|
|
}
|
|
|
|
/// Check that the output of the LOAD has only INSERT, ZPULL, SPULL, or the finalWriteOp as a descendant.
|
|
/// If so mark the LOAD as non-printing.
|
|
/// \param loadOp is the LOAD
|
|
/// \return \b true if the LOAD was marked as non-printing
|
|
bool BitFieldInsertTransform::foldLoad(PcodeOp *loadOp) const
|
|
|
|
{
|
|
Varnode *outvn = loadOp->getOut();
|
|
list<PcodeOp *>::const_iterator iter;
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *op = *iter;
|
|
if (op == finalWriteOp) continue;
|
|
OpCode opc = op->code();
|
|
if (opc != CPUI_INSERT && opc != CPUI_ZPULL && opc != CPUI_SPULL)
|
|
return false;
|
|
}
|
|
func->opMarkNonPrinting(loadOp);
|
|
return true;
|
|
}
|
|
|
|
/// Check that the pointer into the given LOAD is defined by a PTRSUB and that all descendants of the pointer
|
|
/// are LOADs or STOREs that have been absorbed. If so mark the PTRSUB as non-printing.
|
|
/// \param loadOp is the LOAD
|
|
void BitFieldInsertTransform::foldPtrsub(PcodeOp *loadOp) const
|
|
|
|
{
|
|
Varnode *vn = loadOp->getIn(1);
|
|
if (!vn->isWritten()) return;
|
|
PcodeOp *ptrsub = vn->getDef();
|
|
if (ptrsub->code() != CPUI_PTRSUB) return;
|
|
list<PcodeOp *>::const_iterator iter;
|
|
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
|
PcodeOp *op = *iter;
|
|
if (op->code() == CPUI_STORE && op->doesSpecialPrinting()) continue;
|
|
if (op->code() == CPUI_LOAD && op->notPrinted()) continue;
|
|
return;
|
|
}
|
|
func->opMarkNonPrinting(ptrsub);
|
|
}
|
|
|
|
/// Look for (first) two INSERT descendants of \e value being inserted.
|
|
/// If these exist and are of the same form and in the same basic block, delete the second one.
|
|
/// \param rec is the record referencing the INSERTed \e value
|
|
void BitFieldInsertTransform::checkRedundancy(const InsertRecord &rec)
|
|
|
|
{
|
|
if (rec.vn == (Varnode *)0) return;
|
|
PcodeOp *immedOp = (PcodeOp *)0;
|
|
list<PcodeOp *>::const_iterator iter = rec.vn->beginDescend();
|
|
for(;iter != rec.vn->endDescend();++iter) {
|
|
PcodeOp *op = *iter;
|
|
if (op->code() != CPUI_INSERT) {
|
|
if (op->code() != CPUI_INT_RIGHT) continue;
|
|
op = op->getOut()->loneDescend();
|
|
if (op == (PcodeOp *)0 || op->code() != CPUI_INSERT) continue;
|
|
}
|
|
if (immedOp == (PcodeOp *)0) {
|
|
immedOp = op;
|
|
continue;
|
|
}
|
|
if (op->getIn(2)->getOffset() != immedOp->getIn(2)->getOffset()) continue;
|
|
if (op->getIn(3)->getOffset() != immedOp->getIn(3)->getOffset()) continue;
|
|
if (finalWriteOp->code() == CPUI_STORE) {
|
|
PcodeOp *store1 = op->getOut()->loneDescend();
|
|
if (store1 == (PcodeOp *)0) continue;
|
|
if (store1->code() != CPUI_STORE) continue;
|
|
PcodeOp *store2 = immedOp->getOut()->loneDescend();
|
|
if (store2 == (PcodeOp *)0) continue;
|
|
if (store2->code() != CPUI_STORE) continue;
|
|
if (store1->getParent() != store2->getParent()) continue;
|
|
if (!pointerEquality(store1->getIn(1), store2->getIn(1))) continue;
|
|
vector<PcodeOp *> scratch;
|
|
if (store1->getSeqNum().getOrder() < store2->getSeqNum().getOrder())
|
|
func->opDestroyRecursive(store2, scratch);
|
|
else
|
|
func->opDestroyRecursive(store1, scratch);
|
|
}
|
|
else {
|
|
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// \param f is the function
|
|
/// \param op is the p-code terminating the putative bitfield expression
|
|
/// \param dt is the structure containing bitfields
|
|
/// \param off is the amount of offset
|
|
BitFieldInsertTransform::BitFieldInsertTransform(Funcdata *f,PcodeOp *op,Datatype *dt,int4 off)
|
|
: BitFieldTransform(f,dt,off)
|
|
{
|
|
if (initialOffset == -1)
|
|
return;
|
|
finalWriteOp = op;
|
|
Varnode *outvn;
|
|
if (finalWriteOp->code() == CPUI_STORE) {
|
|
outvn = finalWriteOp->getIn(2);
|
|
}
|
|
else if (finalWriteOp->code() == CPUI_INDIRECT) {
|
|
mappedVn = finalWriteOp->getOut(); // Keep the storage location of the INDIRECT output
|
|
outvn = op->getIn(0);
|
|
if (!outvn->isWritten()) return;
|
|
finalWriteOp = outvn->getDef(); // But use the op feeding the INDIRECT as the finalWriteOp
|
|
}
|
|
else {
|
|
outvn = finalWriteOp->getOut();
|
|
mappedVn = outvn;
|
|
}
|
|
containerSize = outvn->getSize();
|
|
originalValue = (Varnode *)0;
|
|
establishFields(outvn,true);
|
|
}
|
|
|
|
/// Verify that any STORE between the original value LOAD and the final STORE
|
|
/// does not affect any of the known original value bits.
|
|
/// \param mask is the set of bits that must come from the putative \e original \e value
|
|
/// \return \b true if there is no interference
|
|
bool BitFieldInsertTransform::verifyLoadStoreOriginalValue(uintb mask) const
|
|
|
|
{
|
|
PcodeOp *loadOp = originalValue->getDef();
|
|
list<PcodeOp *>::const_iterator iter = finalWriteOp->getBasicIter();
|
|
list<PcodeOp *>::const_iterator biter = finalWriteOp->getParent()->beginOp();
|
|
uintb off;
|
|
Varnode *basePtr = rootPointer(finalWriteOp->getIn(1), off);
|
|
while(iter != biter) {
|
|
--iter;
|
|
PcodeOp *op = *iter;
|
|
if (op == loadOp) return true;
|
|
if (op->isCall()) return false;
|
|
if (op->code() != CPUI_STORE) continue;
|
|
if (op->getIn(0)->getOffset() != loadOp->getIn(0)->getOffset())
|
|
continue; // LOAD and STORE not to same address space
|
|
uintb otherOff;
|
|
if (basePtr != rootPointer(op->getIn(1),otherOff))
|
|
return false; // Unrelated pointer (potential alias)
|
|
if (otherOff != off)
|
|
continue;
|
|
Varnode *vn = op->getIn(2);
|
|
if (!vn->isWritten()) return false; // Unknown value
|
|
PcodeOp *insertOp = vn->getDef();
|
|
if (insertOp->code() != CPUI_INSERT) return false; // Unknown value
|
|
uintb insertMask = InsertExpression::getRangeMask(insertOp);
|
|
if ((insertMask & mask) != 0) return false; // Writing bits that are supposed to be original value
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Verify that any write to the mapped storage location between the original value LOAD and the STORE
|
|
/// does not affect any of the known original value bits
|
|
/// \param mask is the set of bits that must come from the putative \e original \e value
|
|
/// \return \b true if there is no interference
|
|
bool BitFieldInsertTransform::verifyMappedOriginalValue(uintb mask) const
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter = finalWriteOp->getBasicIter();
|
|
list<PcodeOp *>::const_iterator biter = finalWriteOp->getParent()->beginOp();
|
|
while(iter != biter) {
|
|
--iter;
|
|
PcodeOp *op = *iter;
|
|
Varnode *vn = op->getOut();
|
|
if (vn == originalValue) return true;
|
|
if (vn == (Varnode *)0) continue;
|
|
if (op->isCall()) return false; // Mapped location in unknown state
|
|
if (vn->getAddr() != originalValue->getAddr()) continue;
|
|
if (vn->getSize() != originalValue->getSize()) continue;
|
|
if (!vn->isWritten()) return false; // Unknown value
|
|
PcodeOp *insertOp = vn->getDef();
|
|
if (insertOp->code() != CPUI_INSERT) return false; // Unknown value
|
|
uintb insertMask = InsertExpression::getRangeMask(insertOp);
|
|
if ((insertMask & mask) != 0) return false; // Writing bits that are supposed to be original value
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Collect all bits which are \b not being INSERTed to by \b this transform.
|
|
/// These must be from the \e original \e value of the storage location.
|
|
/// \return a mask representing any bits coming from the original value
|
|
uintb BitFieldInsertTransform::constructOriginalValueMask(void) const
|
|
|
|
{
|
|
uintb mask = 0;
|
|
for(list<InsertRecord>::const_iterator iter=insertList.begin();iter!=insertList.end();++iter) {
|
|
const InsertRecord &rec(*iter);
|
|
uintb val = 0;
|
|
if (rec.numBits < 8*sizeof(uintb)) {
|
|
val = 1;
|
|
val <<= rec.numBits;
|
|
}
|
|
val -= 1;
|
|
val <<= rec.pos;
|
|
mask |= val;
|
|
}
|
|
mask = ~mask & calc_mask(originalValue->getSize());
|
|
return mask;
|
|
}
|
|
|
|
/// \return \b true if putative original value bits are unaffected
|
|
bool BitFieldInsertTransform::verifyOriginalValueBits(void) const
|
|
|
|
{
|
|
if (originalValue == (Varnode *)0) return true; // Not using original value bits
|
|
uintb mask = constructOriginalValueMask();
|
|
if (mask == 0) return true;
|
|
if (finalWriteOp->code() == CPUI_STORE)
|
|
return verifyLoadStoreOriginalValue(mask);
|
|
return verifyMappedOriginalValue(mask);
|
|
}
|
|
|
|
/// Follow all field in the \b workList back and try to match \e insert expressions.
|
|
/// \return \b true if all fields match
|
|
bool BitFieldInsertTransform::doTrace(void)
|
|
|
|
{
|
|
if (workList.empty())
|
|
return false; // Nothing to follow
|
|
while(!workList.empty()) {
|
|
BitFieldNodeState &node( workList.front() );
|
|
if (!processBackward(node) && !isOverwrittenPartial(node))
|
|
return false;
|
|
workList.pop_front();
|
|
}
|
|
if (insertList.empty()) return false;
|
|
return verifyOriginalValueBits();
|
|
}
|
|
|
|
void BitFieldInsertTransform::apply(void)
|
|
|
|
{
|
|
list<InsertRecord>::const_iterator iter;
|
|
Datatype *partialType = buildPartialType();
|
|
if (finalWriteOp->code() == CPUI_STORE) {
|
|
Varnode *deadPoint = finalWriteOp->getIn(2); // Root of expression that may be dead
|
|
PcodeOp *currentStore = finalWriteOp; // Original STORE is modified for first INSERT
|
|
PcodeOp *loadModel = (PcodeOp *)0;
|
|
Datatype *loadType = (Datatype *)0;
|
|
if (originalValue == (Varnode *)0) {
|
|
originalValue = func->newConstant(containerSize, 0);
|
|
}
|
|
else {
|
|
loadModel = originalValue->getDef();
|
|
loadType = originalValue->getTypeDefFacing();
|
|
}
|
|
for(iter=insertList.begin();iter!=insertList.end();++iter) {
|
|
const InsertRecord &rec(*iter);
|
|
if (currentStore == (PcodeOp *)0) {
|
|
currentStore = func->newOp(3, finalWriteOp->getAddr()); // Create new STORE for each additional INSERT
|
|
func->opSetOpcode(currentStore, CPUI_STORE);
|
|
func->opSetInput(currentStore, finalWriteOp->getIn(0), 0);
|
|
func->opSetInput(currentStore, finalWriteOp->getIn(1), 1);
|
|
func->opInsertAfter(currentStore, finalWriteOp);
|
|
if (loadModel != (PcodeOp *)0) {
|
|
PcodeOp *loadOp = func->newOp(2, loadModel->getAddr()); // Create new LOAD for each additional INSERT
|
|
func->opSetOpcode(loadOp, CPUI_LOAD);
|
|
func->opSetInput(loadOp, loadModel->getIn(0),0);
|
|
func->opSetInput(loadOp, loadModel->getIn(1),1);
|
|
originalValue = func->newUniqueOut(containerSize, loadOp);
|
|
originalValue->updateType(loadType);
|
|
func->opInsertBefore(loadOp,currentStore);
|
|
func->opMarkNonPrinting(loadOp); // Don't print LOAD, prevent CAST ops
|
|
}
|
|
}
|
|
PcodeOp *insertOp = setInsertInputs((PcodeOp *)0,rec);
|
|
Varnode *newOut = func->newUniqueOut(containerSize, insertOp);
|
|
newOut->updateType(partialType);
|
|
func->opSetInput(currentStore,insertOp->getOut(),2);
|
|
func->opInsertBefore(insertOp,currentStore);
|
|
func->opMarkSpecialPrint(currentStore); // Mark special bitfield printing on STORE
|
|
addFieldShift(insertOp, rec);
|
|
currentStore = (PcodeOp *)0;
|
|
}
|
|
func->destroyVarnodeRecursive(deadPoint);
|
|
if (loadModel != (PcodeOp *)0 && loadModel->code() == CPUI_LOAD) {
|
|
if (foldLoad(loadModel)) {
|
|
foldPtrsub(loadModel);
|
|
}
|
|
}
|
|
}
|
|
else { // Mapped variable
|
|
vector<Varnode *> deadPoints;
|
|
for(int4 i=0;i<finalWriteOp->numInput();++i)
|
|
deadPoints.push_back(finalWriteOp->getIn(i)); // Roots of expressions that may be dead
|
|
if (originalValue == (Varnode *)0) {
|
|
originalValue = func->newConstant(containerSize, 0);
|
|
}
|
|
iter = insertList.begin();
|
|
PcodeOp *insertOp = setInsertInputs(finalWriteOp, *iter); // Redefine finalWriteOp as INSERT, preserving original output
|
|
insertOp->getOut()->updateType(partialType);
|
|
addFieldShift(insertOp, *iter);
|
|
++iter;
|
|
|
|
for(;iter!=insertList.end();++iter) {
|
|
PcodeOp *lastOp = insertOp;
|
|
func->opUnsetInput(lastOp,0); // Unset originalValue as input, so it can go to new INSERT
|
|
insertOp = setInsertInputs((PcodeOp *)0, *iter); // New INSERT
|
|
Varnode *newOut = func->newVarnodeOut(containerSize, mappedVn->getAddr(), insertOp);
|
|
newOut->updateType(partialType);
|
|
func->opSetInput(lastOp,newOut,0);
|
|
func->opInsertBefore(insertOp, lastOp);
|
|
addFieldShift(insertOp, *iter);
|
|
}
|
|
for(int4 i=0;i<deadPoints.size();++i)
|
|
func->destroyVarnodeRecursive(deadPoints[i]);
|
|
}
|
|
|
|
for(iter=insertList.begin();iter!=insertList.end();++iter)
|
|
checkRedundancy(*iter);
|
|
}
|
|
|
|
/// If a PcodeOp is given, the specific read of the state.readVn will be replaced with a new \e unique Varnode
|
|
/// holding the effective extraction. Otherwise the state.readVn will be redefined as an extraction for all reads.
|
|
/// \param state holds the extracted Varnode and the position of the bitfield at the point of extraction
|
|
/// \param op is the specific PcodeOp reading the extracted field or null
|
|
BitFieldPullTransform::PullRecord::PullRecord(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
type = normal;
|
|
readVn = state.node;
|
|
readOp = op;
|
|
dt = state.field->type;
|
|
pos = state.origLeastSigBit;
|
|
numBits = state.field->bits.numBits;
|
|
leftShift = state.bitsField.leastSigBit;
|
|
mask = 0;
|
|
}
|
|
|
|
/// \param state holds the extracted Varnode and the position of the bitfield at the point of extraction
|
|
/// \param op is the specific INT_EQUAL or INT_NOTEQUAL PcodeOp reading the extracted field
|
|
/// \param val is a mask representing the bitfield within the Varnode
|
|
BitFieldPullTransform::PullRecord::PullRecord(const BitFieldNodeState &state,PcodeOp *op,uintb val)
|
|
|
|
{
|
|
type = equal;
|
|
readVn = state.node;
|
|
readOp = op;
|
|
dt = state.field->type;
|
|
pos = state.origLeastSigBit;
|
|
numBits = state.field->bits.numBits;
|
|
leftShift = state.bitsField.leastSigBit;
|
|
mask = val;
|
|
}
|
|
|
|
/// \param op is the PcodeOp whose input pull is being aborted
|
|
BitFieldPullTransform::PullRecord::PullRecord(PcodeOp *op)
|
|
|
|
{
|
|
type = aborted;
|
|
readVn = (Varnode *)0;
|
|
readOp = op;
|
|
dt = (Datatype *)0;
|
|
pos = 0;
|
|
numBits = 0;
|
|
leftShift = 0;
|
|
mask = 0;
|
|
}
|
|
|
|
/// Sort based on the PcodeOp whose input is being pulled
|
|
/// \param op2 is the record to compare with \b this
|
|
/// \return \b true if \b this comes before \b op2
|
|
bool BitFieldPullTransform::PullRecord::operator<(const PullRecord &op2) const
|
|
|
|
{
|
|
if (readOp != (PcodeOp *)0 && op2.readOp != (PcodeOp *)0) {
|
|
if (readOp != op2.readOp)
|
|
return (readOp->getSeqNum() < op2.readOp->getSeqNum());
|
|
}
|
|
else if (readOp == (PcodeOp *)0)
|
|
return true;
|
|
else if (op2.readOp == (PcodeOp *)0)
|
|
return false;
|
|
return false;
|
|
}
|
|
|
|
/// \param vn is the Varnode being read
|
|
/// \param bitField is the bitfield being followed
|
|
/// \return \b true if all consumed bits are in the bitfield
|
|
bool BitFieldPullTransform::testConsumed(Varnode *vn,const BitRange &bitField)
|
|
|
|
{
|
|
if (bitField.byteSize > sizeof(uintb)) return false;
|
|
uintb mask = bitField.getMask();
|
|
uintb intersect = mask & vn->getConsume();
|
|
return (intersect == vn->getConsume());
|
|
}
|
|
|
|
/// If the \e bitfield is moved into the output Varnode without losing bits,
|
|
/// add the output as a new \e bitfield state and update usage information for original \b root bits.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INT_LEFT reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleLeftForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (op->getIn(0) != state.node) return;
|
|
Varnode *cvn = op->getIn(1);
|
|
if (!cvn->isConstant()) return;
|
|
int4 sa = (int4)cvn->getOffset();
|
|
BitRange newRange(state.bitsField);
|
|
newRange.shift(sa);
|
|
if (newRange.numBits == 0)
|
|
return;
|
|
if (state.bitsField.numBits == newRange.numBits) {
|
|
bool newSignExt = state.isSignExtended || newRange.isMostSignificant();
|
|
workList.emplace_back(state,newRange,op->getOut(),newSignExt);
|
|
workList.back().bitsUsed.shift(sa);
|
|
}
|
|
else if (testConsumed(op->getOut(),newRange)) {
|
|
pullList.emplace_back(state,op);
|
|
}
|
|
}
|
|
|
|
/// If the \e bitfield is moved into the output Varnode without losing bits,
|
|
/// add the output as a new \e bitfield state and update usage information for original \b root bits.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INT_RIGHT or INT_SRIGHT reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleRightForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (op->getIn(0) != state.node) return;
|
|
Varnode *cvn = op->getIn(1);
|
|
if (!cvn->isConstant()) return;
|
|
int4 sa = (int4)cvn->getOffset();
|
|
BitRange newRange(state.bitsField);
|
|
newRange.shift(-sa);
|
|
if (newRange.numBits == 0)
|
|
return;
|
|
if (state.bitsField.numBits == newRange.numBits) {
|
|
bool newSignExt = (op->code() == CPUI_INT_SRIGHT) ? state.isSignExtended : false;
|
|
workList.emplace_back(state,newRange,op->getOut(),newSignExt);
|
|
workList.back().bitsUsed.shift(-sa);
|
|
if (op->code() == CPUI_INT_SRIGHT && !state.isSignExtended) {
|
|
workList.back().bitsUsed.expandToMost(); // Sign extending bits not in the field
|
|
}
|
|
}
|
|
else if (testConsumed(op->getOut(),newRange)) {
|
|
pullList.emplace_back(state,op);
|
|
}
|
|
}
|
|
|
|
/// If the \e bitfield is masked into the output Varnode without losing bits,
|
|
/// add the output as a new \e bitfield state and update usage information for original \b root bits.
|
|
/// If every bit outside the \e bitfield is zeroed plus additional bits in the \e bitfield,
|
|
/// create a PullRecord for this particular read.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INT_AND reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleAndForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (op->getIn(0) != state.node) return;
|
|
if (state.bitsField.byteSize > sizeof(uintb)) return;
|
|
Varnode *cvn = op->getIn(1);
|
|
if (!cvn->isConstant()) return;
|
|
uintb andVal = cvn->getOffset();
|
|
uintb mask = state.bitsField.getMask();
|
|
uintb intersect = andVal & mask;
|
|
if (intersect == 0) return; // Field is completely masked away
|
|
if (intersect == mask) { // Nothing is masked away, follow the whole field
|
|
bool newSignExt = state.bitsField.isMostSignificant();
|
|
workList.emplace_back(state,state.bitsField,op->getOut(),newSignExt);
|
|
workList.back().bitsUsed.intersectMask(andVal);
|
|
}
|
|
else if (testConsumed(op->getOut(),state.bitsField)) {
|
|
pullList.emplace_back(state,op);
|
|
}
|
|
}
|
|
|
|
/// Add the output Varnode as a new \e bitfield state and update usage information for original \b root bits.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INT_ZEXT or INT_SEXT reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleExtForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *outvn = op->getOut();
|
|
int4 diff = outvn->getSize() - state.node->getSize();
|
|
bool newSignExt = (op->code() == CPUI_INT_SEXT) ? state.isSignExtended : false;
|
|
workList.emplace_back(state,state.bitsField,outvn,newSignExt);
|
|
workList.back().bitsField.extendBytes(diff);
|
|
workList.back().bitsUsed.extendBytes(diff);
|
|
if (op->code() == CPUI_INT_SEXT && !state.isSignExtended) {
|
|
workList.back().bitsUsed.expandToMost(); // Sign extending bits not in the field
|
|
}
|
|
}
|
|
|
|
/// If the INT_MULT can be viewed as a left shift, and If the \e bitfield is moved into the output
|
|
/// Varnode without losing bits, add the output as a new \e bitfield state and update usage information
|
|
/// for original \b root bits.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INT_MULT reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleMultForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (op->getIn(0) != state.node) return;
|
|
Varnode *vn1 = op->getIn(1);
|
|
if (!vn1->isConstant()) return;
|
|
uintb val = vn1->getOffset();
|
|
if (popcount(val) != 1) {
|
|
handleLeastSigOp(state, op);
|
|
return;
|
|
}
|
|
int4 sa = leastsigbit_set(val);
|
|
BitRange newRange(state.bitsField);
|
|
newRange.shift(sa);
|
|
if (newRange.numBits == 0)
|
|
return;
|
|
if (state.bitsField.numBits == newRange.numBits) {
|
|
bool newSignExt = state.isSignExtended || newRange.isMostSignificant();
|
|
workList.emplace_back(state,newRange,op->getOut(),newSignExt);
|
|
workList.back().bitsUsed.shift(sa);
|
|
}
|
|
}
|
|
|
|
/// If the \e bitfield is truncated without losing bits, add the output as a new
|
|
/// \e bitfield state and update usage information for original \b root bits.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the SUBPIECE reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleSubpieceForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (op->getIn(0) != state.node) return;
|
|
int4 leastTrunc = (int4)op->getIn(1)->getOffset();
|
|
int4 mostTrunc = (state.bitsField.byteSize - leastTrunc) - op->getOut()->getSize();
|
|
BitRange newRange(state.bitsField);
|
|
|
|
newRange.truncateLeastSigBytes(leastTrunc);
|
|
newRange.truncateMostSigBytes(mostTrunc);
|
|
if (newRange.numBits == 0)
|
|
return;
|
|
if (state.bitsField.numBits == newRange.numBits) {
|
|
bool newSignExt = state.isSignExtended; // Any sign extension is preserved, since we only truncate and whole field is present
|
|
workList.emplace_back(state,newRange,op->getOut(),newSignExt);
|
|
workList.back().bitsUsed.truncateLeastSigBytes(leastTrunc);
|
|
workList.back().bitsUsed.truncateMostSigBytes(mostTrunc);
|
|
}
|
|
else if (testConsumed(op->getOut(),newRange)) {
|
|
pullList.emplace_back(state,op);
|
|
}
|
|
}
|
|
|
|
/// Test if we can treat the value being INSERTed as a PULL of the current bitfield.
|
|
/// The INSERT must only be inserting bits from the bitfield, in which case we create a PullRecord directly.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INSERT reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleInsertForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (op->getIn(1) != state.node) return; // Handle only if pull is value being inserted
|
|
if (state.bitsField.leastSigBit != 0) return; // Test if least sig bits of inserted value are in the bitfield
|
|
int4 sz = (int4)op->getIn(3)->getOffset();
|
|
if (sz > state.bitsField.numBits) return; // Test if more bits are getting INSERTed than are in bitfield
|
|
|
|
pullList.emplace_back(state,op); // Can treat input to INSERT as pull of current bitfield
|
|
}
|
|
|
|
/// If the \b bitfield is the most significant bits being compared, and the
|
|
/// constant being compared to has 1 bits in the least significant positions,
|
|
/// create a PullRecord indicating the comparison acts on the pulled bits.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the comparison reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleLessForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (!state.bitsField.isMostSignificant())
|
|
return;
|
|
int4 slot = op->getSlot(state.node);
|
|
Varnode *cvn = op->getIn(1-slot);
|
|
if (!cvn->isConstant()) return;
|
|
uintb val = cvn->getOffset();
|
|
bool leastSigZeroBits = (val & 1) == 0;
|
|
int4 numExtremalBits;
|
|
if (leastSigZeroBits)
|
|
numExtremalBits = leastsigbit_set(val); // Check how many least significant 0 bits
|
|
else
|
|
numExtremalBits = leastsigbit_set(~val); // Check how many least significant 1 bits
|
|
if (numExtremalBits < 0)
|
|
numExtremalBits = sizeof(uintb) * 8;
|
|
bool needMaskCheck = false;
|
|
OpCode opc = op->code();
|
|
if (opc == CPUI_INT_SLESS || opc == CPUI_INT_LESS) {
|
|
if (leastSigZeroBits && slot != 0) return;
|
|
if (!leastSigZeroBits && slot == 0)
|
|
needMaskCheck = true;
|
|
}
|
|
else if (opc == CPUI_INT_SLESSEQUAL || opc == CPUI_INT_LESSEQUAL) {
|
|
if (leastSigZeroBits && slot != 1) return;
|
|
if (!leastSigZeroBits && slot == 1)
|
|
needMaskCheck = true;
|
|
}
|
|
if (needMaskCheck) {
|
|
uintb mask;
|
|
if (numExtremalBits >= 8*sizeof(uintb))
|
|
mask = 0;
|
|
else {
|
|
mask = 1;
|
|
mask <<= numExtremalBits;
|
|
}
|
|
mask -= 1;
|
|
if ((mask & state.node->getNZMask()) == mask) return; // Must be at least one 0 bit
|
|
}
|
|
if (state.bitsField.leastSigBit <= numExtremalBits) { // If the field extends into the extremal bits
|
|
// The comparison is only affected by field bits. View field as pulled and then shifted.
|
|
pullList.emplace_back(state,op);
|
|
}
|
|
}
|
|
|
|
/// This handles arithmetic/logical ops where the result on least significant bits doesn't change
|
|
/// if the more significant bits are truncated from the inputs.
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the arithmetic/logical op
|
|
void BitFieldPullTransform::handleLeastSigOp(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
if (state.bitsField.leastSigBit != 0) return; // Field must be in least significant bits
|
|
if (testConsumed(op->getOut(),state.bitsField)) {
|
|
pullList.emplace_back(state,op);
|
|
}
|
|
}
|
|
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
/// \param op is the INT_EQUAL or INT_NOTEQUAL comparison reading the \e bitfield Varnode
|
|
void BitFieldPullTransform::handleEqualForward(const BitFieldNodeState &state,PcodeOp *op)
|
|
|
|
{
|
|
Varnode *cvn = op->getIn(1);
|
|
if (state.bitsField.byteSize > sizeof(uintb)) return;
|
|
if (!cvn->isConstant()) return;
|
|
if (state.field != (const TypeBitField *)0 && state.field->bits.numBits == state.bitsField.numBits) {
|
|
uintb val = state.bitsField.getMask();
|
|
pullList.emplace_back(state,op,val);
|
|
}
|
|
else {
|
|
pullList.emplace_back(op); // Abort any pulls into this op
|
|
}
|
|
}
|
|
|
|
/// \param state is the current state of the \e bitfield and the Varnode holding it
|
|
void BitFieldPullTransform::processForward(BitFieldNodeState &state)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter;
|
|
if (state.isFieldAligned() && state.doesSignExtensionMatch()) {
|
|
pullList.emplace_back(state,(PcodeOp *)0);
|
|
return;
|
|
}
|
|
for(iter=state.node->beginDescend();iter!=state.node->endDescend();++iter) {
|
|
PcodeOp *op = *iter;
|
|
switch(op->code()) {
|
|
case CPUI_INT_LEFT:
|
|
handleLeftForward(state, op);
|
|
break;
|
|
case CPUI_INT_MULT:
|
|
handleMultForward(state, op);
|
|
break;
|
|
case CPUI_INT_RIGHT:
|
|
case CPUI_INT_SRIGHT:
|
|
handleRightForward(state, op);
|
|
break;
|
|
case CPUI_INT_AND:
|
|
handleAndForward(state, op);
|
|
break;
|
|
case CPUI_INT_ZEXT:
|
|
case CPUI_INT_SEXT:
|
|
handleExtForward(state, op);
|
|
break;
|
|
case CPUI_INT_LESS:
|
|
case CPUI_INT_LESSEQUAL:
|
|
case CPUI_INT_SLESS:
|
|
case CPUI_INT_SLESSEQUAL:
|
|
handleLessForward(state, op);
|
|
break;
|
|
case CPUI_INT_EQUAL:
|
|
case CPUI_INT_NOTEQUAL:
|
|
handleEqualForward(state, op);
|
|
break;
|
|
case CPUI_INT_ADD:
|
|
case CPUI_INT_OR:
|
|
case CPUI_INT_XOR:
|
|
case CPUI_INT_2COMP:
|
|
case CPUI_INT_NEGATE:
|
|
handleLeastSigOp(state, op);
|
|
break;
|
|
case CPUI_SUBPIECE:
|
|
handleSubpieceForward(state, op);
|
|
break;
|
|
case CPUI_INSERT:
|
|
handleInsertForward(state, op);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \brief Determine if pulls at a specific INT_EQUAL or INT_NOTEQUAL are consistent as a whole
|
|
///
|
|
/// Run through PullRecords for a single INT_EQUAL or INT_NOTEQUAL. These records are deleted if:
|
|
/// - An \e aborted record is present, indicating a partial field or hole is being compared
|
|
/// - Other unrelated bits are being compared
|
|
///
|
|
/// \param iter points to the first PullRecord for the op
|
|
/// \return an iterator pointing after all PullRecords for the op
|
|
list<BitFieldPullTransform::PullRecord>::iterator BitFieldPullTransform::testCompareGroup(list<PullRecord>::iterator iter)
|
|
|
|
{
|
|
list<PullRecord>::iterator curiter = iter;
|
|
bool isAborted = false;
|
|
uintb collectMask = 0; // Collect mask of all fields being tested
|
|
Varnode *vn = (*iter).readVn;
|
|
PcodeOp *op = (*iter).readOp;
|
|
uintb val = op->getIn(1)->getOffset();
|
|
do {
|
|
PullRecord &rec( *curiter );
|
|
if (rec.readOp != op) break;
|
|
++curiter;
|
|
if (rec.type == PullRecord::aborted)
|
|
isAborted = true;
|
|
collectMask |= rec.mask;
|
|
} while(curiter != pullList.end());
|
|
if (isAborted || (~collectMask & val) != 0 || (~collectMask & vn->getNZMask()) != 0) {
|
|
curiter = pullList.erase(iter,curiter);
|
|
}
|
|
return curiter;
|
|
}
|
|
|
|
/// Create the ZPULL or SPULL op. Duplicate the LOAD if necessary. Add an INT_LEFT if needed.
|
|
/// \param rec is the given PullRecord
|
|
/// \param state is state maintained across all transforms
|
|
void BitFieldPullTransform::applyRecord(PullRecord &rec,TransformState &state)
|
|
|
|
{
|
|
PcodeOp *modOp;
|
|
if (rec.readOp == (PcodeOp *)0) { // null here indicates readVn contains a complete pull
|
|
modOp = rec.readVn->getDef(); // readVn always has a defining op
|
|
func->opUnsetOutput(modOp); // set up to modify the definition of readVn
|
|
}
|
|
else { // Otherwise modify the single read of readVn, readOp
|
|
if (rec.readVn != root)
|
|
modOp = rec.readVn->getDef();
|
|
else
|
|
modOp = rec.readOp;
|
|
int4 slot = rec.readOp->getSlot(rec.readVn);
|
|
rec.readVn = func->newUnique(rec.readVn->getSize()); // New Varnode to hold the complete pull
|
|
func->opSetInput(rec.readOp,rec.readVn,slot);
|
|
}
|
|
Varnode *inVn = root;
|
|
if (loadOp != (PcodeOp *)0 && state.count > 0) {
|
|
PcodeOp *newLoad = func->newOp(2, loadOp->getAddr());
|
|
func->opSetOpcode(newLoad, CPUI_LOAD); // Make copy of original LOAD
|
|
func->opSetInput(newLoad,loadOp->getIn(0),0);
|
|
func->opSetInput(newLoad,loadOp->getIn(1),1);
|
|
inVn = func->newUniqueOut(containerSize, newLoad);
|
|
func->opInsertAfter(newLoad,loadOp);
|
|
func->opMarkNonPrinting(newLoad);
|
|
}
|
|
inVn->updateType(state.partialType);
|
|
PcodeOp *pullOp = func->newOp(3, modOp->getAddr());
|
|
func->opSetOpcode(pullOp, (rec.dt->getMetatype() == TYPE_INT) ? CPUI_SPULL : CPUI_ZPULL);
|
|
func->opSetInput(pullOp,inVn,0);
|
|
func->opSetInput(pullOp,func->newConstant(4,rec.pos),1);
|
|
func->opSetInput(pullOp,func->newConstant(4,rec.numBits),2);
|
|
if (modOp != rec.readOp)
|
|
func->opInsertAfter(pullOp, modOp);
|
|
else
|
|
func->opInsertBefore(pullOp, modOp);
|
|
if (rec.leftShift != 0) {
|
|
Varnode *shiftVn = func->newUniqueOut(containerSize, pullOp);
|
|
PcodeOp *shiftOp = func->newOp(2, modOp->getAddr());
|
|
func->opSetOpcode(shiftOp, CPUI_INT_LEFT);
|
|
func->opSetInput(shiftOp,shiftVn,0);
|
|
func->opSetInput(shiftOp,func->newConstant(4,rec.leftShift),1);
|
|
func->opInsertAfter(shiftOp,pullOp);
|
|
func->opSetOutput(shiftOp, rec.readVn);
|
|
}
|
|
else {
|
|
func->opSetOutput(pullOp, rec.readVn);
|
|
}
|
|
|
|
Varnode *pullOut = pullOp->getOut();
|
|
if (pullOut->getType()->getMetatype() == TYPE_UNKNOWN) {
|
|
Datatype *dt = func->getArch()->types->resizeInteger(rec.dt,pullOut->getSize());
|
|
pullOut->updateType(dt);
|
|
}
|
|
else if (rec.dt->getMetatype() == TYPE_BOOL && pullOut->getSize() == 1 && rec.numBits == 1) {
|
|
pullOut->updateType(rec.dt);
|
|
}
|
|
if (modOp != rec.readOp) {
|
|
Varnode *outvn = modOp->getOut();
|
|
if (outvn == (Varnode *)0 || outvn->hasNoDescend())
|
|
func->opDestroyRecursive(modOp, state.deadScratch);
|
|
}
|
|
state.count += 1;
|
|
}
|
|
|
|
/// The first PullRecord at least must be for a comparison op. If there are more than one,
|
|
/// the op is converted into a boolean expression with comparison for each record.
|
|
/// Then the constant value for each comparison is adjusted to match the PullRecord bitfield.
|
|
/// The PullRecords are \e not removed, but are converted to \e normal records so that
|
|
/// the applyRecord() method can create the ZPULL or SPULL ops.
|
|
/// \param rec must be the first PullRecord in \b pullList
|
|
void BitFieldPullTransform::applyCompareRecord(const PullRecord &rec)
|
|
|
|
{
|
|
uintb origVal = rec.readOp->getIn(1)->getOffset();
|
|
int4 num = 0;
|
|
list<PullRecord>::iterator iter,enditer;
|
|
enditer = pullList.begin();
|
|
while(enditer != pullList.end()) { // Gather pulls to the same compare
|
|
if ((*enditer).readOp != rec.readOp)
|
|
break;
|
|
++enditer;
|
|
num += 1;
|
|
}
|
|
if (num > 1) {
|
|
OpCode opc = rec.readOp->code();
|
|
OpCode combineCode = (opc == CPUI_INT_EQUAL) ? CPUI_BOOL_AND : CPUI_BOOL_OR;
|
|
Varnode *vn = rec.readOp->getIn(0);
|
|
PcodeOp *curCombine = rec.readOp;
|
|
func->opSetOpcode(curCombine,combineCode);
|
|
iter = pullList.begin();
|
|
for(int4 i=0;i<num;++i) {
|
|
PcodeOp *op = func->newOp(2,curCombine->getAddr());
|
|
func->opSetOpcode(op, opc);
|
|
Varnode *boolVn = func->newUniqueOut(1, op);
|
|
func->opSetInput(op,vn,0);
|
|
func->opInsertBefore(op,curCombine);
|
|
if (i == 0) {
|
|
func->opSetInput(curCombine, boolVn, 0);
|
|
}
|
|
else if (i < num-1) {
|
|
PcodeOp *combineOp = func->newOp(2,curCombine->getAddr());
|
|
func->opSetOpcode(combineOp,combineCode);
|
|
Varnode *bool2Vn = func->newUniqueOut(1,combineOp);
|
|
func->opSetInput(curCombine,bool2Vn,1);
|
|
func->opSetInput(combineOp,boolVn,0);
|
|
func->opInsertBefore(combineOp,curCombine);
|
|
curCombine = combineOp;
|
|
}
|
|
else {
|
|
func->opSetInput(curCombine,boolVn,1);
|
|
}
|
|
(*iter).readOp = op;
|
|
++iter;
|
|
}
|
|
}
|
|
iter = pullList.begin();
|
|
while(iter != enditer) {
|
|
PullRecord &subrec(*iter);
|
|
uintb val = origVal & subrec.mask;
|
|
val >>= subrec.leftShift;
|
|
if (subrec.dt->getMetatype() == TYPE_INT)
|
|
val = extend_signbit(val, subrec.numBits, subrec.readVn->getSize());
|
|
Varnode *vn = func->newConstant(subrec.readVn->getSize(),val);
|
|
Datatype *dt = func->getArch()->types->resizeInteger(subrec.dt, subrec.readVn->getSize());
|
|
vn->updateType(dt);
|
|
func->opSetInput(subrec.readOp,vn,1); // Adjust compare value
|
|
subrec.type = PullRecord::normal; // Convert to normal pull
|
|
subrec.leftShift = 0; // left shift has been accounted for
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
/// Check that the output of the LOAD has only ZPULL, SPULL, or INSERT as a descendant.
|
|
/// If so mark the LOAD as non-printing.
|
|
/// \param loadOp is the LOAD
|
|
/// \return \b true if the LOAD was marked as non-printing
|
|
bool BitFieldPullTransform::foldLoad(PcodeOp *loadOp) const
|
|
|
|
{
|
|
Varnode *outvn = loadOp->getOut();
|
|
list<PcodeOp *>::const_iterator iter;
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
OpCode opc = (*iter)->code();
|
|
if (opc != CPUI_ZPULL && opc != CPUI_SPULL && opc != CPUI_INSERT)
|
|
return false;
|
|
}
|
|
func->opMarkNonPrinting(loadOp);
|
|
return true;
|
|
}
|
|
|
|
/// Check that the pointer into the given LOAD is defined by a PTRSUB and that all descendants of the pointer
|
|
/// are LOADs that have been absorbed. If so mark the PTRSUB as non-printing.
|
|
/// \param loadOp is the LOAD
|
|
void BitFieldPullTransform::foldPtrsub(PcodeOp *loadOp) const
|
|
|
|
{
|
|
Varnode *vn = loadOp->getIn(1);
|
|
if (!vn->isWritten()) return;
|
|
PcodeOp *ptrsub = vn->getDef();
|
|
if (ptrsub->code() != CPUI_PTRSUB) return;
|
|
list<PcodeOp *>::const_iterator iter;
|
|
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
|
PcodeOp *op = *iter;
|
|
if (op->code() != CPUI_LOAD) return;
|
|
if (!op->notPrinted()) return; // Check if LOAD has been absorbed
|
|
}
|
|
func->opMarkNonPrinting(ptrsub);
|
|
}
|
|
|
|
/// \param f is the function
|
|
/// \param r is the root Varnode with a \e bitfield data-type
|
|
/// \param dt is the data-type containing bitfields (may be partial)
|
|
/// \param off is the byte offset into the data-type to associate with \b root
|
|
BitFieldPullTransform::BitFieldPullTransform(Funcdata *f,Varnode *r,Datatype *dt,int4 off)
|
|
: BitFieldTransform(f,dt,off)
|
|
{
|
|
if (initialOffset == -1)
|
|
return;
|
|
root = r;
|
|
containerSize = root->getSize();
|
|
if (root->isWritten() && root->getDef()->code() == CPUI_LOAD)
|
|
loadOp = root->getDef();
|
|
else
|
|
loadOp = (PcodeOp *)0;
|
|
establishFields(root,false); // Don't follow holes
|
|
}
|
|
|
|
/// Create a PullRecord at each pull point.
|
|
/// \return \b true if any PullRecords were created
|
|
bool BitFieldPullTransform::doTrace(void)
|
|
|
|
{
|
|
while(!workList.empty()) {
|
|
processForward(workList.front());
|
|
workList.pop_front();
|
|
}
|
|
if (pullList.empty())
|
|
return false;
|
|
pullList.sort();
|
|
list<PullRecord>::iterator iter = pullList.begin();
|
|
while(iter != pullList.end()) {
|
|
if ((*iter).type != PullRecord::normal)
|
|
iter = testCompareGroup(iter);
|
|
else
|
|
++iter;
|
|
}
|
|
return !pullList.empty();
|
|
}
|
|
|
|
/// For each pull record, either:
|
|
/// - Redefine \b readVn with a ZPULL or SPULL. Delete the original op defining \b readVn
|
|
/// - Create a Varnode for a specific \b readOp that effectively holds the pulled value
|
|
void BitFieldPullTransform::apply(void)
|
|
|
|
{
|
|
TransformState state;
|
|
state.count = 0;
|
|
state.partialType = buildPartialType();
|
|
while(!pullList.empty()) {
|
|
PullRecord &rec(pullList.front());
|
|
if (rec.type == PullRecord::equal) {
|
|
applyCompareRecord(rec);
|
|
}
|
|
else {
|
|
applyRecord(rec,state);
|
|
pullList.pop_front();
|
|
}
|
|
}
|
|
if (loadOp != (PcodeOp *)0) {
|
|
if (foldLoad(loadOp))
|
|
foldPtrsub(loadOp);
|
|
}
|
|
}
|
|
|
|
void RuleBitFieldStore::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
oplist.push_back(CPUI_STORE);
|
|
}
|
|
|
|
int4 RuleBitFieldStore::applyOp(PcodeOp *op,Funcdata &data)
|
|
|
|
{
|
|
Datatype *ptr = op->getIn(1)->getTypeReadFacing(op);
|
|
int4 off;
|
|
Datatype *dt = ptr->getPtrInto(off);
|
|
if (dt == (Datatype *)0) return 0;
|
|
if (!dt->hasBitfields()) return 0;
|
|
Varnode *vn = op->getIn(2);
|
|
if (vn->isWritten() && vn->getDef()->code() == CPUI_INSERT) return 0;
|
|
BitFieldInsertTransform transform(&data,op,dt,off);
|
|
if (!transform.doTrace())
|
|
return 0;
|
|
transform.apply();
|
|
return 1;
|
|
}
|
|
|
|
void RuleBitFieldOut::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
uint4 list[]={ CPUI_COPY, CPUI_INT_EQUAL, CPUI_INT_NOTEQUAL, CPUI_INT_SLESS, CPUI_INT_SLESSEQUAL,
|
|
CPUI_INT_LESS, CPUI_INT_LESSEQUAL, CPUI_INT_ZEXT, CPUI_INT_SEXT, CPUI_INT_ADD, CPUI_INT_CARRY,
|
|
CPUI_INT_SCARRY, CPUI_INT_XOR, CPUI_INT_AND, CPUI_INT_OR, CPUI_INT_LEFT, CPUI_INT_RIGHT,
|
|
CPUI_INT_SRIGHT, CPUI_INT_MULT, CPUI_BOOL_NEGATE, CPUI_BOOL_XOR, CPUI_BOOL_AND, CPUI_BOOL_OR,
|
|
CPUI_FLOAT_EQUAL, CPUI_FLOAT_NOTEQUAL, CPUI_FLOAT_LESS, CPUI_FLOAT_LESSEQUAL, CPUI_FLOAT_NAN,
|
|
CPUI_INDIRECT, CPUI_SUBPIECE };
|
|
oplist.insert(oplist.end(),list,list+30);
|
|
}
|
|
|
|
int4 RuleBitFieldOut::applyOp(PcodeOp *op,Funcdata &data)
|
|
|
|
{
|
|
Varnode *outvn = op->getOut();
|
|
Datatype *dt = outvn->getTypeDefFacing();
|
|
if (!dt->hasBitfields()) return 0;
|
|
BitFieldInsertTransform transform(&data,op,dt,0);
|
|
if (!transform.doTrace())
|
|
return 0;
|
|
transform.apply();
|
|
return 1;
|
|
}
|
|
|
|
void RuleBitFieldLoad::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
oplist.push_back(CPUI_LOAD);
|
|
}
|
|
|
|
int4 RuleBitFieldLoad::applyOp(PcodeOp *op,Funcdata &data)
|
|
|
|
{
|
|
Datatype *ptr = op->getIn(1)->getTypeReadFacing(op);
|
|
int4 off;
|
|
Datatype *dt = ptr->getPtrInto(off);
|
|
if (dt == (Datatype *)0) return 0;
|
|
if (!dt->hasBitfields()) return 0;
|
|
if (op->notPrinted()) return 0; // LOAD visited before
|
|
BitFieldPullTransform transform(&data,op->getOut(),dt,off);
|
|
if (!transform.doTrace())
|
|
return 0;
|
|
transform.apply();
|
|
return 1;
|
|
}
|
|
|
|
void RuleBitFieldIn::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
uint4 list[]={ CPUI_COPY,
|
|
CPUI_INT_EQUAL, CPUI_INT_NOTEQUAL, CPUI_INT_SLESS, CPUI_INT_SLESSEQUAL, CPUI_INT_LESS, CPUI_INT_LESSEQUAL,
|
|
CPUI_INT_ZEXT, CPUI_INT_SEXT,
|
|
CPUI_INT_ADD, CPUI_INT_NEGATE,
|
|
CPUI_INT_AND, CPUI_INT_LEFT, CPUI_INT_RIGHT, CPUI_INT_SRIGHT, CPUI_INT_MULT,
|
|
CPUI_SUBPIECE };
|
|
oplist.insert(oplist.end(),list,list+17);
|
|
}
|
|
|
|
int4 RuleBitFieldIn::applyOp(PcodeOp *op,Funcdata &data)
|
|
|
|
{
|
|
Varnode *invn = op->getIn(0);
|
|
Datatype *dt = invn->getTypeReadFacing(op);
|
|
if (!dt->hasBitfields()) return 0;
|
|
BitFieldPullTransform transform(&data,invn,dt,0);
|
|
if (!transform.doTrace())
|
|
return 0;
|
|
transform.apply();
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform transforms involving the expression: `field >> #c`
|
|
///
|
|
/// \param data is the function
|
|
/// \param rightOp is the INT_RIGHT or INT_SRIGHT op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if a transform was performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbRight(Funcdata &data,PcodeOp *rightOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter;
|
|
Varnode *outvn = rightOp->getOut();
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *readOp = *iter;
|
|
if (readOp->code() == CPUI_INT_AND) {
|
|
int4 res = absorbRightAndCompZero(data,rightOp,readOp,pullOp);
|
|
if (res != 0) return res;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Perform transform: `((sfield >> #n) & #1) == #0 => #0 <= sfield`
|
|
///
|
|
/// Perform the variant: `((sfield >> #n) & #1) != #0 => sfield < #0`
|
|
/// \param data is the function
|
|
/// \param rightOp is the INT_RIGHT or INT_SRIGHT op
|
|
/// \param andOp is the INT_AND
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if a transform was performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbRightAndCompZero(Funcdata &data,PcodeOp *rightOp,PcodeOp *andOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
if (pullOp->code() != CPUI_SPULL) return 0;
|
|
Varnode *cvn = rightOp->getIn(1);
|
|
if (!cvn->isConstant()) return 0;
|
|
int4 sa = cvn->getOffset();
|
|
int4 numbits = pullOp->getIn(2)->getOffset();
|
|
if (numbits -1 != sa) return 0; // Check that shift puts sign bit into least sig position
|
|
if (!andOp->getIn(1)->constantMatch(1)) return 0;
|
|
list<PcodeOp *>::const_iterator iter;
|
|
Varnode *outvn = andOp->getOut();
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *readOp = *iter;
|
|
OpCode opc = readOp->code();
|
|
if (opc != CPUI_INT_EQUAL && opc != CPUI_INT_NOTEQUAL) continue;
|
|
if (!readOp->getIn(1)->constantMatch(0)) continue;
|
|
Varnode *vn = pullOp->getOut();
|
|
if (opc == CPUI_INT_EQUAL) {
|
|
data.opSetOpcode(readOp, CPUI_INT_LESSEQUAL);
|
|
Varnode *zvn = readOp->getIn(1);
|
|
data.opSetInput(readOp,vn,1);
|
|
data.opSetInput(readOp,zvn,0);
|
|
}
|
|
else {
|
|
data.opSetOpcode(readOp,CPUI_INT_SLESS);
|
|
data.opSetInput(readOp,vn,0);
|
|
}
|
|
data.destroyVarnodeRecursive(outvn);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Perform transforms involving the expression: `field << #c`
|
|
///
|
|
/// \param data is the function
|
|
/// \param leftOp is the INT_LEFT op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if a transform was performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbLeft(Funcdata &data,PcodeOp *leftOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter;
|
|
Varnode *outvn = leftOp->getOut();
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *readOp = *iter;
|
|
int4 res = 0;
|
|
OpCode opc = readOp->code();
|
|
if (opc == CPUI_INT_SLESS)
|
|
res = absorbCompare(data,readOp,leftOp,pullOp);
|
|
else if (opc == CPUI_INT_RIGHT)
|
|
res = absorbLeftRight(data,readOp,leftOp,pullOp);
|
|
else if (opc == CPUI_INT_AND)
|
|
res = absorbLeftAnd(data,readOp,leftOp,pullOp);
|
|
if (res != 0) return res;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Perform the transform: `(field << #c) >> #d => field >> (#d-#c)`
|
|
///
|
|
/// \param data is the function
|
|
/// \param rightOp is the INT_RIGHT op
|
|
/// \param leftOp is the INT_LEFT op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if a transform was performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbLeftRight(Funcdata &data,PcodeOp *rightOp,PcodeOp *leftOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
Varnode *leftcvn = leftOp->getIn(1);
|
|
if (!leftcvn->isConstant()) return 0;
|
|
Varnode *rightcvn = rightOp->getIn(1);
|
|
if (!rightcvn->isConstant()) return 0;
|
|
int4 bitsize = pullOp->getIn(2)->getOffset();
|
|
Varnode *invn = pullOp->getIn(0);
|
|
int4 containerSize = invn->getSize() * 8;
|
|
int4 leftshift = leftcvn->getOffset();
|
|
int4 rightshift = rightcvn->getOffset();
|
|
if (leftshift + bitsize > containerSize) return 0; // Check if left shift destroys field data
|
|
int4 sa = rightshift - leftshift;
|
|
if (sa == 0) {
|
|
data.totalReplace(rightOp->getOut(),pullOp->getOut());
|
|
data.destroyVarnodeRecursive(rightOp->getOut());
|
|
}
|
|
else if (sa > 0) { // Right shift is bigger than left
|
|
data.opSetInput(rightOp, data.newConstant(rightcvn->getSize(),sa), 1);
|
|
data.opSetInput(rightOp, pullOp->getOut(),0);
|
|
data.destroyVarnodeRecursive(leftOp->getOut());
|
|
}
|
|
else { // Left shift is bigger than right
|
|
data.opSetOpcode(rightOp, CPUI_INT_LEFT);
|
|
data.opSetInput(rightOp, data.newConstant(rightcvn->getSize(),-sa), 1);
|
|
data.opSetInput(rightOp, pullOp->getOut(),0);
|
|
data.destroyVarnodeRecursive(leftOp->getOut());
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform the transform: `((field << #c) & #b) == #d => (field & #b>>c) == #d>>c`
|
|
///
|
|
/// \param data is the function
|
|
/// \param andOp is the INT_AND op
|
|
/// \param leftOp is the INT_LEFT op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if a transform was performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbLeftAnd(Funcdata &data,PcodeOp *andOp,PcodeOp *leftOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
Varnode *shiftAmount = leftOp->getIn(1);
|
|
if (!shiftAmount->isConstant()) return 0;
|
|
int4 sa = shiftAmount->getOffset();
|
|
if (sa < 0 || sa >= sizeof(uintb)*8) return 0;
|
|
Varnode *maskVn = andOp->getIn(1);
|
|
if (!maskVn->isConstant()) return 0;
|
|
uintb mask = maskVn->getOffset();
|
|
list<PcodeOp *>::const_iterator iter;
|
|
Varnode *outvn = andOp->getOut();
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *readOp = *iter;
|
|
OpCode opc = readOp->code();
|
|
if (opc == CPUI_INT_EQUAL || opc == CPUI_INT_NOTEQUAL) {
|
|
Varnode *compVal = readOp->getIn(1);
|
|
if (!compVal->isConstant()) continue;
|
|
uintb val = compVal->getOffset() >> sa;
|
|
if (val << sa != compVal->getOffset()) continue;
|
|
mask >>= sa;
|
|
Varnode *newAnd = data.newConstant(maskVn->getSize(), mask);
|
|
newAnd->updateType(maskVn->getType());
|
|
data.opSetInput(andOp,newAnd,1);
|
|
if (val != compVal->getOffset()) {
|
|
Varnode *newVal = data.newConstant(compVal->getSize(),val);
|
|
newVal->updateType(compVal->getType());
|
|
data.opSetInput(readOp,newVal,1);
|
|
}
|
|
data.opSetInput(andOp,leftOp->getIn(0),0);
|
|
data.destroyVarnodeRecursive(leftOp->getOut());
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Perform transform: `field & #signbit == #0 => field < 0`
|
|
///
|
|
/// \param data is the function
|
|
/// \param andOp is the INT_AND op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if a transform was performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbAnd(Funcdata &data,PcodeOp *andOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
Varnode *maskVn = andOp->getIn(1);
|
|
if (!maskVn->isConstant()) return 0;
|
|
Varnode *vn = pullOp->getOut();
|
|
if (pullOp->code() != CPUI_SPULL) return 0; // Not signed
|
|
int4 bitsize = (int4)pullOp->getIn(2)->getOffset();
|
|
uintb matchVal = 1;
|
|
matchVal <<= (bitsize-1); // Mask for sign-bit
|
|
if (matchVal != maskVn->getOffset()) return 0;
|
|
list<PcodeOp *>::const_iterator iter;
|
|
Varnode *outvn = andOp->getOut();
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *readOp = *iter;
|
|
OpCode opc = readOp->code();
|
|
if (opc == CPUI_INT_EQUAL || opc == CPUI_INT_NOTEQUAL) {
|
|
if (!readOp->getIn(1)->constantMatch(0)) continue;
|
|
Varnode *newZero = data.newConstant(vn->getSize(),0);
|
|
Datatype *dt = data.getArch()->types->resizeInteger(vn->getType(),vn->getSize());
|
|
newZero->updateType(dt);
|
|
if (opc == CPUI_INT_EQUAL) {
|
|
data.opSetOpcode(readOp, CPUI_INT_SLESSEQUAL);
|
|
data.opSetInput(readOp,newZero,0);
|
|
data.opSetInput(readOp,vn,1);
|
|
}
|
|
else {
|
|
data.opSetOpcode(readOp, CPUI_INT_SLESS);
|
|
data.opSetInput(readOp,vn,0);
|
|
data.opSetInput(readOp,newZero,1);
|
|
}
|
|
data.destroyVarnodeRecursive(andOp->getOut());
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Perform transforms involving comparisons: INT_LESS, INT_SLESS
|
|
///
|
|
/// Perform transforms:
|
|
/// - `(boolfield << #c) s< #0 => boolfield`
|
|
/// - `#0 s< (boolfield << #c) => !boolfield`
|
|
/// - `(field << #c) < (#d<<#c) => field < #d`
|
|
/// - `(#d<<#c) < (field << #c) => #d < field`
|
|
///
|
|
/// \param data is the function
|
|
/// \param compOp is the INT_LESS or INT_SLESS op
|
|
/// \param leftOp is the INT_LEFT op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbCompare(Funcdata &data,PcodeOp *compOp,PcodeOp *leftOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
int4 sa = 0;
|
|
if (leftOp != (PcodeOp *)0) {
|
|
Varnode *cvn = leftOp->getIn(1);
|
|
if (!cvn->isConstant()) return 0;
|
|
sa = cvn->getOffset();
|
|
}
|
|
int4 numbits = pullOp->getIn(2)->getOffset();
|
|
Varnode *invn = pullOp->getIn(0);
|
|
int4 sz = invn->getSize() * 8;
|
|
if (numbits + sa != sz) // Verify that high bit of field is shifted into sign-bit
|
|
return 0;
|
|
Varnode *inVn = (leftOp == (PcodeOp *)0) ? pullOp->getOut() : leftOp->getOut();
|
|
Varnode *lessVn0 = compOp->getIn(0);
|
|
Varnode *lessVn1 = compOp->getIn(1);
|
|
if (compOp->code() == CPUI_INT_SLESS) {
|
|
if (numbits == 1 && lessVn0 == inVn && lessVn1->isConstant() && lessVn1->getOffset() == 0) {
|
|
Varnode *oldVn = compOp->getOut();
|
|
data.totalReplace(oldVn,pullOp->getOut());
|
|
data.destroyVarnodeRecursive(oldVn);
|
|
return 1;
|
|
}
|
|
if (numbits == 1 && lessVn1 == inVn && lessVn0->isConstant()
|
|
&& lessVn0->getOffset() == calc_mask(inVn->getSize())) {
|
|
data.opRemoveInput(compOp,0);
|
|
data.opSetOpcode(compOp,CPUI_BOOL_NEGATE);
|
|
data.opSetInput(compOp,pullOp->getOut(),0);
|
|
data.destroyVarnodeRecursive(inVn);
|
|
return 1;
|
|
}
|
|
}
|
|
uintb mask = 1;
|
|
mask = (mask << sa) -1;
|
|
if (sa > 0 && sa < 8*sizeof(uintb) && inVn == lessVn0 && lessVn1->isConstant()) {
|
|
uintb origVal = lessVn1->getOffset();
|
|
uintb lowBits = mask & origVal;
|
|
if (lowBits == 0 || lowBits == 1) {
|
|
uintb newVal;
|
|
if (lowBits == 1) {
|
|
newVal = (origVal - 1) >> sa; // Convert to constant for LESSEQUAL
|
|
newVal = (newVal + 1) & calc_mask(inVn->getSize()); // Convert back to LESS after shift
|
|
}
|
|
else
|
|
newVal = origVal >> sa;
|
|
data.opSetInput(compOp,pullOp->getOut(),0);
|
|
data.opSetInput(compOp,data.newConstant(inVn->getSize(), newVal),1);
|
|
data.destroyVarnodeRecursive(inVn);
|
|
return 1;
|
|
}
|
|
}
|
|
if (sa > 0 && sa < 8*sizeof(uintb) && inVn == lessVn1 && lessVn0->isConstant()) {
|
|
uintb origVal = lessVn0->getOffset();
|
|
uintb lowBits = mask & origVal;
|
|
if (lowBits == 0 || lowBits == mask) {
|
|
uintb newVal;
|
|
if (lowBits == mask) {
|
|
newVal = (origVal + 1) >> sa; // Convert to constant for LESSEQUAL
|
|
newVal = (newVal - 1) & calc_mask(inVn->getSize()); // Convert back to LESS after shift
|
|
}
|
|
else
|
|
newVal = origVal >> sa;
|
|
data.opSetInput(compOp,pullOp->getOut(),1);
|
|
data.opSetInput(compOp,data.newConstant(inVn->getSize(), newVal),0);
|
|
data.destroyVarnodeRecursive(inVn);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// \brief Perform transform: `y = SEXT( SPULL( x, #p, #n ) ) => y = SPULL( x, #p, #n )`
|
|
///
|
|
/// Also transform: `y = ZEXT( ZPULL( x, #p, #n ) ) => y = ZPULL( x, #p, #n )`
|
|
///
|
|
/// \param data is the function
|
|
/// \param extOp is the INT_SEXT or INT_ZEXT op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbExt(Funcdata &data,PcodeOp *extOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
bool pullSigned = pullOp->code() == CPUI_SPULL;
|
|
bool extSigned = extOp->code() == CPUI_INT_SEXT;
|
|
if (extSigned != pullSigned) return 0;
|
|
Varnode *vn = extOp->getIn(0);
|
|
if (vn->loneDescend() != extOp) return 0;
|
|
data.opSetOpcode(extOp, pullOp->code());
|
|
data.opSetInput(extOp,pullOp->getIn(0),0);
|
|
Varnode *posVn = pullOp->getIn(1);
|
|
Varnode *numVn = pullOp->getIn(2);
|
|
data.opInsertInput(extOp,posVn,1);
|
|
data.opInsertInput(extOp,numVn,2);
|
|
data.destroyVarnodeRecursive(vn);
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform transform: `y = SUB( PULL( x, #p, #n ) ) => y = PULL( x, #p, #n )`
|
|
///
|
|
/// \param data is the function
|
|
/// \param subOp is the SUBPIECE op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbSubpiece(Funcdata &data,PcodeOp *subOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
if (subOp->getIn(1)->getOffset() != 0) return 0;
|
|
int4 bitsize = (int4)pullOp->getIn(2)->getOffset();
|
|
Varnode *outvn = subOp->getOut();
|
|
if (bitsize > 8*outvn->getSize()) return 0;
|
|
Varnode *vn = subOp->getIn(0);
|
|
if (vn->loneDescend() != subOp) return 0;
|
|
data.opSetOpcode(subOp, pullOp->code());
|
|
data.opSetInput(subOp, pullOp->getIn(0), 0);
|
|
Varnode *posVn = pullOp->getIn(1);
|
|
Varnode *numVn = pullOp->getIn(2);
|
|
data.opSetInput(subOp,posVn,1);
|
|
data.opInsertInput(subOp,numVn,2);
|
|
data.destroyVarnodeRecursive(vn);
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform transform: `ZPULL( x, #p, #1) != #0 => ZPULL(x, #p, #1)`
|
|
///
|
|
/// Also transform the variant `ZPULL( x, #p, #1) == #0 => !ZPULL(x, #p, #1)`
|
|
/// \param data is the function
|
|
/// \param compOp is the INT_EQUAL or INT_NOTEQUAL op
|
|
/// \param pullOp is the ZPULL or SPULL op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RulePullAbsorb::absorbCompZero(Funcdata &data,PcodeOp *compOp,PcodeOp *pullOp)
|
|
|
|
{
|
|
Varnode *zvn = compOp->getIn(1);
|
|
if (!zvn->constantMatch(0)) return 0;
|
|
int4 bitsize = (int4)pullOp->getIn(2)->getOffset();
|
|
if (bitsize != 1) return 0;
|
|
Varnode *vn = compOp->getIn(0);
|
|
if (vn->loneDescend() != compOp) return 0;
|
|
if (vn->isAddrTied()) return 0;
|
|
if (pullOp->code() == CPUI_SPULL) return 0;
|
|
const TypeBitField *field = BitFieldExpression::getPullField(pullOp);
|
|
if (field == (const TypeBitField *)0 || field->type->getMetatype() != TYPE_BOOL)
|
|
return 0;
|
|
if (compOp->code() == CPUI_INT_EQUAL) {
|
|
if (vn->getSize() > 1) {
|
|
Address smalladdr = vn->getAddr();
|
|
if (vn->getSpace()->isBigEndian())
|
|
smalladdr = smalladdr + (vn->getSize() -1);
|
|
data.opUnsetOutput(pullOp);
|
|
Varnode *newVn = data.newVarnodeOut(1, smalladdr, pullOp);
|
|
Datatype *dt = data.getArch()->types->getBase(1,TYPE_BOOL);
|
|
newVn->updateType(dt);
|
|
data.opSetInput(compOp,newVn,0);
|
|
data.deleteVarnode(vn);
|
|
}
|
|
data.opSetOpcode(compOp,CPUI_BOOL_NEGATE);
|
|
data.opRemoveInput(compOp, 1);
|
|
}
|
|
else {
|
|
data.opSetOpcode(compOp,pullOp->code());
|
|
data.opSetInput(compOp, pullOp->getIn(0), 0);
|
|
Varnode *posVn = pullOp->getIn(1);
|
|
Varnode *numVn = pullOp->getIn(2);
|
|
data.opSetInput(compOp,posVn,1);
|
|
data.opInsertInput(compOp,numVn,2);
|
|
data.destroyVarnodeRecursive(vn);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void RulePullAbsorb::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
oplist.push_back(CPUI_ZPULL);
|
|
oplist.push_back(CPUI_SPULL);
|
|
}
|
|
|
|
int4 RulePullAbsorb::applyOp(PcodeOp *op,Funcdata &data)
|
|
|
|
{
|
|
list<PcodeOp *>::const_iterator iter;
|
|
Varnode *outvn = op->getOut();
|
|
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
|
|
PcodeOp *readOp = *iter;
|
|
int4 res = 0;
|
|
switch(readOp->code()) {
|
|
case CPUI_INT_RIGHT:
|
|
case CPUI_INT_SRIGHT:
|
|
res = absorbRight(data,readOp,op);
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
res = absorbLeft(data,readOp,op);
|
|
break;
|
|
case CPUI_INT_AND:
|
|
res = absorbAnd(data,readOp,op);
|
|
break;
|
|
case CPUI_INT_SLESS:
|
|
case CPUI_INT_LESS:
|
|
res = absorbCompare(data,readOp,(PcodeOp *)0,op);
|
|
break;
|
|
case CPUI_INT_ZEXT:
|
|
case CPUI_INT_SEXT:
|
|
res = absorbExt(data, readOp, op);
|
|
break;
|
|
case CPUI_SUBPIECE:
|
|
res = absorbSubpiece(data, readOp, op);
|
|
break;
|
|
case CPUI_INT_EQUAL:
|
|
case CPUI_INT_NOTEQUAL:
|
|
res = absorbCompZero(data, readOp, op);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (res != 0) return res;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// If the Varnode is shifted, return the Varnode stripped of the shift.
|
|
/// \param vn is the Varnode to test
|
|
/// \param sa is the given shift amount
|
|
/// \return the stripped Varnode or null
|
|
Varnode *RuleInsertAbsorb::leftShiftVarnode(Varnode *vn,int sa)
|
|
|
|
{
|
|
if (!vn->isWritten()) return (Varnode *)0;
|
|
PcodeOp *multOp = vn->getDef();
|
|
Varnode *multVal = multOp->getIn(1);
|
|
if (!multVal->isConstant()) return (Varnode *)0;
|
|
uintb matchVal;
|
|
if (multOp->code() == CPUI_INT_MULT) {
|
|
matchVal = 1;
|
|
matchVal <<= sa;
|
|
}
|
|
else if (multOp->code() == CPUI_INT_LEFT) {
|
|
matchVal = sa;
|
|
}
|
|
else
|
|
return (Varnode *)0;
|
|
if (multVal->getOffset() != matchVal) return (Varnode *)0;
|
|
return multOp->getIn(0);
|
|
}
|
|
|
|
/// \brief Perform the transform: `INSERT( x & #mask, #p, #n ) => INSERT( x, #p, #n )`
|
|
///
|
|
/// \param data is the function
|
|
/// \param andOp is the INT_AND op
|
|
/// \param insertOp is the INSERT op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RuleInsertAbsorb::absorbAnd(Funcdata &data,PcodeOp *andOp,PcodeOp *insertOp)
|
|
|
|
{
|
|
Varnode *cvn = andOp->getIn(1);
|
|
if (!cvn->isConstant()) return 0;
|
|
uintb val = cvn->getOffset();
|
|
uintb mask = InsertExpression::getLSBMask(insertOp);
|
|
if ((mask & val) != mask) return 0; // AND mask must be of least significant bits that get INSERTed
|
|
data.opSetInput(insertOp,andOp->getIn(0),1);
|
|
data.destroyVarnodeRecursive(andOp->getOut());
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform the transform: `INSERT( (x << #c) >> #c, #p, #n ) => INSERT(x, #p, #n )`
|
|
///
|
|
/// Also transform the variant: `INSERT( SUB( x << #c, #0) >> #c, #p, #n ) => INSERT(x, #p, #n)`
|
|
int4 RuleInsertAbsorb::absorbRightLeft(Funcdata &data,PcodeOp *nextOp,PcodeOp *rightOp,PcodeOp *insertOp)
|
|
|
|
{
|
|
PcodeOp *leftOp;
|
|
if (nextOp->code() == CPUI_INT_LEFT)
|
|
leftOp = nextOp;
|
|
else if (nextOp->code() == CPUI_SUBPIECE) {
|
|
if (nextOp->getIn(1)->getOffset() != 0)
|
|
return 0;
|
|
Varnode *subin = nextOp->getIn(0);
|
|
if (!subin->isWritten()) return 0;
|
|
leftOp = subin->getDef();
|
|
if (leftOp->code() != CPUI_INT_LEFT) return 0;
|
|
}
|
|
else
|
|
return 0;
|
|
Varnode *lvn = leftOp->getIn(1);
|
|
if (!lvn->isConstant()) return 0;
|
|
Varnode *rvn = rightOp->getIn(1);
|
|
if (!rvn->isConstant()) return 0;
|
|
int4 lsa = (int4)lvn->getOffset();
|
|
int4 rsa = (int4)rvn->getOffset();
|
|
if (lsa != rsa) return 0;
|
|
int4 bitsize = (int4)insertOp->getIn(3)->getOffset();
|
|
if (bitsize > insertOp->getIn(1)->getSize() * 8 - lsa) // Shifts cancel unless bitsize exceeds number of bits preserved
|
|
return 0;
|
|
data.opSetInput(insertOp,leftOp->getIn(0),1);
|
|
data.destroyVarnodeRecursive(rightOp->getOut());
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform the transform: `field = (a * #c + b * #c) >> #n => field = a + b`
|
|
///
|
|
/// \param data is the function
|
|
/// \param rightOp is the INT_RIGHT or INT_SRIGHT op
|
|
/// \param addOp is the INT_ADD op
|
|
/// \param insertOp is the INSERT op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RuleInsertAbsorb::absorbShiftAdd(Funcdata &data,PcodeOp *rightOp,PcodeOp *addOp,PcodeOp *insertOp)
|
|
|
|
{
|
|
int4 sa = (int4)rightOp->getIn(1)->getOffset();
|
|
if (sa <=0 || sa >= 8*sizeof(uintb))
|
|
return 0;
|
|
Varnode *vn0 = leftShiftVarnode(addOp->getIn(0),sa);
|
|
if (vn0 == (Varnode *)0) return 0;
|
|
Varnode *vn1;
|
|
Varnode *addVn1 = addOp->getIn(1);
|
|
if (addVn1->isConstant()) {
|
|
uintb addVal = addVn1->getOffset();
|
|
addVal >>= sa;
|
|
if ((addVal << sa) != addVn1->getOffset()) return 0;
|
|
vn1 = data.newConstant(vn0->getSize(), addVal);
|
|
vn1->updateType(addVn1->getType());
|
|
}
|
|
else {
|
|
vn1 = leftShiftVarnode(addVn1,sa);
|
|
if (vn1 == (Varnode *)0) return 0;
|
|
}
|
|
int4 bitsize = (int4)insertOp->getIn(3)->getOffset();
|
|
if (bitsize > vn0->getSize() * 8 - sa) // Check that none of the carry bits make it into field
|
|
return 0;
|
|
data.opSetOpcode(rightOp, CPUI_INT_ADD);
|
|
data.opSetInput(rightOp, vn0, 0);
|
|
data.opSetInput(rightOp, vn1, 1);
|
|
data.destroyVarnodeRecursive(addOp->getOut());
|
|
return 1;
|
|
}
|
|
|
|
/// \brief Perform transforms like: `INSERT( (x & #0xff) + y ) => INSERT( x + y )`
|
|
///
|
|
/// The op feeding the INSERT can be any operation where more significant bits of the input do not affect the less significant bits.
|
|
/// \param data is the function
|
|
/// \param baseOp is one of INT_ADD, INT_AND, INT_OR, or INT_XOR.
|
|
/// \param insertOp is the INSERT op
|
|
/// \return 1 if transform performed, 0 otherwise
|
|
int4 RuleInsertAbsorb::absorbNestedAnd(Funcdata &data,PcodeOp *baseOp,PcodeOp *insertOp)
|
|
|
|
{
|
|
if (baseOp->getOut()->loneDescend() != insertOp) return 0; // Result only used by INSERT
|
|
for(int4 slot=0;slot<2;++slot) {
|
|
Varnode *vn = baseOp->getIn(slot);
|
|
if (!vn->isWritten()) continue;
|
|
PcodeOp *andOp = vn->getDef();
|
|
if (andOp->code() != CPUI_INT_AND) continue;
|
|
Varnode *cvn = andOp->getIn(1);
|
|
if (!cvn->isConstant()) continue;
|
|
uintb mask = coveringmask(cvn->getOffset());
|
|
if (mask != cvn->getOffset()) continue;
|
|
if ((mask & 1)==0) continue; // Masking off least significant bits
|
|
int4 count = popcount(mask);
|
|
int4 bitsize = (int4)insertOp->getIn(3)->getOffset();
|
|
if (count < bitsize) continue; // INSERT masks off fewer bits, so AND still has an effect
|
|
data.opSetInput(baseOp,andOp->getIn(0),slot);
|
|
data.destroyVarnodeRecursive(andOp->getOut());
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RuleInsertAbsorb::getOpList(vector<uint4> &oplist) const
|
|
|
|
{
|
|
oplist.push_back(CPUI_INSERT);
|
|
}
|
|
|
|
int4 RuleInsertAbsorb::applyOp(PcodeOp *op,Funcdata &data)
|
|
|
|
{
|
|
Varnode *inVn = op->getIn(1);
|
|
if (!inVn->isWritten()) return 0;
|
|
PcodeOp *inOp = inVn->getDef();
|
|
Varnode *vn;
|
|
PcodeOp *nextOp;
|
|
OpCode opc;
|
|
switch(inOp->code()) {
|
|
case CPUI_SUBPIECE:
|
|
if (inOp->getIn(1)->getOffset() != 0) return 0;
|
|
data.opSetInput(op,inOp->getIn(0),1);
|
|
data.destroyVarnodeRecursive(inVn);
|
|
return 1;
|
|
case CPUI_INT_RIGHT:
|
|
case CPUI_INT_SRIGHT:
|
|
if (!inOp->getIn(1)->isConstant()) return 0;
|
|
vn = inOp->getIn(0);
|
|
if (!vn->isWritten()) return 0;
|
|
nextOp = vn->getDef();
|
|
opc = nextOp->code();
|
|
if (opc == CPUI_INT_ADD)
|
|
return absorbShiftAdd(data, inOp, nextOp, op);
|
|
else if (opc == CPUI_INT_LEFT || opc == CPUI_SUBPIECE)
|
|
return absorbRightLeft(data, nextOp, inOp, op);
|
|
break;
|
|
case CPUI_INT_AND:
|
|
return absorbAnd(data,inOp,op);
|
|
case CPUI_INT_ADD:
|
|
case CPUI_INT_OR:
|
|
case CPUI_INT_XOR:
|
|
case CPUI_INT_MULT:
|
|
return absorbNestedAnd(data, inOp, op);
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // End namespace ghidra
|