ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc

1070 lines
34 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 "funcdata.hh"
namespace ghidra {
AttributeId ATTRIB_NOCODE = AttributeId("nocode",84);
ElementId ELEM_AST = ElementId("ast",115);
ElementId ELEM_FUNCTION = ElementId("function",116);
ElementId ELEM_HIGHLIST = ElementId("highlist",117);
ElementId ELEM_JUMPTABLELIST = ElementId("jumptablelist",118);
ElementId ELEM_VARNODES = ElementId("varnodes",119);
/// \param nm is the (base) name of the function, as a formal symbol
/// \param disp is the name used when displaying the function name in output
/// \param scope is Symbol scope associated with the function
/// \param addr is the entry address for the function
/// \param sym is the symbol representing the function
/// \param sz is the number of bytes (of code) in the function body
Funcdata::Funcdata(const string &nm,const string &disp,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz)
: baseaddr(addr),
funcp(),
vbank(scope->getArch()),
heritage(this),
covermerge(*this)
{ // Initialize high-level properties of
// function by giving address and size
functionSymbol = sym;
flags = 0;
clean_up_index = 0;
high_level_index = 0;
cast_phase_index = 0;
glb = scope->getArch();
minLanedSize = glb->getMinimumLanedRegisterSize();
name = nm;
displayName = disp;
size = sz;
AddrSpace *stackid = glb->getStackSpace();
if (nm.size()==0)
localmap = (ScopeLocal *)0; // Filled in by decode
else {
uint8 id;
if (sym != (FunctionSymbol *)0)
id = sym->getId();
else {
// Missing a symbol, build unique id based on address
id = 0x57AB12CD;
id = (id << 32) | (addr.getOffset() & 0xffffffff);
}
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
glb->symboltab->attachScope(newMap,scope); // This may throw and delete newMap
localmap = newMap;
funcp.setScope(localmap,baseaddr+ -1);
localmap->resetLocalWindow();
}
activeoutput = (ParamActive *)0;
#ifdef OPACTION_DEBUG
jtcallback = (void (*)(Funcdata &orig,Funcdata &fd))0;
opactdbg_count = 0;
opactdbg_breakcount = -1;
opactdbg_on = false;
opactdbg_breakon = false;
opactdbg_active = false;
#endif
}
void Funcdata::clear(void)
{ // Clear everything associated with decompilation (analysis)
flags &= ~(highlevel_on|blocks_generated|processing_started|typerecovery_start|typerecovery_on|
double_precis_on|restart_pending);
clean_up_index = 0;
high_level_index = 0;
cast_phase_index = 0;
minLanedSize = glb->getMinimumLanedRegisterSize();
localmap->clearUnlocked(); // Clear non-permanent stuff
localmap->resetLocalWindow();
clearActiveOutput();
funcp.clearUnlockedOutput(); // Inputs are cleared by localmap
unionMap.clear();
clearBlocks();
obank.clear();
vbank.clear();
clearCallSpecs();
clearJumpTables();
// Do not clear overrides
heritage.clear();
covermerge.clear();
#ifdef OPACTION_DEBUG
opactdbg_count = 0;
#endif
}
/// The comment is added to the global database, indexed via its placement address and
/// the entry address of the function. The emitter will attempt to place the comment
/// before the source expression that maps most closely to the address.
/// \param txt is the string body of the comment
/// \param ad is the placement address
void Funcdata::warning(const string &txt,const Address &ad) const
{
string msg;
if ((flags & jumptablerecovery_on)!=0)
msg = "WARNING (jumptable): ";
else
msg = "WARNING: ";
msg += txt;
glb->commentdb->addCommentNoDuplicate(Comment::warning,baseaddr,ad,msg);
}
/// The warning will be emitted as part of the block comment printed right before the
/// prototype. The comment is stored in the global comment database, indexed via the function's
/// entry address.
/// \param txt is the string body of the comment
void Funcdata::warningHeader(const string &txt) const
{
string msg;
if ((flags & jumptablerecovery_on)!=0)
msg = "WARNING (jumptable): ";
else
msg = "WARNING: ";
msg += txt;
glb->commentdb->addCommentNoDuplicate(Comment::warningheader,baseaddr,baseaddr,msg);
}
/// This routine does basic set-up for analyzing the function. In particular, it
/// generates the raw p-code, builds basic blocks, and generates the call specification
/// objects.
void Funcdata::startProcessing(void)
{
if ((flags & processing_started)!=0)
throw LowlevelError("Function processing already started");
flags |= processing_started;
if (funcp.isInline())
warningHeader("This is an inlined function");
localmap->clearUnlocked();
funcp.clearUnlockedOutput();
Address baddr(baseaddr.getSpace(),0);
Address eaddr(baseaddr.getSpace(),~((uintb)0));
followFlow(baddr,eaddr);
structureReset();
sortCallSpecs(); // Must come after structure reset
heritage.buildInfoList();
localoverride.applyDeadCodeDelay(*this);
}
void Funcdata::stopProcessing(void)
{
flags |= processing_complete;
obank.destroyDead(); // Free up anything in the dead list
#ifdef CPUI_STATISTICS
glb->stats->process(*this);
#endif
}
bool Funcdata::startTypeRecovery(void)
{
if ((flags & typerecovery_start)!=0) return false; // Already started
flags |= typerecovery_start;
return true;
}
Funcdata::~Funcdata(void)
{
// clear();
if (localmap != (ScopeLocal *)0)
glb->symboltab->deleteScope(localmap);
clearCallSpecs();
for(int4 i=0;i<jumpvec.size();++i) // Delete jumptables
delete jumpvec[i];
glb = (Architecture *)0;
}
/// A representation of all PcodeOps in the function body are printed to the
/// stream. Depending on the state of analysis, PcodeOps are grouped into their
/// basic blocks, and within a block, ops are displayed sequentially. Basic labeling
/// of branch destinations is also printed. This is suitable for a console mode or
/// debug view of the state of the function at any given point in its analysis.
/// \param s is the output stream
void Funcdata::printRaw(ostream &s) const
{
if (bblocks.getSize()==0) {
if (obank.empty())
throw RecovError("No operations to print");
PcodeOpTree::const_iterator iter;
s << "Raw operations: \n";
for(iter=obank.beginAll();iter!=obank.endAll();++iter) {
s << (*iter).second->getSeqNum() << ":\t";
(*iter).second->printRaw(s);
s << endl;
}
}
else
bblocks.printRaw(s);
}
/// This routine searches for an marks Varnode objects, like stack-pointer registers,
/// that are used as a base address for a virtual address space. Each Varnode gets a
/// special data-type and is marked so that Varnode::isSpacebase() returns \b true.
void Funcdata::spacebase(void)
{
VarnodeLocSet::const_iterator iter,enditer;
int4 i,j,numspace;
Varnode *vn;
AddrSpace *spc;
for(j=0;j<glb->numSpaces();++j) {
spc = glb->getSpace(j);
if (spc == (AddrSpace *)0) continue;
numspace = spc->numSpacebase();
for(i=0;i<numspace;++i) {
const VarnodeData &point(spc->getSpacebase(i));
// Find input varnode at this size and location
Datatype *ct = glb->types->getTypeSpacebase(spc,getAddress());
Datatype *ptr = glb->types->getTypePointer(point.size,ct,spc->getWordSize());
iter = vbank.beginLoc(point.size,Address(point.space,point.offset));
enditer = vbank.endLoc(point.size,Address(point.space,point.offset));
while(iter != enditer) {
vn = *iter++;
if (vn->isFree()) continue;
if (vn->isSpacebase()) { // This has already been marked spacebase
// We have given it a chance for descendants to
// be eliminated naturally, now force a split if
// it still has multiple descendants
PcodeOp *op = vn->getDef();
if ((op != (PcodeOp *)0)&&(op->code() == CPUI_INT_ADD))
splitUses(vn);
}
else {
vn->setFlags(Varnode::spacebase); // Mark all base registers (not just input)
if (vn->isInput()) // Only set type on the input spacebase register
vn->updateType(ptr,true,true);
}
}
}
}
}
/// Given an address space, like \e stack, that is known to have a base register
/// pointing to it, construct a Varnode representing that register.
/// \param id is the \e stack like address space
/// \return a newly allocated stack-pointer Varnode
Varnode *Funcdata::newSpacebasePtr(AddrSpace *id)
{
Varnode *vn;
// Assume that id has a base register (otherwise an exception is thrown)
const VarnodeData &point(id->getSpacebase(0));
vn = newVarnode(point.size, Address(point.space,point.offset));
return vn;
}
/// Given an address space, like \e stack, that is known to have a base register
/// pointing to it, try to locate the unique Varnode that holds the input value
/// of this register.
/// \param id is the \e stack like address space
/// \return the input stack-pointer Varnode (or NULL if it doesn't exist)
Varnode *Funcdata::findSpacebaseInput(AddrSpace *id) const
{
Varnode *vn;
// Assume that id has a base register (otherwise an exception is thrown)
const VarnodeData &point(id->getSpacebase(0));
vn = vbank.findInput(point.size, Address(point.space,point.offset));
return vn;
}
/// \brief Convert a constant pointer into a \e ram CPUI_PTRSUB
///
/// A constant known to be a pointer into an address space like \b ram is converted
/// into a Varnode defined by CPUI_PTRSUB, which triggers a Symbol lookup at points
/// during analysis. The constant must point to a known Symbol.
///
/// The PTRSUB takes the constant 0 as its first input, which is marked
/// as a \e spacebase to indicate this situation. The second input to PTRSUB becomes
/// the offset to the Symbol within the address space. An additional INT_SUB may be inserted
/// to get from the start of the Symbol to the address indicated by the original
/// constant pointer.
/// \param op is the PcodeOp referencing the constant pointer
/// \param slot is the input slot of the constant pointer
/// \param entry is the Symbol being pointed (in)to
/// \param rampoint is the constant pointer interpreted as an Address
/// \param origval is the constant
/// \param origsize is the size of the constant
void Funcdata::spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const Address &rampoint,uintb origval,int4 origsize)
{
int4 sz = rampoint.getAddrSize();
AddrSpace *spaceid = rampoint.getSpace();
Datatype *sb_type = glb->types->getTypeSpacebase(spaceid,Address());
sb_type = glb->types->getTypePointer(sz,sb_type,spaceid->getWordSize());
Varnode *spacebase_vn,*outvn,*newconst;
uintb extra = rampoint.getOffset() - entry->getAddr().getOffset(); // Offset from beginning of entry
extra = AddrSpace::byteToAddress(extra,rampoint.getSpace()->getWordSize()); // Convert to address units
PcodeOp *addOp = (PcodeOp *)0;
PcodeOp *extraOp = (PcodeOp *)0;
PcodeOp *zextOp = (PcodeOp *)0;
PcodeOp *subOp = (PcodeOp *)0;
bool isCopy = false;
if (op->code() == CPUI_COPY) { // We replace COPY with final op of this calculation
isCopy = true;
if (sz < origsize)
zextOp = op;
else {
op->insertInput(1); // PTRSUB, ADD, SUBPIECE all take 2 parameters
if (origsize < sz)
subOp = op;
else if (extra != 0)
extraOp = op;
else
addOp = op;
}
}
spacebase_vn = newConstant(sz,0);
spacebase_vn->updateType(sb_type,true,true);
spacebase_vn->setFlags(Varnode::spacebase);
if (addOp == (PcodeOp *)0) {
addOp = newOp(2,op->getAddr());
opSetOpcode(addOp,CPUI_PTRSUB);
newUniqueOut(sz,addOp);
opInsertBefore(addOp,op);
}
else {
opSetOpcode(addOp,CPUI_PTRSUB);
}
outvn = addOp->getOut();
// Make sure newconstant and extra preserve origval in address units
uintb newconstoff = origval - extra; // everything is already in address units
newconst = newConstant(sz,newconstoff);
newconst->setPtrCheck(); // No longer need to check this constant as a pointer
if (spaceid->isTruncated())
addOp->setPtrFlow();
opSetInput(addOp,spacebase_vn,0);
opSetInput(addOp,newconst,1);
Symbol *sym = entry->getSymbol();
Datatype *entrytype = sym->getType();
Datatype *ptrentrytype = glb->types->getTypePointerStripArray(sz,entrytype,spaceid->getWordSize());
bool typelock = sym->isTypeLocked();
if (typelock && (entrytype->getMetatype() == TYPE_UNKNOWN))
typelock = false;
outvn->updateType(ptrentrytype,typelock,false);
if (extra != 0) {
if (extraOp == (PcodeOp *)0) {
extraOp = newOp(2,op->getAddr());
opSetOpcode(extraOp,CPUI_INT_ADD);
newUniqueOut(sz,extraOp);
opInsertBefore(extraOp,op);
}
else
opSetOpcode(extraOp,CPUI_INT_ADD);
Varnode *extconst = newConstant(sz,extra);
extconst->setPtrCheck();
opSetInput(extraOp,outvn,0);
opSetInput(extraOp,extconst,1);
outvn = extraOp->getOut();
}
if (sz < origsize) { // The new constant is smaller than the original varnode, so we extend it
if (zextOp == (PcodeOp *)0) {
zextOp = newOp(1,op->getAddr());
opSetOpcode(zextOp,CPUI_INT_ZEXT); // Create an extension to get back to original varnode size
newUniqueOut(origsize,zextOp);
opInsertBefore(zextOp,op);
}
else
opSetOpcode(zextOp,CPUI_INT_ZEXT);
opSetInput(zextOp,outvn,0);
outvn = zextOp->getOut();
}
else if (origsize < sz) { // The new constant is bigger than the original varnode, truncate it
if (subOp == (PcodeOp *)0) {
subOp = newOp(2,op->getAddr());
opSetOpcode(subOp,CPUI_SUBPIECE);
newUniqueOut(origsize,subOp);
opInsertBefore(subOp,op);
}
else
opSetOpcode(subOp,CPUI_SUBPIECE);
opSetInput(subOp,outvn,0);
opSetInput(subOp,newConstant(4, 0), 1); // Take least significant piece
outvn = subOp->getOut();
}
if (!isCopy)
opSetInput(op,outvn,slot);
}
void Funcdata::clearCallSpecs(void)
{
int4 i;
for(i=0;i<qlst.size();++i)
delete qlst[i]; // Delete each func_callspec
qlst.clear(); // Delete list of pointers
}
FuncCallSpecs *Funcdata::getCallSpecs(const PcodeOp *op) const
{
int4 i;
const Varnode *vn;
vn = op->getIn(0);
if (vn->getSpace()->getType()==IPTR_FSPEC)
return FuncCallSpecs::getFspecFromConst(vn->getAddr());
for(i=0;i<qlst.size();++i)
if (qlst[i]->getOp() == op) return qlst[i];
return (FuncCallSpecs *)0;
}
/// \brief Compare call specification objects by call site address
///
/// \param a is the first call specification to compare
/// \param b is the second call specification
/// \return \b true if the first call specification should come before the second
bool Funcdata::compareCallspecs(const FuncCallSpecs *a,const FuncCallSpecs *b)
{
int4 ind1,ind2;
ind1 = a->getOp()->getParent()->getIndex();
ind2 = b->getOp()->getParent()->getIndex();
if (ind1 != ind2) return (ind1 < ind2);
return (a->getOp()->getSeqNum().getOrder() < b->getOp()->getSeqNum().getOrder());
}
/// Calls are put in dominance order so that earlier calls get evaluated first.
/// Order affects parameter analysis.
void Funcdata::sortCallSpecs(void)
{
sort(qlst.begin(),qlst.end(),compareCallspecs);
}
/// This is used internally if a CALL is removed (because it is unreachable)
/// \param op is the particular specification to remove
void Funcdata::deleteCallSpecs(PcodeOp *op)
{
vector<FuncCallSpecs *>::iterator iter;
for(iter=qlst.begin();iter!=qlst.end();++iter) {
FuncCallSpecs *fc = *iter;
if (fc->getOp() == op) {
delete fc;
qlst.erase(iter);
return;
}
}
}
/// If \e extrapop is unknown, recover it from what we know about this function
/// and set the value permanently for \b this Funcdata object.
/// If there is no function body it may be impossible to know the value, in which case
/// this returns the reserved value indicating \e extrapop is unknown.
///
/// \return the recovered value
int4 Funcdata::fillinExtrapop(void)
{
if (hasNoCode()) // If no code to make a decision on
return funcp.getExtraPop(); // either we already know it or we don't
if (funcp.getExtraPop() != ProtoModel::extrapop_unknown)
return funcp.getExtraPop(); // If we already know it, just return it
list<PcodeOp *>::const_iterator iter = beginOp(CPUI_RETURN);
if (iter == endOp(CPUI_RETURN)) return 0; // If no return statements, answer is irrelevant
PcodeOp *retop = *iter;
uint1 buffer[4];
glb->loader->loadFill(buffer,4,retop->getAddr());
// We are assuming x86 code here
int4 extrapop = 4; // The default case
if (buffer[0] == 0xc2) {
extrapop = buffer[2]; // Pull out immediate 16-bits
extrapop <<= 8;
extrapop += buffer[1];
extrapop += 4; // extra 4 for the return address
}
funcp.setExtraPop( extrapop ); // Save what we have learned on the prototype
return extrapop;
}
/// A description of each Varnode currently involved in the data-flow of \b this
/// function is printed to the output stream. This is suitable as part of a console mode
/// or debug view of the function at any point during its analysis
/// \param s is the output stream
void Funcdata::printVarnodeTree(ostream &s) const
{
VarnodeDefSet::const_iterator iter,enditer;
Varnode *vn;
iter = vbank.beginDef();
enditer = vbank.endDef();
while(iter != enditer) {
vn = *iter++;
vn->printInfo(s);
}
}
/// Each scope has a set of memory ranges associated with it, encompassing
/// storage locations of variables that are \e assumed to be in the scope.
/// Each range for each local scope is printed.
/// \param s is the output stream
void Funcdata::printLocalRange(ostream &s) const
{
localmap->printBounds(s);
ScopeMap::const_iterator iter,enditer;
iter = localmap->childrenBegin();
enditer = localmap->childrenEnd();
for(;iter!=enditer;++iter) {
Scope *l1 = (*iter).second;
l1->printBounds(s);
}
}
/// Parse a \<jumptablelist> element and build a JumpTable object for
/// each \<jumptable> child element.
/// \param decoder is the stream decoder
void Funcdata::decodeJumpTable(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_JUMPTABLELIST);
while(decoder.peekElement() != 0) {
JumpTable *jt = new JumpTable(glb);
jt->decode(decoder);
jumpvec.push_back(jt);
}
decoder.closeElement(elemId);
}
/// A \<jumptablelist> element is written with \<jumptable> children describing
/// each jump-table associated with the control-flow of \b this function.
/// \param encoder is the stream encoder
void Funcdata::encodeJumpTable(Encoder &encoder) const
{
if (jumpvec.empty()) return;
vector<JumpTable *>::const_iterator iter;
encoder.openElement(ELEM_JUMPTABLELIST);
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter)
(*iter)->encode(encoder);
encoder.closeElement(ELEM_JUMPTABLELIST);
}
/// \brief Encode descriptions for a set of Varnodes to a stream
///
/// This is an internal function for the function's marshaling system.
/// Individual elements are written in sequence for Varnodes in a given set.
/// The set is bounded by iterators using the 'loc' ordering.
/// \param encoder is the stream encoder
/// \param iter is the beginning of the set
/// \param enditer is the end of the set
void Funcdata::encodeVarnode(Encoder &encoder,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer)
{
Varnode *vn;
while(iter!=enditer) {
vn = *iter++;
vn->encode(encoder);
}
}
/// This produces a single \<highlist> element, with a \<high> child for each
/// high-level variable (HighVariable) currently associated with \b this function.
/// \param encoder is the stream encoder
void Funcdata::encodeHigh(Encoder &encoder) const
{
Varnode *vn;
HighVariable *high;
if (!isHighOn()) return;
encoder.openElement(ELEM_HIGHLIST);
VarnodeLocSet::const_iterator iter;
for(iter=beginLoc();iter!=endLoc();++iter) {
vn = *iter;
if (vn->isAnnotation()) continue;
high = vn->getHigh();
if (high->isMark()) continue;
high->setMark();
high->encode(encoder);
}
for(iter=beginLoc();iter!=endLoc();++iter) {
vn = *iter;
if (!vn->isAnnotation())
vn->getHigh()->clearMark();
}
encoder.closeElement(ELEM_HIGHLIST);
}
/// A single \<ast> element is produced with children describing Varnodes, PcodeOps, and
/// basic blocks making up \b this function's current syntax tree.
/// \param encoder is the stream encoder
void Funcdata::encodeTree(Encoder &encoder) const
{
encoder.openElement(ELEM_AST);
encoder.openElement(ELEM_VARNODES);
for(int4 i=0;i<glb->numSpaces();++i) {
AddrSpace *base = glb->getSpace(i);
if (base == (AddrSpace *)0 || base->getType()==IPTR_IOP) continue;
VarnodeLocSet::const_iterator iter = vbank.beginLoc(base);
VarnodeLocSet::const_iterator enditer = vbank.endLoc(base);
encodeVarnode(encoder,iter,enditer);
}
encoder.closeElement(ELEM_VARNODES);
list<PcodeOp *>::iterator oiter,endoiter;
PcodeOp *op;
BlockBasic *bs;
for(int4 i=0;i<bblocks.getSize();++i) {
bs = (BlockBasic *)bblocks.getBlock(i);
encoder.openElement(ELEM_BLOCK);
encoder.writeSignedInteger(ATTRIB_INDEX, bs->getIndex());
bs->encodeBody(encoder);
oiter = bs->beginOp();
endoiter = bs->endOp();
while(oiter != endoiter) {
op = *oiter++;
op->encode(encoder);
}
encoder.closeElement(ELEM_BLOCK);
}
for(int4 i=0;i<bblocks.getSize();++i) {
bs = (BlockBasic *)bblocks.getBlock(i);
if (bs->sizeIn() == 0) continue;
encoder.openElement(ELEM_BLOCKEDGE);
encoder.writeSignedInteger(ATTRIB_INDEX, bs->getIndex());
bs->encodeEdges(encoder);
encoder.closeElement(ELEM_BLOCKEDGE);
}
encoder.closeElement(ELEM_AST);
}
/// A description of \b this function is written to the stream,
/// including name, address, prototype, symbol, jump-table, and override information.
/// If indicated by the caller, a description of the entire PcodeOp and Varnode
/// tree is also emitted.
/// \param encoder is the stream encoder
/// \param id is the unique id associated with the function symbol
/// \param savetree is \b true if the p-code tree should be emitted
void Funcdata::encode(Encoder &encoder,uint8 id,bool savetree) const
{
encoder.openElement(ELEM_FUNCTION);
if (id != 0)
encoder.writeUnsignedInteger(ATTRIB_ID, id);
encoder.writeString(ATTRIB_NAME, name);
encoder.writeSignedInteger(ATTRIB_SIZE, size);
if (hasNoCode())
encoder.writeBool(ATTRIB_NOCODE, true);
baseaddr.encode(encoder);
if (!hasNoCode()) {
localmap->encodeRecursive(encoder,false); // Save scope and all subscopes
}
if (savetree) {
encodeTree(encoder);
encodeHigh(encoder);
}
encodeJumpTable(encoder);
funcp.encode(encoder); // Must be saved after database
localoverride.encode(encoder,glb);
encoder.closeElement(ELEM_FUNCTION);
}
/// Parse a \<function> element, recovering the name, address, prototype, symbol,
/// jump-table, and override information for \b this function.
/// \param decoder is the stream decoder
/// \return the symbol id associated with the function
uint8 Funcdata::decode(Decoder &decoder)
{
// clear(); // Shouldn't be needed
name.clear();
size = -1;
uint8 id = 0;
AddrSpace *stackid = glb->getStackSpace();
uint4 elemId = decoder.openElement(ELEM_FUNCTION);
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId == ATTRIB_NAME)
name = decoder.readString();
else if (attribId == ATTRIB_SIZE) {
size = decoder.readSignedInteger();
}
else if (attribId == ATTRIB_ID) {
id = decoder.readUnsignedInteger();
}
else if (attribId == ATTRIB_NOCODE) {
if (decoder.readBool())
flags |= no_code;
}
else if (attribId == ATTRIB_LABEL)
displayName = decoder.readString();
}
if (name.size() == 0)
throw LowlevelError("Missing function name");
if (displayName.size() == 0)
displayName = name;
if (size == -1)
throw LowlevelError("Missing function size");
baseaddr = Address::decode( decoder );
for(;;) {
uint4 subId = decoder.peekElement();
if (subId == 0) break;
if (subId == ELEM_LOCALDB) {
if (localmap != (ScopeLocal *)0)
throw LowlevelError("Pre-existing local scope when restoring: "+name);
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
glb->symboltab->decodeScope(decoder,newMap); // May delete newMap and throw
localmap = newMap;
}
else if (subId == ELEM_OVERRIDE)
localoverride.decode(decoder,glb);
else if (subId == ELEM_PROTOTYPE) {
if (localmap == (ScopeLocal *)0) {
// If we haven't seen a <localdb> tag yet, assume we have a default local scope
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
Scope *scope = glb->symboltab->getGlobalScope();
glb->symboltab->attachScope(newMap,scope); // May delete newMap and throw
localmap = newMap;
}
funcp.setScope(localmap,baseaddr+ -1); // localmap built earlier
funcp.decode(decoder,glb);
}
else if (subId == ELEM_JUMPTABLELIST)
decodeJumpTable(decoder);
}
decoder.closeElement(elemId);
if (localmap == (ScopeLocal *)0) { // Seen neither <localdb> or <prototype>
// This is a function shell, so we provide default locals
ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb);
Scope *scope = glb->symboltab->getGlobalScope();
glb->symboltab->attachScope(newMap,scope); // May delete newMap and throw
localmap = newMap;
funcp.setScope(localmap,baseaddr+ -1);
}
localmap->resetLocalWindow();
return id;
}
/// \brief Inject p-code from a \e payload into \b this live function
///
/// Raw PcodeOps are generated from the payload within a given basic block at a specific
/// position in \b this function.
/// \param payload is the injection payload
/// \param addr is the address at the point of injection
/// \param bl is the given basic block holding the new ops
/// \param iter indicates the point of insertion
void Funcdata::doLiveInject(InjectPayload *payload,const Address &addr,BlockBasic *bl,list<PcodeOp *>::iterator iter)
{
PcodeEmitFd emitter;
InjectContext &context(glb->pcodeinjectlib->getCachedContext());
emitter.setFuncdata(this);
context.clear();
context.baseaddr = addr; // Shouldn't be using inst_next, inst_next2 or inst_start here
context.nextaddr = addr;
list<PcodeOp *>::const_iterator deaditer = obank.endDead();
bool deadempty = (obank.beginDead() == deaditer);
if (!deadempty)
--deaditer;
payload->inject(context,emitter);
// Calculate iterator to first injected op
if (deadempty)
deaditer = obank.beginDead();
else
++deaditer;
while(deaditer != obank.endDead()) {
PcodeOp *op = *deaditer;
++deaditer;
if (op->isCallOrBranch())
throw LowlevelError("Illegal branching injection");
opInsert(op,bl,iter);
}
}
void PcodeEmitFd::dump(const Address &addr,OpCode opc,VarnodeData *outvar,VarnodeData *vars,int4 isize)
{ // Convert template data into a real PcodeOp
PcodeOp *op;
Varnode *vn;
if (outvar != (VarnodeData *)0) {
Address oaddr(outvar->space,outvar->offset);
op = fd->newOp(isize,addr);
fd->newVarnodeOut(outvar->size,oaddr,op);
}
else
op = fd->newOp(isize,addr);
fd->opSetOpcode(op,opc);
int4 i=0;
if (op->isCodeRef()) { // Is the first input parameter a code reference
Address addrcode(vars[0].space,vars[0].offset);
// addrcode.toPhysical() // For backward compatibility with SLED
fd->opSetInput(op,fd->newCodeRef(addrcode),0);
i += 1;
// This is handled by FlowInfo
// if ((opc==CPUI_CALL)&&(addrcode==pos->getNaddr())) {
// This is probably PIC code and the call is really a jump
// fd->op_setopcode(op,CPUI_BRANCH);
// }
}
for(;i<isize;++i) {
vn = fd->newVarnode(vars[i].size,vars[i].space,vars[i].offset);
fd->opSetInput(op,vn,i);
}
}
/// \brief Get the resolved union field associated with the given edge
///
/// If there is no field associated with the edge, null is returned
/// \param parent is the data-type being resolved
/// \param op is the PcodeOp component of the given edge
/// \param slot is the slot component of the given edge
/// \return the associated field as a ResolvedUnion or null
const ResolvedUnion *Funcdata::getUnionField(const Datatype *parent,const PcodeOp *op,int4 slot) const
{
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
ResolveEdge edge(parent,op,slot);
iter = unionMap.find(edge);
if (iter != unionMap.end())
return &(*iter).second;
return (const ResolvedUnion *)0;
}
/// \brief Associate a union field with the given edge
///
/// If there was a previous association, it is overwritten unless it was \e locked.
/// The method returns \b true except in this case where a previous locked association exists.
/// \param parent is the parent union data-type
/// \param op is the PcodeOp component of the given edge
/// \param slot is the slot component of the given edge
/// \param resolve is the resolved union
/// \return \b true unless there was a locked association
bool Funcdata::setUnionField(const Datatype *parent,const PcodeOp *op,int4 slot,const ResolvedUnion &resolve)
{
ResolveEdge edge(parent,op,slot);
pair<map<ResolveEdge,ResolvedUnion>::iterator,bool> res;
res = unionMap.emplace(edge,resolve);
if (!res.second) {
if ((*res.first).second.isLocked()) {
return false;
}
(*res.first).second = resolve;
}
if (op->code() == CPUI_MULTIEQUAL && slot >= 0) {
// Data-type propagation doesn't happen between MULTIEQUAL input slots holding the same Varnode
// So if this is a MULTIEQUAL, copy resolution to any other input slots holding the same Varnode
const Varnode *vn = op->getIn(slot); // The Varnode being directly set
for(int4 i=0;i<op->numInput();++i) {
if (i == slot) continue;
if (op->getIn(i) != vn) continue; // Check that different input slot holds same Varnode
ResolveEdge dupedge(parent,op,i);
res = unionMap.emplace(dupedge,resolve);
if (!res.second) {
if (!(*res.first).second.isLocked())
(*res.first).second = resolve;
}
}
}
return true;
}
/// \brief Force a specific union field resolution for the given edge
///
/// The \b parent data-type is taken directly from the given Varnode.
/// \param parent is the parent data-type
/// \param fieldNum is the index of the field to force
/// \param op is PcodeOp of the edge
/// \param slot is -1 for the write edge or >=0 indicating the particular read edge
void Funcdata::forceFacingType(Datatype *parent,int4 fieldNum,PcodeOp *op,int4 slot)
{
Datatype *baseType = parent;
if (baseType->getMetatype() == TYPE_PTR)
baseType = ((TypePointer *)baseType)->getPtrTo();
if (parent->isPointerRel()) {
// Don't associate a relative pointer with the resolution, but convert to a standard pointer
parent = glb->types->getTypePointer(parent->getSize(), baseType, ((TypePointer *)parent)->getWordSize());
}
ResolvedUnion resolve(parent,fieldNum,*glb->types);
setUnionField(parent, op, slot, resolve);
}
/// \brief Copy a read/write facing resolution for a specific data-type from one PcodeOp to another
///
/// \param parent is the data-type that needs resolution
/// \param op is the new reading PcodeOp
/// \param slot is the new slot (-1 for write, >=0 for read)
/// \param oldOp is the PcodeOp to inherit the resolution from
/// \param oldSlot is the old slot (-1 for write, >=0 for read)
int4 Funcdata::inheritResolution(Datatype *parent,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot)
{
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
ResolveEdge edge(parent,oldOp,oldSlot);
iter = unionMap.find(edge);
if (iter == unionMap.end())
return -1;
setUnionField(parent,op,slot,(*iter).second);
return (*iter).second.getFieldNum();
}
#ifdef OPACTION_DEBUG
/// The current state of the op is recorded for later comparison after
/// its been modified.
/// \param op is the given PcodeOp being recorded
void Funcdata::debugModCheck(PcodeOp *op)
{
if (op->isModified()) return;
if (!debugCheckRange(op)) return;
op->setAdditionalFlag(PcodeOp::modified);
ostringstream before;
op->printDebug(before);
modify_list.push_back(op);
modify_before.push_back( before.str() );
}
void Funcdata::debugModClear(void)
{
for(int4 i=0;i<modify_list.size();++i)
modify_list[i]->clearAdditionalFlag(PcodeOp::modified);
modify_list.clear();
modify_before.clear();
opactdbg_active = false;
}
/// \param actionname is the name of the Action being debugged
void Funcdata::debugModPrint(const string &actionname)
{
if (!opactdbg_active) return;
opactdbg_active = false;
if (modify_list.empty()) return;
PcodeOp *op;
ostringstream s;
opactdbg_breakon |= (opactdbg_count == opactdbg_breakcount);
s << "DEBUG " << dec << opactdbg_count++ << ": " << actionname << endl;
for(int4 i=0;i<modify_list.size();++i) {
op = modify_list[i];
s << modify_before[i] << endl;
s << " ";
op->printDebug(s);
s << endl;
op->clearAdditionalFlag(PcodeOp::modified);
}
modify_list.clear();
modify_before.clear();
glb->printDebug(s.str());
}
/// \param pclow is the beginning of the memory range to trace
/// \param pchigh is the end of the range
/// \param uqlow is an (optional) sequence number to associate with the beginning of the range
/// \param uqhigh is an (optional) sequence number to associate with the end of the range
void Funcdata::debugSetRange(const Address &pclow,const Address &pchigh,
uintm uqlow,uintm uqhigh)
{
opactdbg_on = true;
opactdbg_pclow.push_back(pclow);
opactdbg_pchigh.push_back(pchigh);
opactdbg_uqlow.push_back(uqlow);
opactdbg_uqhigh.push_back(uqhigh);
}
/// \param op is the given PcodeOp to check
/// \return \b true if the op is being traced
bool Funcdata::debugCheckRange(PcodeOp *op)
{
int4 i,size;
size = opactdbg_pclow.size();
for(i=0;i<size;++i) {
if (!opactdbg_pclow[i].isInvalid()) {
if (op->getAddr() < opactdbg_pclow[i])
continue;
if (opactdbg_pchigh[i] < op->getAddr())
continue;
}
if (opactdbg_uqlow[i] != ~((uintm)0)) {
if (opactdbg_uqlow[i] > op->getTime())
continue;
if (opactdbg_uqhigh[i] < op->getTime())
continue;
}
return true;
}
return false;
}
void Funcdata::debugPrintRange(int4 i) const
{
ostringstream s;
if (!opactdbg_pclow[i].isInvalid()) {
s << "PC = (";
opactdbg_pclow[i].printRaw(s);
s << ',';
opactdbg_pchigh[i].printRaw(s);
s << ") ";
}
else
s << "entire function ";
if (opactdbg_uqlow[i] != ~((uintm)0)) {
s << "unique = (" << hex << opactdbg_uqlow[i] << ',';
s << opactdbg_uqhigh[i] << ')';
}
glb->printDebug(s.str());
}
#endif
} // End namespace ghidra