Foundation for replacing fold's dead/abstract binary encoder with a real
SSA P-Code frontend. Verified root-cause gap: serializeBinaryIR() emits only
data-discovery nodes (sections/strings/imports/exports); the control-flow
BinaryExprKind values (BasicBlock/CallSite/...) are never constructed, so the
Z3-BB encoder's encodable filter is always empty and encodeBinaryBBs never
runs — hence 0 binary Z3 findings. The P-Code pipeline supersedes that path.
Phase 0 — docs/internal/ghidra-pcode-integration-research.md: P-Code opcode
core, the decompiler C++ embedding API (LoadImage/SleighArchitecture/Funcdata),
the CORE+DECCORE+SLEIGH source sets, no-bison finding, and pypcode as build
reference.
Phase 1 — vendored Ghidra decompiler at pinned commit (Ghidra_12.1_build,
7e89d94e34...) under third_party/ghidra-decompiler/ (cpp + AArch64 specs +
Apache-2.0 LICENSE + VENDOR.md + .gitignore for build artifacts). Validated by
hand on this clang-21/macOS toolchain:
- full SSA engine (CORE+DECCORE+SLEIGH, 80 files incl. heritage/funcdata/
action) compiles clean, -std=c++11 -O2, NO BFD, NO bison (generated
parsers are committed upstream; ruleparse isn't in the lib set);
- sleigh compiler builds; AArch64 .slaspec -> .sla compiles;
- tools/pcode_dump.cpp (custom LoadImage, no BFD) lifts the real arm64
sym._div_by_zero_direct to correct P-Code: `mov w9,#0 -> COPY(const 0)`,
`sdiv -> INT_SDIV(w8,w9)` — divisor zero-ness explicit in the data flow,
so the Phase-3 encoder proves div-by-zero trivially, multi-arch.
Not yet meson-wired (next step) and no fold-side IR/encoder yet (Phase 2-3);
this commit is the validated vendor + smoke tool.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
251 lines
6.6 KiB
C++
251 lines
6.6 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 <iostream>
|
|
#include <cstdlib>
|
|
#include "libdecomp.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
using std::cin;
|
|
using std::cout;
|
|
using std::cerr;
|
|
|
|
class IfcLoadFile : public IfaceDecompCommand {
|
|
public:
|
|
virtual void execute(istream &s);
|
|
};
|
|
|
|
class IfcAddpath : public IfaceDecompCommand {
|
|
public:
|
|
virtual void execute(istream &s);
|
|
};
|
|
|
|
class IfcSave : public IfaceDecompCommand {
|
|
public:
|
|
virtual void execute(istream &s);
|
|
};
|
|
|
|
class IfcRestore : public IfaceDecompCommand {
|
|
public:
|
|
virtual void execute(istream &s);
|
|
};
|
|
|
|
void IfcLoadFile::execute(istream &s)
|
|
|
|
{
|
|
string filename,target;
|
|
|
|
if (dcp->conf != (Architecture *)0)
|
|
throw IfaceExecutionError("Load image already present");
|
|
s >> filename;
|
|
if (!s.eof()) { // If there are two parameters
|
|
target = filename; // Second is filename, first is target
|
|
s >> filename;
|
|
}
|
|
else
|
|
target = "default";
|
|
|
|
ArchitectureCapability *capa = ArchitectureCapability::findCapability(filename);
|
|
if (capa == (ArchitectureCapability *)0)
|
|
throw IfaceExecutionError("Unable to recognize imagefile "+filename);
|
|
dcp->conf = capa->buildArchitecture(filename,target,status->optr);
|
|
// Attempt to open file and discern
|
|
// the processor architecture
|
|
DocumentStorage store; // temporary storage for xml docs
|
|
|
|
#ifdef CPUI_RULECOMPILE
|
|
if (dcp->experimental_file.size() != 0) {
|
|
*status->optr << "Trying to parse " << dcp->experimental_file << " for experimental rules" << endl;
|
|
try {
|
|
Element *root = store.openDocument(dcp->experimental_file)->getRoot();
|
|
if (root->getName() == "experimental_rules")
|
|
store.registerTag(root);
|
|
else
|
|
*status->optr << "Wrong tag type for experimental rules: "+root->getName() << endl;
|
|
}
|
|
catch(DecoderError &err) {
|
|
*status->optr << err.explain << endl;
|
|
*status->optr << "Skipping experimental rules" << endl;
|
|
}
|
|
}
|
|
#endif
|
|
string errmsg;
|
|
bool iserror = false;
|
|
try {
|
|
dcp->conf->init(store);
|
|
} catch(DecoderError &err) {
|
|
errmsg = err.explain;
|
|
iserror = true;
|
|
} catch(LowlevelError &err) {
|
|
errmsg = err.explain;
|
|
iserror = true;
|
|
}
|
|
if (iserror) {
|
|
*status->optr << errmsg << endl;
|
|
*status->optr << "Could not create architecture" << endl;
|
|
delete dcp->conf;
|
|
dcp->conf = (Architecture *)0;
|
|
return;
|
|
}
|
|
if (capa->getName() == "xml") // If file is xml
|
|
dcp->conf->readLoaderSymbols("::"); // Read in loader symbols
|
|
#ifdef OPACTION_DEBUG
|
|
dcp->conf->setDebugStream(status->fileoptr);
|
|
#endif
|
|
*status->optr << filename << " successfully loaded: " << dcp->conf->getDescription() << endl;
|
|
}
|
|
|
|
void IfcAddpath::execute(istream &s)
|
|
|
|
{
|
|
string newpath;
|
|
|
|
s >> newpath;
|
|
if (newpath.empty())
|
|
throw IfaceParseError("Missing path name");
|
|
SleighArchitecture::specpaths.addDir2Path(newpath);
|
|
}
|
|
|
|
static string savefile;
|
|
|
|
void IfcSave::execute(istream &s)
|
|
|
|
{ // Save state to filename specified on command line
|
|
ofstream fs;
|
|
|
|
s >> ws;
|
|
if (!s.eof())
|
|
s >> savefile;
|
|
|
|
if (savefile.size()==0)
|
|
throw IfaceParseError("Missing savefile name");
|
|
|
|
fs.open( savefile.c_str() );
|
|
if (!fs)
|
|
throw IfaceExecutionError("Unable to open file: "+savefile);
|
|
|
|
XmlEncode encoder(fs);
|
|
dcp->conf->encode(encoder);
|
|
fs.close();
|
|
}
|
|
|
|
void IfcRestore::execute(istream &s)
|
|
|
|
{ // Restore saved state
|
|
s >> savefile;
|
|
if (savefile.size()==0)
|
|
throw IfaceParseError("Missing file name");
|
|
|
|
DocumentStorage store;
|
|
Document *doc = store.openDocument(savefile);
|
|
store.registerTag(doc->getRoot());
|
|
dcp->clearArchitecture(); // Clear any old architecture
|
|
ArchitectureCapability *capa = ArchitectureCapability::findCapability(doc);
|
|
if (capa == (ArchitectureCapability *)0)
|
|
throw IfaceExecutionError("Could not find savefile tag");
|
|
dcp->conf = capa->buildArchitecture("","",status->optr);
|
|
try {
|
|
dcp->conf->restoreXml(store);
|
|
} catch(LowlevelError &err) {
|
|
throw IfaceExecutionError(err.explain);
|
|
} catch(DecoderError &err) {
|
|
throw IfaceExecutionError(err.explain);
|
|
}
|
|
|
|
#ifdef OPACTION_DEBUG
|
|
dcp->conf->setDebugStream(status->fileoptr);
|
|
#endif
|
|
*status->optr << savefile << " successfully loaded: " << dcp->conf->getDescription() << endl;
|
|
}
|
|
|
|
} // End namespace ghidra
|
|
|
|
int main(int argc,char **argv)
|
|
|
|
{
|
|
using namespace ghidra;
|
|
|
|
const char *initscript = (const char *)0;
|
|
|
|
{
|
|
vector<string> extrapaths;
|
|
int4 i = 1;
|
|
while ((i < argc) && (argv[i][0] == '-')) {
|
|
if (argv[i][1] == 'i')
|
|
initscript = argv[++i];
|
|
else if (argv[i][1] == 's')
|
|
extrapaths.push_back(argv[++i]);
|
|
i += 1;
|
|
}
|
|
|
|
string ghidraroot = FileManage::discoverGhidraRoot(argv[0]);
|
|
if (ghidraroot.size() == 0) {
|
|
const char *sleighhomepath = getenv("SLEIGHHOME");
|
|
if (sleighhomepath == (const char*) 0) {
|
|
if (extrapaths.empty()) {
|
|
cerr << "Could not discover root of Ghidra installation" << endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
ghidraroot = sleighhomepath;
|
|
}
|
|
startDecompilerLibrary(ghidraroot.c_str(), extrapaths);
|
|
}
|
|
|
|
IfaceStatus *status;
|
|
try {
|
|
status = new IfaceTerm("[decomp]> ",cin,cout); // Set up interface
|
|
} catch(IfaceError &err) {
|
|
cerr << "Interface error during setup: " << err.explain << endl;
|
|
exit(1);
|
|
}
|
|
IfaceCapability::registerAllCommands(status); // Register commands for decompiler and all modules
|
|
|
|
// Extra commands specific to the console application
|
|
status->registerCom(new IfcLoadFile(),"load","file");
|
|
status->registerCom(new IfcAddpath(),"addpath");
|
|
status->registerCom(new IfcSave(),"save");
|
|
status->registerCom(new IfcRestore(),"restore");
|
|
|
|
if (initscript != (const char *)0) {
|
|
try {
|
|
status->pushScript(initscript,"init> ");
|
|
} catch(IfaceParseError &err) {
|
|
*status->optr << err.explain << endl;
|
|
status->done = true;
|
|
}
|
|
}
|
|
|
|
if (!status->done)
|
|
mainloop(status);
|
|
int4 retval = status->isInError() ? 1 : 0;
|
|
|
|
#ifdef CPUI_STATISTICS
|
|
IfaceDecompData *decompdata = (IfaceDecompData *)status->getData("decompile");
|
|
decompdata->conf->stats->printResults(cout);
|
|
#endif
|
|
|
|
try {
|
|
delete status;
|
|
} catch(IfaceError &err) {
|
|
cerr << err.explain << endl;
|
|
}
|
|
|
|
shutdownDecompilerLibrary();
|
|
|
|
exit(retval);
|
|
}
|