// Phase-1 smoke tool for the vendored Ghidra decompiler. // // Lifts a raw machine-code buffer to Ghidra P-Code using a compiled .sla and // prints disassembly + P-Code per instruction. This validates the vendored // engine builds, the .sla loads, and lifting works end-to-end — before any // fold-side IR/encoder glue (Phase 2+). Bytes are served via a custom // LoadImage (no BFD): fold's loader contract. // // pcode_dump // #include #include #include #include #include "loadimage.hh" #include "sleigh.hh" using namespace ghidra; namespace { // Serves a flat byte buffer at a fixed base address; zero-fills outside it. class BufferLoadImage : public LoadImage { uintb base_; std::vector data_; public: BufferLoadImage(uintb base, std::vector data) : LoadImage("nofile"), base_(base), data_(std::move(data)) {} void loadFill(uint1* ptr, int4 size, const Address& addr) override { uintb start = addr.getOffset(); for (int4 i = 0; i < size; ++i) { uintb off = start + i; ptr[i] = (off >= base_ && off < base_ + data_.size()) ? data_[off - base_] : 0; } } std::string getArchType() const override { return "buffer"; } void adjustVma(long) override {} uintb end() const { return base_ + data_.size(); } }; class AsmPrinter : public AssemblyEmit { public: void dump(const Address& addr, const std::string& mnem, const std::string& body) override { addr.printRaw(std::cout); std::cout << ": " << mnem << ' ' << body << '\n'; } }; void printVar(std::ostream& s, const VarnodeData& v) { s << '(' << v.space->getName() << ','; v.space->printOffset(s, v.offset); s << ',' << std::dec << v.size << ')'; } class PcodePrinter : public PcodeEmit { public: void dump(const Address&, OpCode opc, VarnodeData* outvar, VarnodeData* vars, int4 isize) override { std::cout << " "; if (outvar != nullptr) { printVar(std::cout, *outvar); std::cout << " = "; } std::cout << get_opname(opc); for (int4 i = 0; i < isize; ++i) { std::cout << ' '; printVar(std::cout, vars[i]); } std::cout << '\n'; } }; } // namespace int main(int argc, char** argv) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " \n"; return 2; } const std::string sla_path = argv[1]; const uintb base = std::strtoull(argv[2], nullptr, 16); std::ifstream bf(argv[3], std::ios::binary); if (!bf) { std::cerr << "cannot open bytes file: " << argv[3] << '\n'; return 2; } std::vector bytes((std::istreambuf_iterator(bf)), std::istreambuf_iterator()); if (bytes.empty()) { std::cerr << "empty bytes file\n"; return 2; } AttributeId::initialize(); ElementId::initialize(); BufferLoadImage loader(base, bytes); ContextInternal context; Sleigh trans(&loader, &context); std::istringstream slatag("" + sla_path + ""); DocumentStorage docstorage; Element* root = docstorage.parseDocument(slatag)->getRoot(); docstorage.registerTag(root); try { trans.initialize(docstorage); } catch (const LowlevelError& e) { std::cerr << "sleigh init failed: " << e.explain << '\n'; return 1; } AsmPrinter asmp; PcodePrinter pcodep; Address addr(trans.getDefaultCodeSpace(), base); Address last(trans.getDefaultCodeSpace(), loader.end()); int instrs = 0; while (addr < last) { try { trans.printAssembly(asmp, addr); int4 len = trans.oneInstruction(pcodep, addr); if (len <= 0) break; addr = addr + len; ++instrs; } catch (const BadDataError& e) { std::cerr << "bad data at "; addr.printRaw(std::cerr); std::cerr << ": " << e.explain << '\n'; break; } catch (const LowlevelError& e) { std::cerr << "lift error: " << e.explain << '\n'; break; } } std::cerr << "[pcode_dump] lifted " << instrs << " instruction(s)\n"; return 0; }