Merge remote-tracking branch 'origin/GP-5816_ReturnValueRecovery' into patch

This commit is contained in:
Ryan Kurtz 2025-07-10 05:35:35 -04:00
commit 3ad921d5c7
8 changed files with 135 additions and 57 deletions

View file

@ -457,7 +457,7 @@ RECURSIVE = NO
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slghparse.cc slghparse.hh slghscan.cc slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc codedata.hh codedata.cc semantics.hh semantics.cc grammar.hh grammar.cc callgraph.hh callgraph.cc filemanage.hh filemanage.cc graph.hh graph.cc loadimage_bfd.hh loadimage_bfd.cc pcodecompile.cc pcodecompile.hh pcodeparse.hh pcodeparse.cc inject_sleigh.hh inject_sleigh.cc context.hh context.cc consolemain.cc sleighexample.cc xml.cc double.hh double.cc paramid.hh paramid.cc prefersplit.hh prefersplit.cc
EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slghparse.cc slghparse.hh slghscan.cc slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc codedata.hh codedata.cc semantics.hh semantics.cc grammar.hh grammar.cc callgraph.hh callgraph.cc filemanage.hh filemanage.cc graph.hh graph.cc loadimage_bfd.hh loadimage_bfd.cc pcodecompile.cc pcodecompile.hh pcodeparse.hh pcodeparse.cc context.hh context.cc consolemain.cc sleighexample.cc xml.cc double.hh double.cc paramid.hh paramid.cc prefersplit.hh prefersplit.cc
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded
@ -589,12 +589,6 @@ VERBATIM_HEADERS = NO
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
@ -744,7 +738,7 @@ COMPACT_LATEX = NO
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.

View file

