GP-6985 Send back consistent function symbol or hole

This commit is contained in:
caheckman
2026-06-23 16:13:54 +00:00
parent ef70780544
commit 05c8a9c312
14 changed files with 156 additions and 144 deletions
@@ -4,9 +4,9 @@
* 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.
@@ -43,7 +43,7 @@ void Action::issueWarning(Architecture *glb)
{
if ((flags&(rule_warnings_on|rule_warnings_given)) == rule_warnings_on) {
flags |= rule_warnings_given;
glb->printMessage("WARNING: Applied action "+name);
glb->printWarning("Applied action "+name);
}
}
@@ -640,7 +640,7 @@ void Rule::issueWarning(Architecture *glb)
{
if ((flags&(warnings_on|warnings_given)) == warnings_on) {
flags |= warnings_given;
glb->printMessage("WARNING: Applied rule "+name);
glb->printWarning("Applied rule "+name);
}
}
@@ -862,11 +862,11 @@ int4 ActionPool::processOp(PcodeOp *op,Funcdata &data)
rule_index = 0;
}
}
#ifdef CPUI_DEBUG
else if (opc != op->code()) {
data.getArch()->printMessage("ERROR: Rule " + rl->getName() + " changed op without returning result of 1!");
opc = op->code();
rule_index = 0;
throw LowlevelError("ERROR: Rule " + rl->getName() + " changed op without returning result of 1!");
}
#endif
}
op_state++;
rule_index = 0;
@@ -244,11 +244,11 @@ public:
/// \return the description
virtual string getDescription(void) const { return archid; }
/// \brief Print an error message to console
/// \brief Print a warning message to console
///
/// Write the given message to whatever the registered error stream is
/// Write the given message to a registered stream.
/// \param message is the error message
virtual void printMessage(const string &message) const=0;
virtual void printWarning(const string &message) const=0;
virtual void encode(Encoder &encoder) const; ///< Encode \b this architecture to a stream
virtual void restoreXml(DocumentStorage &store); ///< Restore the Architecture state from XML documents
virtual void nameFunction(const Address &addr,string &name) const; ///< Pick a default name for a function
@@ -2318,7 +2318,7 @@ int4 ActionReturnSplit::apply(Funcdata &data)
count += 1;
#ifdef BLOCKCONSISTENT_DEBUG
if (!data.getBasicBlocks().isConsistent())
data.getArch()->printMessage("Block structure is not consistent");
data.getArch()->printWarning("Block structure is not consistent");
#endif
}
return 0;
@@ -1598,7 +1598,7 @@ Symbol *Scope::addMapSym(Decoder &decoder)
SymbolEntry entry(sym);
entry.decode(decoder);
if (entry.isInvalid()) {
glb->printMessage("WARNING: Throwing out symbol with invalid mapping: "+sym->getName());
glb->printWarning("Throwing out symbol with invalid mapping: "+sym->getName());
removeSymbol(sym);
decoder.closeElement(elemId);
return (Symbol *)0;
@@ -1623,9 +1623,9 @@ FunctionSymbol *Scope::addFunction(const Address &addr,const string &nm)
SymbolEntry *overlap = queryContainer(addr,1,Address());
if (overlap != (SymbolEntry *)0) {
string errmsg = "WARNING: Function "+name;
string errmsg = "Function "+nm;
errmsg += " overlaps object: "+overlap->getSymbol()->getName();
glb->printMessage(errmsg);
glb->printWarning(errmsg);
}
sym = new FunctionSymbol(owner,nm,glb->min_funcsymbol_size);
addSymbolInternal(sym);
@@ -1672,9 +1672,12 @@ LabSymbol *Scope::addCodeLabel(const Address &addr,const string &nm)
SymbolEntry *overlap = queryContainer(addr,1,addr);
if (overlap != (SymbolEntry *)0) {
string errmsg = "WARNING: Codelabel "+nm;
errmsg += " overlaps object: "+overlap->getSymbol()->getName();
glb->printMessage(errmsg);
FunctionSymbol *funcsym = dynamic_cast<FunctionSymbol *>(overlap->getSymbol());
if (funcsym == (FunctionSymbol *)0) { // Overlapping with something that isn't a function body
string errmsg = "Codelabel "+nm;
errmsg += " overlaps object: "+overlap->getSymbol()->getName();
glb->printWarning(errmsg);
}
}
sym = new LabSymbol(owner,nm);
addSymbolInternal(sym);
@@ -895,10 +895,13 @@ bool ArchitectureGhidra::getCPoolRef(const vector<uintb> &refs,Decoder &decoder)
return readAll(sin,decoder);
}
void ArchitectureGhidra::printMessage(const string &message) const
void ArchitectureGhidra::printWarning(const string &message) const
{
warnings += '\n'+message;
sout.write("\000\000\001\022",4);
sout << "Decompiler: " << message;
sout.write("\000\000\001\023",4);
sout.flush();
}
/// \brief Construct given specification files and i/o streams
@@ -82,7 +82,6 @@ struct JavaError : public LowlevelError {
class ArchitectureGhidra : public Architecture {
istream &sin; ///< Input stream for interfacing with Ghidra
ostream &sout; ///< Output stream for interfacing with Ghidra
mutable string warnings; ///< Warnings accumulated by the decompiler
string pspecxml; ///< XML pspec passed from Ghidra
string cspecxml; ///< XML cspec passed from Ghidra
string tspecxml; ///< Stripped down .sla file passed from Ghidra
@@ -108,8 +107,6 @@ class ArchitectureGhidra : public Architecture {
public:
ArchitectureGhidra(const string &pspec,const string &cspec,const string &tspec,const string &corespec,
istream &i,ostream &o);
const string &getWarnings(void) const { return warnings; } ///< Get warnings produced by the last decompilation
void clearWarnings(void) { warnings.clear(); } ///< Clear warnings
bool getRegister(const string &regname,Decoder &decoder); ///< Retrieve a register description given a name
string getRegisterName(const VarnodeData &vndata); ///< Retrieve a register name given its storage location
bool getTrackedRegisters(const Address &addr,Decoder &decoder); ///< Retrieve \e tracked register values at the given address
@@ -153,7 +150,7 @@ public:
bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info
void getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc);
virtual void printMessage(const string &message) const;
virtual void printWarning(const string &message) const;
static int4 readToAnyBurst(istream &s); ///< Read the next message protocol marker
static bool readBoolStream(istream &s); ///< Read a boolean value from the client
@@ -99,18 +99,17 @@ void GhidraCommand::loadParameters(void)
if (ghidra == (ArchitectureGhidra *)0)
throw JavaError("decompiler","No architecture registered with decompiler");
ghidra->clearWarnings();
}
/// This method sends any warnings accumulated during execution back, but it can be overloaded
/// to send back any kind of information. Individual records are sent using
/// the message protocol.
void GhidraCommand::sendResult(void)
/// This method can be overloaded to send back any kind of information as a string. Individual records
/// are sent using the message protocol. If the command failed, a string describing the error is passed in.
/// \param errorMsg is, if not empty, the error causing the commmand to fail
void GhidraCommand::sendResult(const string &errorMsg)
{
if (ghidra != (ArchitectureGhidra *)0) {
sout.write("\000\000\001\020",4);
sout << ghidra->getWarnings();
sout << errorMsg;
sout.write("\000\000\001\021",4);
}
}
@@ -127,6 +126,7 @@ int4 GhidraCommand::doit(void)
{
status = 0;
sout.write("\000\000\001\006",4); // Command response header
string errorMsg;
try {
loadParameters();
int4 type = ArchitectureGhidra::readToAnyBurst(sin);
@@ -135,25 +135,19 @@ int4 GhidraCommand::doit(void)
rawAction();
}
catch(DecoderError &err) {
string errmsg;
errmsg = "Marshaling error: " + err.explain;
ghidra->printMessage( errmsg );
errorMsg = "Marshaling error: " + err.explain;
}
catch(JavaError &err) {
ArchitectureGhidra::passJavaException(sout,err.type,err.explain);
return status; // Abort sending any results
}
catch(RecovError &err) {
string errmsg;
errmsg = "Recoverable Error: " + err.explain;
ghidra->printMessage( errmsg );
errorMsg = "Recoverable Error: " + err.explain;
}
catch(LowlevelError &err) {
string errmsg;
errmsg = "Low-level Error: " + err.explain;
ghidra->printMessage( errmsg );
errorMsg = "Low-level Error: " + err.explain;
}
sendResult();
sendResult(errorMsg);
sout.write("\000\000\001\007",4); // Command response closer
sout.flush();
return status;
@@ -200,13 +194,13 @@ void RegisterProgram::rawAction(void)
archid = open;
}
void RegisterProgram::sendResult(void)
void RegisterProgram::sendResult(const string &errorMsg)
{
sout.write("\000\000\001\016",4);
sout << dec << archid;
sout.write("\000\000\001\017",4);
GhidraCommand::sendResult();
GhidraCommand::sendResult(errorMsg);
}
void DeregisterProgram::loadParameters(void)
@@ -225,7 +219,6 @@ void DeregisterProgram::loadParameters(void)
if (ghidra == (ArchitectureGhidra *)0)
throw JavaError("decompiler","No architecture registered with decompiler");
ghidra->clearWarnings();
}
void DeregisterProgram::rawAction(void)
@@ -250,13 +243,13 @@ void DeregisterProgram::rawAction(void)
res = 0;
}
void DeregisterProgram::sendResult(void)
void DeregisterProgram::sendResult(const string &errorMsg)
{
sout.write("\000\000\001\016",4);
sout << dec << res;
sout.write("\000\000\001\017",4);
GhidraCommand::sendResult();
GhidraCommand::sendResult(errorMsg);
}
void FlushNative::rawAction(void)
@@ -272,13 +265,13 @@ void FlushNative::rawAction(void)
res = 0;
}
void FlushNative::sendResult(void)
void FlushNative::sendResult(const string &errorMsg)
{
sout.write("\000\000\001\016",4);
sout << dec << res;
sout.write("\000\000\001\017",4);
GhidraCommand::sendResult();
GhidraCommand::sendResult(errorMsg);
}
void DecompileAt::loadParameters(void)
@@ -405,14 +398,14 @@ void SetAction::rawAction(void)
res = true;
}
void SetAction::sendResult(void)
void SetAction::sendResult(const string &errorMsg)
{
if (res)
ArchitectureGhidra::writeStringStream(sout,"t");
else
ArchitectureGhidra::writeStringStream(sout,"f");
GhidraCommand::sendResult();
GhidraCommand::sendResult(errorMsg);
}
void SetOptions::loadParameters(void)
@@ -444,14 +437,14 @@ void SetOptions::rawAction(void)
res = true;
}
void SetOptions::sendResult(void)
void SetOptions::sendResult(const string &errorMsg)
{
if (res)
ArchitectureGhidra::writeStringStream(sout,"t");
else
ArchitectureGhidra::writeStringStream(sout,"f");
GhidraCommand::sendResult();
GhidraCommand::sendResult(errorMsg);
}
/// A command is read from the Ghidra client. The matching GhidraCommand object is
@@ -4,9 +4,9 @@
* 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.
@@ -82,7 +82,7 @@ protected:
ArchitectureGhidra *ghidra; ///< The Architecture on which to perform the command
int4 status; ///< Meta-command to system (0=wait for next command, 1=terminate process)
virtual void loadParameters(void); ///< Read parameters directing command execution
virtual void sendResult(void); ///< Send results of the command (if any) back to the Ghidra client
virtual void sendResult(const string &errorMsg); ///< Send back response to the command
public:
GhidraCommand(void) : sin(cin),sout(cout) {
ghidra = (ArchitectureGhidra *)0;
@@ -112,7 +112,7 @@ class RegisterProgram : public GhidraCommand {
string tspec; ///< Configuration (address-spaces) for the Translate object
string corespec; ///< A description of core data-types for the TypeFactory object
virtual void loadParameters(void);
virtual void sendResult(void);
virtual void sendResult(const string &errorMsg);
public:
int4 archid; ///< Resulting id of the program to send back
virtual void rawAction(void);
@@ -126,7 +126,7 @@ public:
class DeregisterProgram : public GhidraCommand {
int4 inid; ///< The id of the Architecture being terminated
virtual void loadParameters(void);
virtual void sendResult(void);
virtual void sendResult(const string &errorMsg);
public:
int4 res; ///< The meta-command being issued to send back
virtual void rawAction(void);
@@ -140,7 +140,7 @@ public:
/// (re)fetch any symbols as needed.
/// The command expects a single string parameter encoding the id of the program to flush.
class FlushNative : public GhidraCommand {
virtual void sendResult(void);
virtual void sendResult(const string &errorMsg);
public:
int4 res; ///< Success status returned to the client (0=success)
virtual void rawAction(void);
@@ -211,7 +211,7 @@ class SetAction : public GhidraCommand {
string actionstring; ///< The \e root Action to switch to
string printstring; ///< The \e printing output configuration to toggle
virtual void loadParameters(void);
virtual void sendResult(void);
virtual void sendResult(const string &errorMsg);
public:
bool res; ///< Set to \b true if the configuration action was successful
virtual void rawAction(void);
@@ -229,7 +229,7 @@ public:
class SetOptions : public GhidraCommand {
Decoder *decoder; ///< The \<optionslist> decoder
virtual void loadParameters(void);
virtual void sendResult(void);
virtual void sendResult(const string &errorMsg);
public:
bool res; ///< Set to \b true if the option change succeeded
SetOptions(void) { decoder = (Decoder *)0; res = false; } ///< Constructor
@@ -342,7 +342,7 @@ void SleighArchitecture::resolveArchitecture(void)
if (description[i].getId() == baseid) {
languageindex = i;
if (description[i].isDeprecated())
printMessage("WARNING: Language "+baseid+" is deprecated");
printWarning("Language "+baseid+" is deprecated");
break;
}
}
@@ -476,6 +476,12 @@ void SleighArchitecture::restoreXmlHeader(const Element *el)
target = el->getAttributeValue("target");
}
void SleighArchitecture::printWarning(const string &message) const
{
*errorstream << "WARNING: " << message << endl;
}
/// Given an architecture target string try to recover an
/// appropriate processor name for use in a normalized \e language \e id.
/// \param nm is the given target string
@@ -135,7 +135,7 @@ public:
const string &getTarget(void) const { return target; } ///< Get the \e language \e id of the active processor
void encodeHeader(Encoder &encoder) const; ///< Encode basic attributes of the active executable
void restoreXmlHeader(const Element *el); ///< Restore from basic attributes of an executable
virtual void printMessage(const string &message) const { *errorstream << message << endl; }
virtual void printWarning(const string &message) const;
virtual ~SleighArchitecture(void);
virtual string getDescription(void) const;
File diff suppressed because it is too large Load Diff
@@ -50,6 +50,8 @@ public class DecompileCallback {
public final static int MAX_SYMBOL_COUNT = 16;
public final static int MAX_FUNCTION_OFFCUT = 8; // Maximum distance from function entry point where a query still returns the HighFunctionSymbol
/**
* Data returned for a query about strings
*/
@@ -79,7 +81,7 @@ public class DecompileCallback {
private AddressFactory addrfactory;
private ConstantPool cpool;
private PcodeDataTypeManager dtmanage;
private String nativeMessage;
private String errorMessage;
private InstructionBlock lastPseudoInstructionBlock;
private Disassembler pseudoDisassembler;
@@ -94,7 +96,7 @@ public class DecompileCallback {
dtmanage = dt;
default_extrapop = pcodecompilerspec.getDefaultCallingConvention().getExtrapop();
cpool = null;
nativeMessage = null;
errorMessage = "";
debug = null;
}
@@ -116,7 +118,7 @@ public class DecompileCallback {
if (debug != null) {
debug.setPcodeDataTypeManager(dtmanage);
}
nativeMessage = null; // Clear last message
errorMessage = ""; // Clear last message
lastPseudoInstructionBlock = null;
if (pseudoDisassembler != null) {
pseudoDisassembler.resetDisassemblerContext();
@@ -124,19 +126,19 @@ public class DecompileCallback {
}
/**
* @return the last message from the decompiler
* @return the last error message from the decompiler, or an empty string
*/
public String getNativeMessage() {
return nativeMessage;
public String getErrorMessage() {
return errorMessage;
}
/**
* Cache a message returned by the decompiler process
* Cache an error message returned by the decompiler process
*
* @param msg is the message
*/
void setNativeMessage(String msg) {
nativeMessage = msg;
void setErrorMessage(String msg) {
errorMessage = msg;
}
/**
@@ -893,9 +895,14 @@ public class DecompileCallback {
private void encodeFunction(Encoder encoder, Function func, Address addr,
boolean includeDefaultNames) throws IOException {
Address entry = func.getEntryPoint();
if (entry.getAddressSpace().equals(addr.getAddressSpace())) {
Address endHole = addr; // If all else fails, return hole [addr,addr]
AddressRange range = func.getBody().getRangeContaining(addr);
if (range != null && range.contains(entry)) {
long diff = addr.getOffset() - entry.getOffset();
if ((diff >= 0) && (diff < 8)) {
if (diff < 0) {
endHole = entry.subtract(1); // Return hole [addr,entry)
}
else if (diff < MAX_FUNCTION_OFFCUT) {
HighFunction hfunc =
new HighFunction(func, pcodelanguage, pcodecompilerspec, dtmanage);
@@ -903,7 +910,14 @@ public class DecompileCallback {
hfunc.grabFromFunction(extrapop, includeDefaultNames,
(extrapop != default_extrapop));
HighSymbol functionSymbol = new HighFunctionSymbol(entry, (int) (diff + 1), hfunc);
long size = range.getMaxAddress().getOffset() - entry.getOffset() + 1;
if (size <= 0) {
size = 1;
}
else if (size > MAX_FUNCTION_OFFCUT) {
size = MAX_FUNCTION_OFFCUT;
}
HighSymbol functionSymbol = new HighFunctionSymbol(entry, (int) size, hfunc);
Namespace namespc = functionSymbol.getNamespace();
if (debug != null) {
debug.getFNTypes(hfunc);
@@ -912,23 +926,15 @@ public class DecompileCallback {
encodeResult(encoder, functionSymbol, namespc);
return;
}
}
AddressRangeIterator iter = func.getBody().getAddressRanges();
while (iter.hasNext()) {
AddressRange range = iter.next();
if (range.contains(addr)) {
Address first = range.getMinAddress();
Address last = range.getMaxAddress();
encodeHole(encoder, first.getAddressSpace(), first.getUnsignedOffset(),
last.getUnsignedOffset(), MutabilitySettingsDefinition.CONSTANT);
return;
else {
endHole = range.getMaxAddress();
}
}
// There is probably some sort of error, just return a block
// containing the single queried address
else if (range != null) {
endHole = range.getMaxAddress();
}
encodeHole(encoder, addr.getAddressSpace(), addr.getUnsignedOffset(),
addr.getUnsignedOffset(), MutabilitySettingsDefinition.CONSTANT);
endHole.getUnsignedOffset(), MutabilitySettingsDefinition.CONSTANT);
}
private int getExtraPopOverride(Function func, Address addr) {
@@ -304,6 +304,17 @@ public class DecompileProcess {
throw new DecompileException(type, message);
}
private void generateWarning() throws IOException {
stringDecoder.open(1 << 16, programSource);
int type = readToBuffer(stringDecoder);
if (type != 19) {
throw new IOException("GHIDRA/decompiler alignment error");
}
stringDecoder.endIngest();
Msg.warn(this, stringDecoder.toString());
stringDecoder.clear();
}
private void readResponse(ByteIngest mainResponse) throws IOException, DecompileException {
mainResponse.clear();
readToResponse();
@@ -420,21 +431,24 @@ public class DecompileProcess {
currentResponse.endIngest();
currentResponse = null; // Reset current buffer as a native message may follow
break;
case 16: // Beginning of any native message from the decompiler
if (currentResponse != null) { // Beginning of native message before end of main response
case 16: // Beginning of any error message from the decompiler
if (currentResponse != null) { // If error message comes before end of main response
currentResponse.clear(); // Don't try to parse main response
}
currentResponse = stringDecoder;
currentResponse.open(1 << 20, programSource);
break;
case 17: // End of the native message from the decompiler
case 17: // End of the error message from the decompiler
if (currentResponse == null) {
throw new IOException("Mismatched message header");
}
currentResponse.endIngest();
callback.setNativeMessage(currentResponse.toString());
callback.setErrorMessage(currentResponse.toString());
currentResponse = null;
break;
case 18: // Beginning of informational message
generateWarning();
break;
default:
throw new IOException("GHIDRA/decompiler alignment error");
@@ -4,9 +4,9 @@
* 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.
@@ -155,11 +155,7 @@ public class DecompileResults {
/**
* Return any error message associated with the
* decompilation producing these results. Generally,
* there will only be an error if the decompilation was
* aborted for some reason, but there could conceivably
* be warnings obtainable via this method, even if the
* decompilation did complete.
* decompilation producing these results.
* @return any error message associated with these results
*/
public String getErrorMessage() {