|
decompiler 1.0.0
|
Class that converts bitfield insertion expressions into explicit INSERT operations. More...
#include <bitfield.hh>
Classes | |
| class | InsertRecord |
| Info about a Varnode that can be treated as a write to a single bitfield. More... | |
Public Member Functions | |
| BitFieldInsertTransform (Funcdata *f, PcodeOp *op, Datatype *dt, int4 off) | |
| Construct from a terminating op. | |
| bool | doTrace (void) |
| Trace bitfields backward from the terminating op. | |
| void | apply (void) |
| Transform recovered expressions into INSERT operations. | |
Public Member Functions inherited from ghidra::BitFieldTransform | |
| BitFieldTransform (Funcdata *f, Datatype *dt, int4 off) | |
| Constructor setting up basic info about bitfield data-type. | |
Private Member Functions | |
| bool | verifyLoadStoreOriginalValue (uintb mask) const |
| Test for other STORE ops interfering with the original value. | |
| bool | verifyMappedOriginalValue (uintb mask) const |
| Test for other ops interfering with the mapped original value. | |
| uintb | constructOriginalValueMask (void) const |
| Calculate mask where 1 bits represent all the bits being preserved. | |
| bool | verifyOriginalValueBits (void) const |
| Do final check that unINSERTed bits come from the original value. | |
| bool | isOverwrittenPartial (const BitFieldNodeState &state) |
| Is given state a partial field that is overwritten later. | |
| bool | checkPulledOriginalValue (BitFieldNodeState &state) |
| Is this an original value defined by ZPULL or SPULL. | |
| bool | checkOriginalBase (Varnode *vn) |
| Check if the given Varnode is the original LOAD or mapped value. | |
| bool | isOriginalValue (BitFieldNodeState &state) |
| Is the given Varnode a (partial) copy of the original value being INSERTed into. | |
| bool | addConstantWrite (BitFieldNodeState &state) |
| Create InsertRecord writing a constant into the field. | |
| bool | addZeroOut (BitFieldNodeState &state) |
| Create InsertRecord writing 0 into the field. | |
| void | addFieldWrite (BitFieldNodeState &state) |
| Create InsertRecord writing Varnode into the field. | |
| bool | handleAndBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through INT_AND with a mask. | |
| bool | handleOrBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through one branch of INT_OR. | |
| bool | handleAddBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through one branch of INT_AND. | |
| bool | handleLeftBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through INT_LEFT by a constant. | |
| bool | handleRightBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through INT_SRIGHT by a constant. | |
| bool | handleZextBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through INT_ZEXT. | |
| bool | handleMultBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through INT_MULT. | |
| bool | handleSubpieceBack (BitFieldNodeState &state, PcodeOp *op) |
| Follow bitfield back through SUBPIECE. | |
| bool | testCallOriginal (BitFieldNodeState &state, PcodeOp *op) |
| Test if a call is producing the original value. | |
| bool | processBackward (BitFieldNodeState &state) |
| Follow field back, creating an InsertRecord if possible. | |
| PcodeOp * | setInsertInputs (PcodeOp *op, const InsertRecord &rec) |
| Fill-in INSERT inputs based on given InsertRecord. | |
| void | addFieldShift (PcodeOp *insertOp, const InsertRecord &rec) |
| Create any shift p-code op specified by given InsertRecord. | |
| bool | foldLoad (PcodeOp *loadOp) const |
| Try to mark LOAD as part of INSERT. | |
| void | foldPtrsub (PcodeOp *loadOp) const |
| Try to mark PTRSUB as part of INSERT. | |
| void | checkRedundancy (const InsertRecord &rec) |
| Check if value is getting INSERTed twice and remove second. | |
Private Attributes | |
| PcodeOp * | finalWriteOp |
| STORE to bitfields or op outputing to bitfields. | |
| Varnode * | originalValue |
| Value prior to insertion. | |
| Varnode * | mappedVn |
| Bitfield container written to. | |
| list< InsertRecord > | insertList |
| Insertion actions. | |
Additional Inherited Members | |
Protected Member Functions inherited from ghidra::BitFieldTransform | |
| void | establishFields (Varnode *vn, bool followHoles) |
| Build worklist for each bitfield overlapped by given Varnode. | |
| Datatype * | buildPartialType (void) |
| Build the (partial) data-type associated with the root bitfield container. | |
Static Protected Member Functions inherited from ghidra::BitFieldTransform | |
| static bool | findOverwrite (Varnode *vn, BlockBasic *bl, const BitRange &range) |
| Return true if specified bits in a Varnode are overwritten in the same basic block. | |
Protected Attributes inherited from ghidra::BitFieldTransform | |
| Funcdata * | func |
| The containing function. | |
| TypeStruct * | parentStruct |
| Structure owning the bitfields. | |
| list< BitFieldNodeState > | workList |
| Fields that are being followed. | |
| int4 | initialOffset |
| Byte offset into parent structure. | |
| int4 | containerSize |
| Size of Varnode containing bitfields. | |
| bool | isBigEndian |
| Endianness associated with bitfields. | |
Class that converts bitfield insertion expressions into explicit INSERT operations.
The doTrace() method traces backward from a root Varnode that contains bitfields to find points that can be treated as a value written to an individual bitfield, creating an InsertRecord at each point. If all bits of the Varnode are accounted for, the apply() method transforms expressions based on any InsertRecord.
| ghidra::BitFieldInsertTransform::BitFieldInsertTransform | ( | Funcdata * | f, |
| PcodeOp * | op, | ||
| Datatype * | dt, | ||
| int4 | off | ||
| ) |
Construct from a terminating op.
| f | is the function |
| op | is the p-code terminating the putative bitfield expression |
| dt | is the structure containing bitfields |
| off | is the amount of offset |
References ghidra::PcodeOp::code(), ghidra::BitFieldTransform::containerSize, ghidra::CPUI_INDIRECT, ghidra::CPUI_STORE, ghidra::BitFieldTransform::establishFields(), finalWriteOp, ghidra::Varnode::getDef(), ghidra::PcodeOp::getIn(), ghidra::PcodeOp::getOut(), ghidra::Varnode::getSize(), ghidra::BitFieldTransform::initialOffset, ghidra::Varnode::isWritten(), mappedVn, and originalValue.
|
private |
Create InsertRecord writing a constant into the field.
If the state is not following a specific field, false is returned.
| state | gives the constant Varnode and field |
References ghidra::TypeBitField::bits, ghidra::BitFieldNodeState::bitsField, ghidra::BitRange::byteSize, ghidra::BitFieldNodeState::field, ghidra::BitRange::getMask(), ghidra::Datatype::getMetatype(), ghidra::Varnode::getOffset(), insertList, ghidra::BitRange::leastSigBit, ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, ghidra::BitFieldNodeState::origLeastSigBit, ghidra::TypeBitField::type, and ghidra::TYPE_INT.
Referenced by processBackward().
|
private |
Create any shift p-code op specified by given InsertRecord.
If necessary, a INT_RIGHT is performed on the (insertion value) input to INSERT.
| insertOp | is the INSERT |
| rec | is the given InsertRecord |
References ghidra::CPUI_INT_RIGHT, ghidra::BitFieldTransform::func, ghidra::PcodeOp::getAddr(), ghidra::PcodeOp::getIn(), ghidra::Varnode::getSize(), ghidra::Funcdata::newConstant(), ghidra::Funcdata::newOp(), ghidra::Funcdata::newUniqueOut(), ghidra::Funcdata::opInsertBefore(), ghidra::Funcdata::opSetInput(), ghidra::Funcdata::opSetOpcode(), and ghidra::BitFieldInsertTransform::InsertRecord::shiftAmount.
Referenced by apply().
|
private |
Create InsertRecord writing Varnode into the field.
| state | is the specific Varnode and field |
References ghidra::TypeBitField::bits, ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::field, ghidra::Datatype::getSize(), ghidra::Varnode::getSize(), insertList, ghidra::BitRange::leastSigBit, ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, ghidra::BitFieldNodeState::origLeastSigBit, and ghidra::TypeBitField::type.
Referenced by processBackward().
|
private |
Create InsertRecord writing 0 into the field.
If the state is not following a specific field, false is returned. The state will no longer be followed.
| state | describes the field |
References ghidra::TypeBitField::bits, ghidra::BitFieldNodeState::field, insertList, ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, ghidra::BitFieldNodeState::origLeastSigBit, and ghidra::TypeBitField::type.
Referenced by handleAndBack(), handleLeftBack(), handleMultBack(), handleZextBack(), and processBackward().
|
private |
Check if the given Varnode is the original LOAD or mapped value.
If the Varnode is a the initial value of the storage being inserted into, return true. This can be either the result of the initial LOAD or the mapped storage location being read directly.
| vn | is the given Varnode to check |
References ghidra::PcodeOp::code(), ghidra::CPUI_LOAD, ghidra::CPUI_STORE, finalWriteOp, ghidra::Varnode::getAddr(), ghidra::Varnode::getDef(), ghidra::PcodeOp::getIn(), ghidra::PcodeOp::getParent(), ghidra::Varnode::getSize(), ghidra::Varnode::isAddrTied(), ghidra::Varnode::isWritten(), mappedVn, and originalValue.
Referenced by checkPulledOriginalValue(), and isOriginalValue().
|
private |
Check if value is getting INSERTed twice and remove second.
Look for (first) two INSERT descendants of value being inserted. If these exist and are of the same form and in the same basic block, delete the second one.
| rec | is the record referencing the INSERTed value |
References ghidra::Varnode::beginDescend(), ghidra::PcodeOp::code(), ghidra::CPUI_INSERT, ghidra::CPUI_INT_RIGHT, ghidra::CPUI_STORE, ghidra::Varnode::endDescend(), finalWriteOp, ghidra::BitFieldTransform::func, ghidra::PcodeOp::getIn(), ghidra::Varnode::getOffset(), ghidra::SeqNum::getOrder(), ghidra::PcodeOp::getOut(), ghidra::PcodeOp::getParent(), ghidra::PcodeOp::getSeqNum(), ghidra::Varnode::loneDescend(), ghidra::Funcdata::opDestroyRecursive(), and ghidra::BitFieldInsertTransform::InsertRecord::vn.
Referenced by apply().
|
private |
Calculate mask where 1 bits represent all the bits being preserved.
Collect all bits which are not being INSERTed to by this transform. These must be from the original value of the storage location.
References ghidra::calc_mask(), ghidra::Varnode::getSize(), insertList, ghidra::BitFieldInsertTransform::InsertRecord::numBits, originalValue, and ghidra::BitFieldInsertTransform::InsertRecord::pos.
Referenced by verifyOriginalValueBits().
| bool ghidra::BitFieldInsertTransform::doTrace | ( | void | ) |
Trace bitfields backward from the terminating op.
Follow all field in the workList back and try to match insert expressions.
References insertList, isOverwrittenPartial(), processBackward(), verifyOriginalValueBits(), and ghidra::BitFieldTransform::workList.
Referenced by ghidra::RuleBitFieldStore::applyOp(), and ghidra::RuleBitFieldOut::applyOp().
|
private |
Try to mark LOAD as part of INSERT.
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.
| loadOp | is the LOAD |
References ghidra::Varnode::beginDescend(), ghidra::PcodeOp::code(), ghidra::CPUI_INSERT, ghidra::CPUI_SPULL, ghidra::CPUI_ZPULL, ghidra::Varnode::endDescend(), finalWriteOp, ghidra::BitFieldTransform::func, ghidra::PcodeOp::getOut(), and ghidra::Funcdata::opMarkNonPrinting().
Referenced by apply().
|
private |
Try to mark PTRSUB as part of INSERT.
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.
| loadOp | is the LOAD |
References ghidra::Varnode::beginDescend(), ghidra::PcodeOp::code(), ghidra::CPUI_LOAD, ghidra::CPUI_PTRSUB, ghidra::CPUI_STORE, ghidra::PcodeOp::doesSpecialPrinting(), ghidra::Varnode::endDescend(), ghidra::BitFieldTransform::func, ghidra::Varnode::getDef(), ghidra::PcodeOp::getIn(), ghidra::Varnode::isWritten(), ghidra::PcodeOp::notPrinted(), and ghidra::Funcdata::opMarkNonPrinting().
Referenced by apply().
|
private |
Follow bitfield back through INT_AND with a mask.
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 false.
| state | is the field being followed |
| op | is the INT_AND |
References addZeroOut(), ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::BitRange::byteSize, ghidra::PcodeOp::getIn(), ghidra::BitRange::getMask(), ghidra::Varnode::getOffset(), ghidra::BitRange::intersectMask(), ghidra::Varnode::isConstant(), and ghidra::BitFieldNodeState::node.
Referenced by processBackward().
|
private |
Follow bitfield back through INT_LEFT by a constant.
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 false.
| state | is the field being followed |
| op | is the INT_LEFT |
References addZeroOut(), ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::PcodeOp::getIn(), ghidra::Varnode::getOffset(), ghidra::Varnode::isConstant(), ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, and ghidra::BitRange::shift().
Referenced by processBackward().
|
private |
Follow bitfield back through INT_MULT.
Treat INT_MULT by a power of 2 like INT_LEFT.
| state | is the field being followed |
| op | is the INT_MULT |
References addZeroOut(), ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::PcodeOp::getIn(), ghidra::Varnode::getOffset(), ghidra::Varnode::isConstant(), ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, and ghidra::BitRange::shift().
Referenced by processBackward().
|
private |
Follow bitfield back through one branch of INT_OR.
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 false;
| state | is the field being followed |
| op | is the INT_OR |
References ghidra::BitFieldNodeState::bitsField, ghidra::BitRange::byteSize, ghidra::PcodeOp::getIn(), ghidra::BitRange::getMask(), ghidra::Varnode::getNZMask(), ghidra::Varnode::isConstant(), and ghidra::BitFieldNodeState::node.
Referenced by processBackward().
|
private |
Follow bitfield back through INT_SRIGHT by a constant.
Update the state to reflect the shift.
| state | is the field being followed |
| op | is the INT_RIGHT |
References ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::PcodeOp::getIn(), ghidra::Varnode::getOffset(), ghidra::Varnode::isConstant(), ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, and ghidra::BitRange::shift().
Referenced by processBackward().
|
private |
Follow bitfield back through SUBPIECE.
Follow the field into the input of the SUBPIECE, which may have shifted it
| state | is the field being followed |
| op | is the SUBPIECE |
References ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::BitRange::extendBytes(), ghidra::PcodeOp::getIn(), ghidra::Varnode::getOffset(), ghidra::Varnode::getSize(), ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, and ghidra::BitRange::shift().
Referenced by processBackward().
|
private |
Follow bitfield back through INT_ZEXT.
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 false.
| state | is the field being followed |
| op | is the INT_ZEXT |
References addZeroOut(), ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::PcodeOp::getIn(), ghidra::PcodeOp::getOut(), ghidra::Varnode::getSize(), ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, and ghidra::BitRange::truncateMostSigBytes().
Referenced by processBackward().
|
private |
Is the given Varnode a (partial) copy of the original value being INSERTed into.
| state | is the given Varnode |
References ghidra::BitFieldNodeState::bitsField, checkOriginalBase(), checkPulledOriginalValue(), ghidra::BitRange::leastSigBit, ghidra::BitFieldNodeState::node, originalValue, and ghidra::BitFieldNodeState::origLeastSigBit.
Referenced by processBackward().
|
private |
Is given state a partial field that is overwritten later.
If the state is for a partial field whose storage location is overwritten later in the same basic block, return true
| state | is the field |
References ghidra::BitFieldNodeState::bitsField, ghidra::BitRange::byteSize, ghidra::PcodeOp::code(), ghidra::CPUI_STORE, ghidra::BitFieldNodeState::field, finalWriteOp, ghidra::BitFieldTransform::findOverwrite(), ghidra::PcodeOp::getParent(), ghidra::Varnode::getSize(), ghidra::BitFieldTransform::initialOffset, ghidra::BitFieldTransform::isBigEndian, mappedVn, ghidra::BitRange::numBits, and ghidra::BitFieldNodeState::origLeastSigBit.
Referenced by doTrace().
|
private |
Follow field back, creating an InsertRecord if possible.
| state | is the field to follow backward |
References addConstantWrite(), addFieldWrite(), addZeroOut(), ghidra::BitFieldNodeState::bitsField, ghidra::BitFieldNodeState::bitsUsed, ghidra::BitRange::byteSize, ghidra::PcodeOp::code(), ghidra::CPUI_CALL, ghidra::CPUI_CALLIND, ghidra::CPUI_CALLOTHER, ghidra::CPUI_COPY, ghidra::CPUI_INT_ADD, ghidra::CPUI_INT_AND, ghidra::CPUI_INT_LEFT, ghidra::CPUI_INT_MULT, ghidra::CPUI_INT_OR, ghidra::CPUI_INT_SRIGHT, ghidra::CPUI_INT_ZEXT, ghidra::CPUI_SUBPIECE, ghidra::BitFieldNodeState::field, ghidra::Varnode::getDef(), ghidra::PcodeOp::getIn(), ghidra::Varnode::getNZMask(), handleAddBack(), handleAndBack(), handleLeftBack(), handleMultBack(), handleOrBack(), handleRightBack(), handleSubpieceBack(), handleZextBack(), ghidra::BitRange::intersectMask(), ghidra::Varnode::isConstant(), ghidra::BitFieldNodeState::isFieldAligned(), isOriginalValue(), ghidra::Varnode::isWritten(), ghidra::BitFieldNodeState::node, ghidra::BitRange::numBits, and testCallOriginal().
Referenced by doTrace().
|
private |
Fill-in INSERT inputs based on given InsertRecord.
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.
| op | is a preexisting p-code op to reconfigure, or null |
| rec | is the record describing the INSERT |
References ghidra::BitFieldInsertTransform::InsertRecord::constVal, ghidra::BitFieldTransform::containerSize, ghidra::CPUI_INSERT, ghidra::BitFieldInsertTransform::InsertRecord::dt, finalWriteOp, ghidra::BitFieldTransform::func, ghidra::PcodeOp::getAddr(), ghidra::Datatype::getSize(), ghidra::Funcdata::newConstant(), ghidra::Funcdata::newOp(), ghidra::BitFieldInsertTransform::InsertRecord::numBits, ghidra::PcodeOp::numInput(), ghidra::Funcdata::opInsertInput(), ghidra::Funcdata::opMarkSpecialPrint(), ghidra::Funcdata::opSetInput(), ghidra::Funcdata::opSetOpcode(), originalValue, ghidra::BitFieldInsertTransform::InsertRecord::pos, ghidra::Varnode::updateType(), and ghidra::BitFieldInsertTransform::InsertRecord::vn.
Referenced by apply().
|
private |
Test if a call is producing the original value.
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.
| state | is the field being followed |
| op | is the call |
References ghidra::BitFieldNodeState::bitsField, ghidra::PcodeOp::code(), ghidra::CPUI_STORE, finalWriteOp, ghidra::Datatype::getMetatype(), ghidra::TypePartialStruct::getOffset(), ghidra::PcodeOp::getOut(), ghidra::TypePartialStruct::getParent(), ghidra::Varnode::getTypeDefFacing(), ghidra::BitFieldTransform::initialOffset, ghidra::Varnode::isAddrTied(), ghidra::PcodeOp::isCall(), ghidra::BitRange::leastSigBit, mappedVn, originalValue, ghidra::BitFieldNodeState::origLeastSigBit, ghidra::BitFieldTransform::parentStruct, ghidra::TYPE_PARTIALSTRUCT, and ghidra::TYPE_STRUCT.
Referenced by processBackward().
|
private |
Test for other STORE ops interfering with the original value.
Verify that any STORE between the original value LOAD and the final STORE does not affect any of the known original value bits.
| mask | is the set of bits that must come from the putative original value |
References ghidra::BlockBasic::beginOp(), ghidra::PcodeOp::code(), ghidra::CPUI_INSERT, ghidra::CPUI_STORE, finalWriteOp, ghidra::PcodeOp::getBasicIter(), ghidra::Varnode::getDef(), ghidra::PcodeOp::getIn(), ghidra::Varnode::getOffset(), ghidra::PcodeOp::getParent(), ghidra::InsertExpression::getRangeMask(), ghidra::PcodeOp::isCall(), ghidra::Varnode::isWritten(), and originalValue.
Referenced by verifyOriginalValueBits().
|
private |
Test for other ops interfering with the mapped original value.
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
| mask | is the set of bits that must come from the putative original value |
References ghidra::BlockBasic::beginOp(), ghidra::PcodeOp::code(), ghidra::CPUI_INSERT, finalWriteOp, ghidra::Varnode::getAddr(), ghidra::PcodeOp::getBasicIter(), ghidra::Varnode::getDef(), ghidra::PcodeOp::getOut(), ghidra::PcodeOp::getParent(), ghidra::InsertExpression::getRangeMask(), ghidra::Varnode::getSize(), ghidra::PcodeOp::isCall(), ghidra::Varnode::isWritten(), and originalValue.
Referenced by verifyOriginalValueBits().
|
private |
Do final check that unINSERTed bits come from the original value.
References ghidra::PcodeOp::code(), constructOriginalValueMask(), ghidra::CPUI_STORE, finalWriteOp, originalValue, verifyLoadStoreOriginalValue(), and verifyMappedOriginalValue().
Referenced by doTrace().