@ -33,6 +33,10 @@ InjectPayloadSleigh::~InjectPayloadSleigh(void)
delete tpl;
}
/// Create an empty payload in preparation for parsing the injection from a stream
/// \param src is a name or other description of the document to be parsed
/// \param nm is the name of the injection
/// \param tp is the type of injection
InjectPayloadSleigh::InjectPayloadSleigh(const string &src,const string &nm,int4 tp)
: InjectPayload(nm,tp)
{
@ -63,8 +67,8 @@ void InjectPayloadSleigh::inject(InjectContext &context,PcodeEmit &emit) const
con.cacher.emit(con.baseaddr,&emit);
}
/// The content is read as raw p-code source
/// \param decoder is the XML stream decoder
/// The content is read as raw p-code source.
/// \param decoder is the stream decoder
void InjectPayloadSleigh::decodeBody(Decoder &decoder)
{
@ -95,11 +99,18 @@ void InjectPayloadSleigh::printTemplate(ostream &s) const
tpl->encode(encoder,-1);
}
/// \brief Verify that storage locations passed in -con- match the restrictions set for a given payload
///
/// If the parsed injection does not match the restrictions, an exception is thrown.
/// \param con is the SLEIGH context established after parsing the injection
/// \param inputlist is the list of input parameters specified for the given payload
/// \param output is the list of output parameters specified for the given payload
/// \param source is a description or name for the payload document
void InjectPayloadSleigh::checkParameterRestrictions(InjectContextSleigh &con,
const vector<InjectParameter> &inputlist,
const vector<InjectParameter> &output,
const string &source)
{ // Verify that the storage locations passed in -con- match the restrictions set for this payload
{
if (inputlist.size() != con.inputlist.size())
throw LowlevelError("Injection parameter list has different number of parameters than p-code operation: "+source);
for(int4 i=0;i<inputlist.size();++i) {
@ -116,11 +127,18 @@ void InjectPayloadSleigh::checkParameterRestrictions(InjectContextSleigh &con,
}
}
/// \brief Set-up operands in the parser state so that they pick up storage locations in InjectContext
///
/// \param con is context for the parser
/// \param walker is the parser state
/// \param inputlist is the input varnodes as described by the payload
/// \param output is the output varnodes as described by the payload
/// \param source is a description or name of the payload document
void InjectPayloadSleigh::setupParameters(InjectContextSleigh &con,ParserWalkerChange &walker,
const vector<InjectParameter> &inputlist,
const vector<InjectParameter> &output,
const string &source)
{ // Set-up operands in the parser state so that they pick up storage locations in InjectContext
{
checkParameterRestrictions(con,inputlist,output,source);
ParserContext *pos = walker.getParserContext();
for(int4 i=0;i<inputlist.size();++i) {
@ -195,6 +213,11 @@ void InjectPayloadCallother::decode(Decoder &decoder)
decoder.closeElement(elemId);
}
/// \brief Constructor for use with decode
///
/// \param g is the Architecture owning the script
/// \param src is a description or name of the payload document
/// \param nm is the name of the script
ExecutablePcodeSleigh::ExecutablePcodeSleigh(Architecture *g,const string &src,const string &nm)
: ExecutablePcode(g,src,nm)
{
@ -252,6 +275,25 @@ void ExecutablePcodeSleigh::printTemplate(ostream &s) const
tpl->encode(encoder,-1);
}
/// \brief Constructor for use with decode
///
/// \param g is the Architecture
/// \param base is original InjectPayload object whose dynamic payloads are being cached
InjectPayloadDynamic::InjectPayloadDynamic(Architecture *g,InjectPayload *base)
: InjectPayload(base->getName(),base->getType())
{
glb = g;
dynamic = true;
// Clone basic properties of the original payload
incidentalCopy = base->isIncidentalCopy();
paramshift = base->getParamShift();
for(int4 i=0;i<base->sizeInput();++i)
inputlist.push_back(base->getInput(i));
for(int4 i=0;i<base->sizeOutput();++i)
output.push_back(base->getOutput(i));
}
InjectPayloadDynamic::~InjectPayloadDynamic(void)
{
@ -260,6 +302,10 @@ InjectPayloadDynamic::~InjectPayloadDynamic(void)
delete (*iter).second;
}
/// \brief Decode a specific p-code sequence and the context in which it applied
///
/// Decode the Address for a specific context and then elements for the specific p-code ops.
/// \param decoder is the stream to pull from
void InjectPayloadDynamic::decodeEntry(Decoder &decoder)
{
@ -301,14 +347,6 @@ PcodeInjectLibrarySleigh::PcodeInjectLibrarySleigh(Architecture *g)
contextCache.glb = g;
}
int4 PcodeInjectLibrarySleigh::registerDynamicInject(InjectPayload *payload)
{
int4 id = injection.size();
injection.push_back(payload);
return id;
}
/// \brief Force a payload to be dynamic for debug purposes
///
/// Debug information may include inject information for payloads that aren't dynamic.
@ -320,12 +358,18 @@ InjectPayloadDynamic *PcodeInjectLibrarySleigh::forceDebugDynamic(int4 injectid)
{
InjectPayload *oldPayload = injection[injectid];
InjectPayloadDynamic *newPayload = new InjectPayloadDynamic(glb,oldPayload->getName(),oldPayload->getType());
InjectPayloadDynamic *newPayload = new InjectPayloadDynamic(glb,oldPayload);
delete oldPayload;
injection[injectid] = newPayload;
return newPayload;
}
/// \brief Convert SLEIGH syntax to p-code templates for the given InjectPayload
///
/// The payload \b parsestring must be populated with SLEIGH synatax.
/// The SLEIGH translator is used to parse the syntax and produce the
/// p-code templates that are then ready to be injected via InjectPayload::inject.
/// \param payload is the given InjectPayload
void PcodeInjectLibrarySleigh::parseInject(InjectPayload *payload)
{
@ -391,7 +435,7 @@ void PcodeInjectLibrarySleigh::registerInject(int4 injectid)
{
InjectPayload *payload = injection[injectid];
if (payload->isDynamic()) {
InjectPayload *sub = new InjectPayloadDynamic(glb,payload->getName(),payload->getType());
InjectPayload *sub = new InjectPayloadDynamic(glb,payload);
delete payload;
payload = sub;
injection[injectid] = payload;

View file

@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// \file inject_sleigh.hh
/// \brief Implementation of p-code injection using the internal SLEIGH engine to build the p-code ops
#ifndef __INJECT_SLEIGH_HH__
#define __INJECT_SLEIGH_HH__
@ -21,24 +24,30 @@
namespace ghidra {
/// \brief Context for performing injection using the SLEIGH engine
class InjectContextSleigh : public InjectContext {
public:
PcodeCacher cacher;
ParserContext *pos;
InjectContextSleigh(void) { pos = (ParserContext *)0; }
PcodeCacher cacher; ///< Cache of p-code data accumulated just prior to injection
ParserContext *pos; ///< Context for final parsing and emitting of p-code during injection
InjectContextSleigh(void) { pos = (ParserContext *)0; } ///< Constructor
virtual ~InjectContextSleigh(void);
virtual void encode(Encoder &encoder) const {} // We don't need this functionality for sleigh
virtual void encode(Encoder &encoder) const {} ///< We don't need this functionality for sleigh
};
/// \brief An injection payload built by the SLEIGH engine
///
/// The p-code ops for the injection are described using SLEIGH syntax.
/// This object can hold both the SLEIGH syntax as a string or the p-code templates
/// (VarnodeTpl and OpTpl) that are prepared for emitting the p-code for the injection.
class InjectPayloadSleigh : public InjectPayload {
friend class PcodeInjectLibrarySleigh;
ConstructTpl *tpl;
string parsestring;
string source;
ConstructTpl *tpl; ///< The VarnodeTpl and OpTpl objects prepared for injection
string parsestring; ///< SLEIGH syntax describing the injection p-code
string source; ///< A description of the document containing the SLEIGH syntax
protected:
void decodeBody(Decoder &decoder); ///< Parse the <body> tag
void decodeBody(Decoder &decoder); ///< Parse the \<body> tag
public:
InjectPayloadSleigh(const string &src,const string &nm,int4 tp);
InjectPayloadSleigh(const string &src,const string &nm,int4 tp); ///< Constructor for use with decode
virtual ~InjectPayloadSleigh(void);
virtual void inject(InjectContext &context,PcodeEmit &emit) const;
virtual void decode(Decoder &decoder);
@ -52,24 +61,27 @@ public:
const string &source);
};
/// \brief An injection payload, described by SLEIGH, for replacing CALL ops to specific functions
class InjectPayloadCallfixup : public InjectPayloadSleigh {
vector<string> targetSymbolNames;
vector<string> targetSymbolNames; ///< Names (symbols) of specific functions to replace with \b this payload
public:
InjectPayloadCallfixup(const string &sourceName);
InjectPayloadCallfixup(const string &sourceName); ///< Constructor
virtual void decode(Decoder &decoder);
};
/// \brief An injection payload, described by SLEIGH, for replacing specific user (CALLOTHER) ops
class InjectPayloadCallother : public InjectPayloadSleigh {
public:
InjectPayloadCallother(const string &sourceName);
InjectPayloadCallother(const string &sourceName); ///< Constructor
virtual void decode(Decoder &decoder);
};
/// \brief A p-code snippet, described by SLEIGH, that can be executed as a script
class ExecutablePcodeSleigh : public ExecutablePcode {
friend class PcodeInjectLibrarySleigh;
protected:
string parsestring;
ConstructTpl *tpl;
string parsestring; ///< SLEIGH syntax describing the p-code snippet
ConstructTpl *tpl; ///< Parsed template objects (VarnodeTpl and OpTpl) ready for injection
public:
ExecutablePcodeSleigh(Architecture *g,const string &src,const string &nm);
virtual ~ExecutablePcodeSleigh(void);
@ -78,11 +90,16 @@ protected:
virtual void printTemplate(ostream &s) const;
};
/// \brief A debugging placeholder for a payload that changes depending on context
///
/// Implemented as a simple map from an Address to an XML description of the p-code sequence to inject.
/// This is used internally by PcodeInjectLibrarySleigh in a debug environment to hold multiple payloads
/// for objects where InjectPayload::isDynamic() returns \b true.
class InjectPayloadDynamic : public InjectPayload {
Architecture *glb;
map<Address,Document *> addrMap; // Map from address to specific inject
Architecture *glb; ///< The architecture owning \b this payload
map<Address,Document *> addrMap; ///< Map from address to specific inject
public:
InjectPayloadDynamic(Architecture *g,const string &nm,int4 tp) : InjectPayload(nm,tp) { glb = g; dynamic = true; }
InjectPayloadDynamic(Architecture *g,InjectPayload *base);
virtual ~InjectPayloadDynamic(void);
void decodeEntry(Decoder &decoder);
virtual void inject(InjectContext &context,PcodeEmit &emit) const;
@ -91,18 +108,21 @@ public:
virtual string getSource(void) const { return "dynamic"; }
};
/// \brief An implementation of an injection library using the internal SLEIGH engine to build payloads
///
/// Payloads from compiler specs and other sources are parsed as SLEIGH syntax and stored
/// internally as InjectPayloadSleigh objects.
class PcodeInjectLibrarySleigh : public PcodeInjectLibrary {
const SleighBase *slgh;
vector<OpBehavior *> inst;
InjectContextSleigh contextCache;
int4 registerDynamicInject(InjectPayload *payload);
const SleighBase *slgh; ///< The SLEIGH engine for parsing payloads
vector<OpBehavior *> inst; ///< P-code behaviors used for p-code scripts
InjectContextSleigh contextCache; ///< Reusable context for emitting p-code payloads
InjectPayloadDynamic *forceDebugDynamic(int4 injectid);
void parseInject(InjectPayload *payload);
protected:
virtual int4 allocateInject(const string &sourceName,const string &name,int4 type);
virtual void registerInject(int4 injectid);
public:
PcodeInjectLibrarySleigh(Architecture *g);
PcodeInjectLibrarySleigh(Architecture *g); ///< Constructor
virtual void decodeDebug(Decoder &decoder);
virtual int4 manualCallFixup(const string &name,const string &snippetstring);
virtual int4 manualCallOtherFixup(const string &name,const string &outname,const vector<string> &inname,

View file

@ -9602,12 +9602,12 @@ bool RuleIgnoreNan::isAnotherNan(Varnode *vn)
///
/// The given PcodeOp takes input from a NaN operation through a specific slot. We look for a floating-point comparison
/// PcodeOp (FLOAT_LESS, FLOAT_LESSEQUAL, FLOAT_EQUAL, or FLOAT_NOTEQUAL) that is combined with the given PcodeOp and
/// has the same input Varnode as the NaN. The data-flow must be combined either through a BOOL_OR or BOOL_AND
/// operation, or the given PcodeOp must be a CBRANCH that protects immediate control-flow to another CBRANCH
/// taking the result of the comparison as input. If a matching comparison is found, the NaN input to the given
/// PcodeOp is removed, assuming the output of the NaN operation is always \b false.
/// Input from an unmodified NaN result must be combined through a BOOL_OR, but a NaN result that has been negated
/// must combine through a BOOL_AND.
/// has the same input Varnode as the NaN. The data-flow must be combined either through a BOOL_OR, BOOL_AND,
/// INT_EQUAL, or INT_NOTEQUAL operation, or the given PcodeOp must be a CBRANCH that protects immediate control-flow
/// to another CBRANCH taking the result of the comparison as input. If a matching comparison is found, the NaN input
/// to the given PcodeOp is removed, assuming the output of the NaN operation is always \b false.
/// If a NaN result is combined through a BOOL_OR, it must be unmodified, but a NaN result combined
/// through a BOOL_AND must be negated.
/// \param floatVar is the input Varnode to NaN operation
/// \param op is the given PcodeOp to test
/// \param slot is the input index of the NaN operation
@ -9618,7 +9618,8 @@ bool RuleIgnoreNan::isAnotherNan(Varnode *vn)
Varnode *RuleIgnoreNan::testForComparison(Varnode *floatVar,PcodeOp *op,int4 slot,OpCode matchCode,int4 &count,Funcdata &data)
{
if (op->code() == matchCode) {
OpCode opc = op->code();
if (opc == matchCode) {
Varnode *vn = op->getIn(1 - slot);
if (checkBackForCompare(floatVar,vn)) {
data.opSetOpcode(op,CPUI_COPY);
@ -9630,7 +9631,14 @@ Varnode *RuleIgnoreNan::testForComparison(Varnode *floatVar,PcodeOp *op,int4 slo
return op->getOut();
}
}
else if (op->code() == CPUI_CBRANCH) {
else if (opc == CPUI_INT_EQUAL || opc == CPUI_INT_NOTEQUAL) {
Varnode *vn = op->getIn(1 - slot);
if (checkBackForCompare(floatVar,vn)) {
data.opSetInput(op,data.newConstant(1,(matchCode == CPUI_BOOL_OR) ? 0 : 1),slot);
count += 1;
}
}
else if (opc == CPUI_CBRANCH) {
BlockBasic *parent = op->getParent();
PcodeOp *lastOp;
int4 outDir = (matchCode == CPUI_BOOL_OR) ? 0 : 1;

View file

@ -2004,6 +2004,12 @@ bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags)
if (vn->loneDescend() == (PcodeOp*)0) return false;
const PcodeOp *op = vn->getDef();
if (op == (PcodeOp*)0) return true;
while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY
vn = op->getIn(0);
if (vn->loneDescend() == (PcodeOp *)0) return false;
op = vn->getDef();
if (op == (PcodeOp *)0) return true;
}
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
}

View file

@ -1,5 +1,5 @@
<decompilertest>
<binaryimage arch="x86:LE:32:default:gcc">
<binaryimage arch="x86:LE:32:default:win">
<!--
Example function returning a structure stored across multiple registers
-->

View file

@ -213,6 +213,9 @@
<register name="d15"/>
</unaffected>
<killedbycall>
<register name="x0"/>
<register name="x1"/>
<register name="q0"/>
<!-- x8: indirect result location register, which is not
reflected in the pentry list -->
<register name="x8"/>

View file

@ -241,6 +241,9 @@
<register name="d15"/>
</unaffected>
<killedbycall>
<register name="x0"/>
<register name="x1"/>
<register name="q0"/>
<!-- x8: indirect result location register, which is not
reflected in the pentry list -->
<register name="x8"/>