From 30f07abe962af05913d4fbe817335c31e8fb370d Mon Sep 17 00:00:00 2001 From: AkshayK Date: Fri, 13 Mar 2026 01:26:50 -0400 Subject: [PATCH 1/2] decomp: fix null pointer checks, checks for possible cyclic calls, checks for interger overflow --- include/patchestry/AST/OperationBuilder.hpp | 4 ++ lib/patchestry/AST/FunctionBuilder.cpp | 41 +++++++++--- lib/patchestry/AST/OperationBuilder.cpp | 27 +++++--- lib/patchestry/AST/OperationStmt.cpp | 27 +++++++- lib/patchestry/Dialect/Pcode/Deserialize.cpp | 14 +++- lib/patchestry/Ghidra/JsonDeserialize.cpp | 70 +++++++++++--------- lib/patchestry/Passes/Compiler.cpp | 8 ++- tools/patchir-decomp/main.cpp | 17 ++++- 8 files changed, 149 insertions(+), 59 deletions(-) diff --git a/include/patchestry/AST/OperationBuilder.hpp b/include/patchestry/AST/OperationBuilder.hpp index f73c21a7..a4136936 100644 --- a/include/patchestry/AST/OperationBuilder.hpp +++ b/include/patchestry/AST/OperationBuilder.hpp @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -268,6 +269,9 @@ namespace patchestry::ast { // Cache for intrinsic function declarations std::unordered_map< std::string, clang::FunctionDecl * > intrinsic_decls; + + // Cycle detection for create_temporary forward-reference resolution + std::unordered_set< std::string > resolving_temporaries; }; } // namespace patchestry::ast diff --git a/lib/patchestry/AST/FunctionBuilder.cpp b/lib/patchestry/AST/FunctionBuilder.cpp index 6ee85af2..428e5ccb 100644 --- a/lib/patchestry/AST/FunctionBuilder.cpp +++ b/lib/patchestry/AST/FunctionBuilder.cpp @@ -110,8 +110,12 @@ namespace patchestry::ast { addr = std::stoull( key.substr(first_colon + 1, second_colon - first_colon - 1), nullptr, 16 ); - } catch (...) { - LOG(WARNING) << "BlockKeyComparator: failed to parse hex address in key: " << key; + } catch (const std::invalid_argument &) { + LOG(WARNING) << "BlockKeyComparator: non-numeric hex address in key: " << key; + return {std::numeric_limits< uint64_t >::max(), + std::numeric_limits< int64_t >::max()}; + } catch (const std::out_of_range &) { + LOG(WARNING) << "BlockKeyComparator: hex address out of range in key: " << key; return {std::numeric_limits< uint64_t >::max(), std::numeric_limits< int64_t >::max()}; } @@ -130,8 +134,11 @@ namespace patchestry::ast { int64_t idx = 0; try { idx = std::stoll(idx_str); - } catch (...) { - LOG(WARNING) << "BlockKeyComparator: failed to parse block index in key: " << key; + } catch (const std::invalid_argument &) { + LOG(WARNING) << "BlockKeyComparator: non-numeric block index in key: " << key; + return {addr, std::numeric_limits< int64_t >::max()}; + } catch (const std::out_of_range &) { + LOG(WARNING) << "BlockKeyComparator: block index out of range in key: " << key; return {addr, std::numeric_limits< int64_t >::max()}; } @@ -338,8 +345,13 @@ namespace patchestry::ast { << param_op->key; continue; } - const auto ¶m_type = - type_builder.get().get_serialized_types().at(*param_op->type); + auto type_iter = type_builder.get().get_serialized_types().find(*param_op->type); + if (type_iter == type_builder.get().get_serialized_types().end()) { + LOG(ERROR) << "Parameter type not found in serialized types: " + << *param_op->type << ", key: " << param_op->key; + continue; + } + const auto ¶m_type = type_iter->second; auto location = SourceLocation(ctx.getSourceManager(), param_op->key); auto *param_decl = clang::ParmVarDecl::Create( @@ -686,9 +698,15 @@ namespace patchestry::ast { } const auto &operation = block.operations.at(operation_key); + + // Save pending_materialized in case create_operation re-enters + // (e.g., create_temporary resolving a forward reference triggers + // nested operation building that appends to the same vector). + auto saved_pending = std::move(pending_materialized); + pending_materialized.clear(); + if (auto [stmt, should_merge_to_next] = create_operation(ctx, operation); stmt) { - // Drain any VarDecl materializations queued during create_operation - // (e.g., from create_temporary promoting a cached expr into a VarDecl). + // Drain any VarDecl materializations queued during create_operation. // These must appear before the consuming statement in the output. for (auto *pending : pending_materialized) { stmt_vec.push_back(pending); @@ -699,6 +717,13 @@ namespace patchestry::ast { if (!should_merge_to_next) { stmt_vec.push_back(stmt); } + } else { + pending_materialized.clear(); + } + + // Restore any outer-level pending materializations + for (auto *s : saved_pending) { + pending_materialized.push_back(s); } } diff --git a/lib/patchestry/AST/OperationBuilder.cpp b/lib/patchestry/AST/OperationBuilder.cpp index 3bf8fe38..62eba955 100644 --- a/lib/patchestry/AST/OperationBuilder.cpp +++ b/lib/patchestry/AST/OperationBuilder.cpp @@ -92,7 +92,7 @@ namespace patchestry::ast { clang::Stmt *OpBuilder::create_parameter(clang::ASTContext &ctx, const Varnode &vnode) { if (!vnode.operation || vnode.kind != Varnode::VARNODE_PARAM) { - assert(false && "Invalid parameter varnode"); + LOG(ERROR) << "Invalid parameter varnode\n"; return nullptr; } @@ -109,7 +109,7 @@ namespace patchestry::ast { clang::Stmt *OpBuilder::create_global(clang::ASTContext &ctx, const Varnode &vnode) { if (!vnode.global || vnode.kind != Varnode::VARNODE_GLOBAL) { - assert(false && "Invalid global varnode"); + LOG(ERROR) << "Invalid global varnode\n"; return {}; } @@ -150,18 +150,29 @@ namespace patchestry::ast { // via a recursive call (which will fall into Case 2). This prevents re-execution // if create_temporary is called again for the same key before create_basic_block // reaches the defining block. - if (auto maybe_operation = operationFromKey(function, vnode.operation.value())) { + const auto &op_key = *vnode.operation; + if (resolving_temporaries.contains(op_key)) { + LOG(ERROR) << "Cyclic forward reference detected for temporary: " << op_key << "\n"; + return {}; + } + resolving_temporaries.insert(op_key); + + clang::Stmt *result = nullptr; + if (auto maybe_operation = operationFromKey(function, op_key)) { auto [stmt, _] = function_builder().create_operation(ctx, *maybe_operation); if (stmt) { - function_builder().operation_stmts.emplace(*vnode.operation, stmt); + function_builder().operation_stmts.emplace(op_key, stmt); // Recurse: will hit Case 1 (if already local) or Case 2. - return create_temporary(ctx, function, vnode); + result = create_temporary(ctx, function, vnode); + } else { + result = stmt; } - return stmt; + } else { + LOG(ERROR) << "Failed to get operation for key: " << op_key << "\n"; } - assert(false && "Failed to get operation for key"); - return {}; + resolving_temporaries.erase(op_key); + return result; } clang::Stmt *OpBuilder::create_function(clang::ASTContext &ctx, const Varnode &vnode) { diff --git a/lib/patchestry/AST/OperationStmt.cpp b/lib/patchestry/AST/OperationStmt.cpp index 673370b1..f59dcaeb 100644 --- a/lib/patchestry/AST/OperationStmt.cpp +++ b/lib/patchestry/AST/OperationStmt.cpp @@ -307,7 +307,10 @@ namespace patchestry::ast { } auto result = sema().ImpCastExprToType(expr, to_type, kind); - assert(!result.isInvalid() && "Failed to make implicit cast expr"); + if (result.isInvalid()) { + LOG(ERROR) << "Failed to make implicit cast expr\n"; + return nullptr; + } return result.getAs< clang::Expr >(); } @@ -723,6 +726,11 @@ namespace patchestry::ast { auto loc = SourceLocation(ctx.getSourceManager(), op.key); auto *condition_expr = clang::dyn_cast< clang::Expr >(create_varnode(ctx, function, *op.condition)); + if (!condition_expr) { + LOG(ERROR) << "Failed to create condition expression for cbranch. key: " << op.key + << "\n"; + return {}; + } clang::Stmt *taken_stmt = nullptr; clang::Stmt *not_taken_stmt = nullptr; @@ -1260,6 +1268,10 @@ namespace patchestry::ast { } auto operation = operationFromKey(function, *op.target->operation); + if (!operation) { + LOG(ERROR) << "CALL target operation not found. key: " << op.key << "\n"; + return {}; + } auto [stmt, _] = function_builder().create_operation(ctx, *operation); auto result = sema().BuildCallExpr( nullptr, clang::dyn_cast< clang::Expr >(stmt), op_loc, arguments, op_loc @@ -1550,6 +1562,10 @@ namespace patchestry::ast { } auto location = SourceLocation(ctx.getSourceManager(), op.key); + if (op.inputs[1].size > UINT32_MAX / 8U) { + LOG(ERROR) << "PIECE input size too large, would overflow. key: " << op.key; + return {}; + } unsigned low_width = op.inputs[1].size * 8; auto merge_to_next = !op.output.has_value(); @@ -1638,6 +1654,11 @@ namespace patchestry::ast { auto *expr = clang::dyn_cast< clang::Expr >(create_varnode(ctx, function, op.inputs[0])); + if (!expr || !shift_value) { + LOG(ERROR) << "Failed to create SUBPIECE input expression. key: " << op.key; + return {}; + } + if (!ctx.hasSameUnqualifiedType(expr->getType(), op_type)) { if (auto *casted_expr = make_cast(ctx, expr, op_type, op_location)) { expr = casted_expr; @@ -2352,12 +2373,12 @@ namespace patchestry::ast { for (auto *field : decl->fields()) { auto offset = static_cast< unsigned int >(layout.getFieldOffset(field->getFieldIndex())); - if (offset >= target_offset * 8U) { + if (offset >= static_cast< uint64_t >(target_offset) * 8U) { return field; } } - assert(false && "Failed to find field decl at offset, check!"); + LOG(ERROR) << "Failed to find field decl at offset " << target_offset << "\n"; return nullptr; }; diff --git a/lib/patchestry/Dialect/Pcode/Deserialize.cpp b/lib/patchestry/Dialect/Pcode/Deserialize.cpp index 6f868976..b6b8316e 100644 --- a/lib/patchestry/Dialect/Pcode/Deserialize.cpp +++ b/lib/patchestry/Dialect/Pcode/Deserialize.cpp @@ -39,7 +39,12 @@ namespace patchestry::pc { bld.setInsertionPointToStart(bld.createBlock(&fn.getBlocks())); if (auto blocks = json.getArray("basic_blocks")) { for (const auto &block : *blocks) { - process_block(*block.getAsObject()); + const auto *block_obj = block.getAsObject(); + if (!block_obj) { + mlir::emitError(bld.getUnknownLoc(), "Block entry is not a JSON object."); + continue; + } + process_block(*block_obj); } } } @@ -63,7 +68,12 @@ namespace patchestry::pc { } for (const auto &inst : *insts) { - process_instruction(*inst.getAsObject()); + const auto *inst_obj = inst.getAsObject(); + if (!inst_obj) { + mlir::emitError(bld.getUnknownLoc(), "Instruction entry is not a JSON object."); + continue; + } + process_instruction(*inst_obj); } } diff --git a/lib/patchestry/Ghidra/JsonDeserialize.cpp b/lib/patchestry/Ghidra/JsonDeserialize.cpp index 3780e47f..fa09dad3 100644 --- a/lib/patchestry/Ghidra/JsonDeserialize.cpp +++ b/lib/patchestry/Ghidra/JsonDeserialize.cpp @@ -119,11 +119,16 @@ namespace patchestry::ghidra { return; } - std::unordered_map< std::string, const JsonValue & > types_value_map; + std::unordered_map< std::string, const JsonValue * > types_value_map; for (const auto &type : type_obj) { const auto &type_value = type.getSecond(); - auto vnode_type = create_vnode_type(*type_value.getAsObject()); + const auto *type_obj_ptr = type_value.getAsObject(); + if (!type_obj_ptr) { + LOG(ERROR) << "Invalid JSON object for type entry\n"; + continue; + } + auto vnode_type = create_vnode_type(*type_obj_ptr); if (!vnode_type) { LOG(ERROR) << "Failed to create varnode type\n"; continue; @@ -132,7 +137,7 @@ namespace patchestry::ghidra { const auto type_key = type.getFirst().str(); vnode_type->SetKey(type_key); serialized_types.emplace(type_key, std::move(vnode_type)); - types_value_map.emplace(type_key, type_value); + types_value_map.emplace(type_key, &type_value); } LOG(INFO) << "Number of entry in serialized types: " << serialized_types.size() << "\n"; @@ -141,11 +146,17 @@ namespace patchestry::ghidra { for (const auto &[type_key, vnode_type] : serialized_types) { auto iter = types_value_map.find(type_key); if (iter == types_value_map.end()) { - assert(false && "type_key is missing from value map"); + LOG(ERROR) << "type_key is missing from value map: " << type_key << "\n"; + continue; + } + + const auto *json_value = iter->second; + const auto *json_obj = json_value->getAsObject(); + if (!json_obj) { + LOG(ERROR) << "Invalid JSON object for type key: " << type_key << "\n"; continue; } - const auto &json_value = iter->second; switch (vnode_type->kind) { case VarnodeType::Kind::VT_BOOLEAN: case VarnodeType::Kind::VT_INTEGER: @@ -155,73 +166,62 @@ namespace patchestry::ghidra { case VarnodeType::Kind::VT_VOID: deserialize_buildin( *dynamic_cast< BuiltinType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; - case VarnodeType::Kind::VT_ARRAY: { - auto *obj = json_value.getAsObject(); - if (!obj) { - LOG(ERROR) << "Invalid JSON object for array type"; - break; - } + case VarnodeType::Kind::VT_ARRAY: deserialize_array( *dynamic_cast< ArrayType * >(vnode_type.get()), - obj, serialized_types + json_obj, serialized_types ); break; - } - case VarnodeType::Kind::VT_POINTER: { + case VarnodeType::Kind::VT_POINTER: deserialize_pointer( *dynamic_cast< PointerType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; - } - case VarnodeType::Kind::VT_FUNCTION: { + case VarnodeType::Kind::VT_FUNCTION: deserialize_function_type( *dynamic_cast< FunctionType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; - } case VarnodeType::Kind::VT_STRUCT: - case VarnodeType::Kind::VT_UNION: { + case VarnodeType::Kind::VT_UNION: deserialize_composite( *dynamic_cast< CompositeType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; - } - case VarnodeType::Kind::VT_ENUM: { + case VarnodeType::Kind::VT_ENUM: deserialize_enum( *dynamic_cast< EnumType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; - } - case VarnodeType::Kind::VT_TYPEDEF: { + case VarnodeType::Kind::VT_TYPEDEF: deserialize_typedef( *dynamic_cast< TypedefType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; - } case VarnodeType::Kind::VT_UNDEFINED: deserialize_undefined_type( *dynamic_cast< UndefinedType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; case VarnodeType::Kind::VT_BITFIELD: deserialize_bitfield( *dynamic_cast< BitFieldType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; case VarnodeType::Kind::VT_STRING: deserialize_string( *dynamic_cast< StringType * >(vnode_type.get()), - *json_value.getAsObject(), serialized_types + *json_obj, serialized_types ); break; case VarnodeType::Kind::VT_INVALID: @@ -699,7 +699,11 @@ namespace patchestry::ghidra { if (const auto *input_array = pcode_obj.getArray("inputs")) { for (auto input : *input_array) { - if (auto maybe_varnode = create_varnode(*input.getAsObject())) { + const auto *input_obj = input.getAsObject(); + if (!input_obj) { + continue; + } + if (auto maybe_varnode = create_varnode(*input_obj)) { operation.inputs.emplace_back(std::move(*maybe_varnode)); } } diff --git a/lib/patchestry/Passes/Compiler.cpp b/lib/patchestry/Passes/Compiler.cpp index 7fefe1e9..24ee61dd 100644 --- a/lib/patchestry/Passes/Compiler.cpp +++ b/lib/patchestry/Passes/Compiler.cpp @@ -104,8 +104,12 @@ namespace patchestry::passes { } const std::string &arch(lang_vec[0]); - int bit_size = std::stoi(lang_vec[2]); - auto is_le = (lang_vec[1] == "LE"); + int bit_size = 0; + if (llvm::StringRef(lang_vec[2]).getAsInteger(10, bit_size)) { + LOG(ERROR) << "Invalid bit size in language id: " << lang_vec[2] << "\n"; + return ""; + } + auto is_le = (lang_vec[1] == "LE"); auto variant = lang_vec.size() > 3 ? lang_vec[3] : ""; auto is_equal = [&](std::string astr, std::string bstr) -> bool { diff --git a/tools/patchir-decomp/main.cpp b/tools/patchir-decomp/main.cpp index ab2a03ab..df38b83b 100644 --- a/tools/patchir-decomp/main.cpp +++ b/tools/patchir-decomp/main.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -163,8 +164,12 @@ namespace { return ""; } - int bit_size = std::stoi(lang_vec[2]); - auto is_le = (lang_vec[1] == "LE"); + int bit_size = 0; + if (llvm::StringRef(lang_vec[2]).getAsInteger(10, bit_size)) { + LOG(ERROR) << "Invalid bit size in language id: " << lang_vec[2] << "\n"; + return ""; + } + auto is_le = (lang_vec[1] == "LE"); auto is_equal = [&](std::string astr, std::string bstr) -> bool { // transform both the string to lower-case and compare @@ -232,7 +237,13 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - auto program = patchestry::ghidra::JsonParser().deserialize_program(*json->getAsObject()); + const auto *json_obj = json->getAsObject(); + if (!json_obj) { + LOG(ERROR) << "Input JSON is not an object\n"; + return EXIT_FAILURE; + } + + auto program = patchestry::ghidra::JsonParser().deserialize_program(*json_obj); if (!program.has_value()) { LOG(ERROR) << "Failed to deserialize JSON file '" << options.input_file << "' as patchestry program\n"; From 0a516f0b90939a67dc444cdaed2cffb870ba8934 Mon Sep 17 00:00:00 2001 From: AkshayK Date: Fri, 13 Mar 2026 01:48:08 -0400 Subject: [PATCH 2/2] decomp: cleanup dead code --- .../patchestry/Dialect/Pcode/Deserialize.hpp | 41 ---------- .../patchestry/Ghidra/PcodeTranslation.hpp | 14 ---- lib/patchestry/Dialect/Pcode/CMakeLists.txt | 1 - lib/patchestry/Dialect/Pcode/Deserialize.cpp | 82 ------------------- lib/patchestry/Ghidra/CMakeLists.txt | 1 - lib/patchestry/Ghidra/PcodeTranslation.cpp | 49 ----------- test/CMakeLists.txt | 10 --- test/lit.cfg.py | 3 - test/pcode-translate/function.json | 17 ---- test/pcode-translate/help.json | 2 - tools/CMakeLists.txt | 1 - tools/pcode-translate/CMakeLists.txt | 33 -------- tools/pcode-translate/main.cpp | 16 ---- 13 files changed, 270 deletions(-) delete mode 100644 include/patchestry/Dialect/Pcode/Deserialize.hpp delete mode 100644 include/patchestry/Ghidra/PcodeTranslation.hpp delete mode 100644 lib/patchestry/Dialect/Pcode/Deserialize.cpp delete mode 100644 lib/patchestry/Ghidra/PcodeTranslation.cpp delete mode 100644 test/pcode-translate/function.json delete mode 100644 test/pcode-translate/help.json delete mode 100644 tools/pcode-translate/CMakeLists.txt delete mode 100644 tools/pcode-translate/main.cpp diff --git a/include/patchestry/Dialect/Pcode/Deserialize.hpp b/include/patchestry/Dialect/Pcode/Deserialize.hpp deleted file mode 100644 index 7757b8b8..00000000 --- a/include/patchestry/Dialect/Pcode/Deserialize.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2024, Trail of Bits, Inc. - * - * This source code is licensed in accordance with the terms specified in - * the LICENSE file found in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace patchestry::pc { - - using json_arr = llvm::json::Array; - using json_obj = llvm::json::Object; - using json_val = llvm::json::Value; - - struct deserializer - { - mlir_builder bld; - - explicit deserializer(mlir::ModuleOp mod) : bld(mod) { - assert(mod->getNumRegions() > 0 && "Module has no regions."); - auto ® = mod->getRegion(0); - assert(reg.hasOneBlock() && "Region has unexpected blocks."); - bld.setInsertionPointToStart(&*reg.begin()); - } - - void process(const json_obj &json); - void process_function(const json_obj &json); - void process_block(const json_obj &json); - void process_instruction(const json_obj &json); - }; - - mlir::OwningOpRef< mlir::ModuleOp > deserialize(const json_obj &json, mcontext_t *mctx); - -} // namespace patchestry::pc diff --git a/include/patchestry/Ghidra/PcodeTranslation.hpp b/include/patchestry/Ghidra/PcodeTranslation.hpp deleted file mode 100644 index ae0dc750..00000000 --- a/include/patchestry/Ghidra/PcodeTranslation.hpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2024, Trail of Bits, Inc. - * - * This source code is licensed in accordance with the terms specified in - * the LICENSE file found in the root directory of this source tree. - */ - -#pragma once - -namespace patchestry::ghidra { - - void register_pcode_translation(); - -} // namespace patchestry::ghidra diff --git a/lib/patchestry/Dialect/Pcode/CMakeLists.txt b/lib/patchestry/Dialect/Pcode/CMakeLists.txt index 470e39d2..efb3337d 100644 --- a/lib/patchestry/Dialect/Pcode/CMakeLists.txt +++ b/lib/patchestry/Dialect/Pcode/CMakeLists.txt @@ -7,7 +7,6 @@ add_mlir_dialect_library(MLIRPcode PcodeDialect.cpp PcodeOps.cpp PcodeTypes.cpp - Deserialize.cpp ADDITIONAL_HEADER_DIRS ${PROJECT_SOURCE_DIR}/include/patchestry diff --git a/lib/patchestry/Dialect/Pcode/Deserialize.cpp b/lib/patchestry/Dialect/Pcode/Deserialize.cpp deleted file mode 100644 index b6b8316e..00000000 --- a/lib/patchestry/Dialect/Pcode/Deserialize.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2024, Trail of Bits, Inc. - * All rights reserved. - * - * This source code is licensed in accordance with the terms specified in - * the LICENSE file found in the root directory of this source tree. - */ - -#include -#include - -namespace patchestry::pc { - - mlir::OwningOpRef< mlir::ModuleOp > deserialize(const json_obj &json, mcontext_t *mctx) { - // FIXME: use implicit module creation - auto loc = mlir::UnknownLoc::get(mctx); - auto mod = mlir::OwningOpRef< mlir::ModuleOp >(mlir::ModuleOp::create(loc)); - - deserializer des(mod.get()); - des.process(json); - - return mod; - } - - void deserializer::process(const json_obj &json) { - // FIXME: implement multi-function support - process_function(json); - } - - void deserializer::process_function(const json_obj &json) { - if (!json.getString("name")) { - mlir::emitError(bld.getUnknownLoc(), "Function JSON missing 'name' field."); - return; - } - - auto _ = insertion_guard(bld); - auto fn = bld.create< pc::FuncOp >(bld.getUnknownLoc(), json.getString("name").value()); - - bld.setInsertionPointToStart(bld.createBlock(&fn.getBlocks())); - if (auto blocks = json.getArray("basic_blocks")) { - for (const auto &block : *blocks) { - const auto *block_obj = block.getAsObject(); - if (!block_obj) { - mlir::emitError(bld.getUnknownLoc(), "Block entry is not a JSON object."); - continue; - } - process_block(*block_obj); - } - } - } - - void deserializer::process_block(const json_obj &json) { - if (!json.getString("label")) { - mlir::emitError(bld.getUnknownLoc(), "Block JSON missing 'label' field."); - return; - } - - auto _ = insertion_guard(bld); - auto block = - bld.create< pc::BlockOp >(bld.getUnknownLoc(), json.getString("label").value()); - - bld.createBlock(&block.getInstructions()); - - const auto *insts = json.getArray("instructions"); - if (insts == nullptr) { - mlir::emitError(bld.getUnknownLoc(), "Block JSON missing 'instructions' field."); - return; - } - - for (const auto &inst : *insts) { - const auto *inst_obj = inst.getAsObject(); - if (!inst_obj) { - mlir::emitError(bld.getUnknownLoc(), "Instruction entry is not a JSON object."); - continue; - } - process_instruction(*inst_obj); - } - } - - void deserializer::process_instruction(const json_obj &json) {} - -} // namespace patchestry::pc diff --git a/lib/patchestry/Ghidra/CMakeLists.txt b/lib/patchestry/Ghidra/CMakeLists.txt index 1793aa94..d4ab7ddd 100644 --- a/lib/patchestry/Ghidra/CMakeLists.txt +++ b/lib/patchestry/Ghidra/CMakeLists.txt @@ -4,7 +4,6 @@ # LICENSE file found in the root directory of this source tree. add_library(patchestry_ghidra STATIC - PcodeTranslation.cpp JsonDeserialize.cpp ) diff --git a/lib/patchestry/Ghidra/PcodeTranslation.cpp b/lib/patchestry/Ghidra/PcodeTranslation.cpp deleted file mode 100644 index b5ca3845..00000000 --- a/lib/patchestry/Ghidra/PcodeTranslation.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2024, Trail of Bits, Inc. - * - * This source code is licensed in accordance with the terms specified in - * the LICENSE file found in the root directory of this source tree. - */ - -#include - -#include -#include - -#include - -#include -#include - -#include -#include -#include - -namespace patchestry::ghidra { - - static mlir::OwningOpRef< mlir_operation > - deserialize(const llvm::MemoryBuffer *buffer, mcontext_t *mctx) { - mctx->loadAllAvailableDialects(); - - auto json = llvm::json::parse(buffer->getBuffer()); - if (!json) { - mlir::emitError(mlir::UnknownLoc::get(mctx), "failed to parse PCode JSON: ") - << toString(json.takeError()); - } - return pc::deserialize(*json->getAsObject(), mctx); - } - - void register_pcode_translation() { - mlir::TranslateToMLIRRegistration( - "deserialize-pcode", "translate Ghidra Pcode JSON into Patchestry's Pcode dialect", - [](llvm::SourceMgr &smgr, mcontext_t *mctx) { - assert(smgr.getNumBuffers() == 1 && "expected one buffer"); - return deserialize(smgr.getMemoryBuffer(smgr.getMainFileID()), mctx); - }, - [](mlir::DialectRegistry ®istry) { - registry.insert< patchestry::pc::PcodeDialect >(); - } - ); - } - -} // namespace patchestry::ghidra diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 174853be..21576a88 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,11 +20,6 @@ add_lit_testsuite(ghidra-output-tests ${CMAKE_CURRENT_SOURCE_DIR}/ghidra ) -add_lit_testsuite(pcode-translation-tests - "Running pcode-translate tests" - ${CMAKE_CURRENT_SOURCE_DIR}/pcode-translate -) - add_lit_testsuite(patchir-decomp-tests "Running patchir-decomp tests" ${CMAKE_CURRENT_SOURCE_DIR}/patchir-decomp @@ -41,11 +36,6 @@ add_test(NAME ghidra-output-tests --param BUILD_TYPE=$ ) -add_test(NAME pcode-translation-tests - COMMAND lit -v -j 4 "${CMAKE_CURRENT_BINARY_DIR}/pcode-translate" - --param BUILD_TYPE=$ -) - add_test(NAME patchir-decomp-tests COMMAND lit -v -j 4 "${CMAKE_CURRENT_BINARY_DIR}/patchir-decomp" --param BUILD_TYPE=$ diff --git a/test/lit.cfg.py b/test/lit.cfg.py index dd72d166..01cbd0dd 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -58,8 +58,6 @@ def patchestry_tool_path(tool): return os.path.join(*path, tool) config.decompiler_headless_tool = os.path.join(config.patchestry_script_dir, 'decompile-headless.sh') -config.pcode_translate_tool = patchestry_tool_path('pcode-translate') - config.json_strip_comments = os.path.join(config.test_scripts_dir, 'strip-json-comments.sh') config.patchir_decomp_tool = patchestry_tool_path('patchir-decomp') @@ -121,7 +119,6 @@ def get_compiler_command(arch): ToolSubst('%host_cc', command=config.host_cc), ToolSubst('%host_cxx', command=config.host_cxx), ToolSubst('%decompile-headless', command=config.decompiler_headless_tool), - ToolSubst('%pcode-translate', command=config.pcode_translate_tool), ToolSubst('%patchir-decomp', command=config.patchir_decomp_tool), ToolSubst('%patchir-transform', command=config.patchir_transform_tool), ToolSubst('%patchir-cir2llvm', command=config.patchir_cir2llvm_tool), diff --git a/test/pcode-translate/function.json b/test/pcode-translate/function.json deleted file mode 100644 index 45670694..00000000 --- a/test/pcode-translate/function.json +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: bash %strip-json-comments %s | %pcode-translate --deserialize-pcode | %file-check %s -{ - // CHECK: pc.func @function - "name": "function", - "basic_blocks": [ - { - // CHECK: pc.block @fisrt_block - "label": "fisrt_block", - "instructions": [] - }, - { - // CHECK: pc.block @second_block - "label": "second_block", - "instructions": [] - } - ] -} \ No newline at end of file diff --git a/test/pcode-translate/help.json b/test/pcode-translate/help.json deleted file mode 100644 index 7319eda2..00000000 --- a/test/pcode-translate/help.json +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: %pcode-translate --help | %file-check %s -// CHECK: --deserialize-pcode diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 2c5880c4..ba8943f0 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -3,7 +3,6 @@ # This source code is licensed in accordance with the terms specified in the # LICENSE file found in the root directory of this source tree. -add_subdirectory(pcode-translate) add_subdirectory(patchir-decomp) add_subdirectory(patchir-cir2llvm) add_subdirectory(patchir-transform) diff --git a/tools/pcode-translate/CMakeLists.txt b/tools/pcode-translate/CMakeLists.txt deleted file mode 100644 index a22157b8..00000000 --- a/tools/pcode-translate/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2024, Trail of Bits, Inc. -# -# This source code is licensed in accordance with the terms specified in the -# LICENSE file found in the root directory of this source tree. - -set(LLVM_LINK_COMPONENTS - Support -) - - -add_executable(pcode-translate - main.cpp -) - -llvm_update_compile_flags(pcode-translate) -target_link_libraries(pcode-translate - PRIVATE - MLIRIR - MLIRParser - MLIRPass - MLIRTranslateLib - MLIRSupport - patchestry::ghidra -) - -mlir_check_link_libraries(pcode-translate) - -if (PATCHESTRY_INSTALL) - install(TARGETS pcode-translate - DESTINATION ${CMAKE_INSTALL_BINDIR} - COMPONENT patchestry-tools - ) -endif() diff --git a/tools/pcode-translate/main.cpp b/tools/pcode-translate/main.cpp deleted file mode 100644 index bc711f6f..00000000 --- a/tools/pcode-translate/main.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2024, Trail of Bits, Inc. - * - * This source code is licensed in accordance with the terms specified in - * the LICENSE file found in the root directory of this source tree. - */ - -#include -#include - -#include - -int main(int argc, char **argv) { - patchestry::ghidra::register_pcode_translation(); - return mlir::failed(mlir::mlirTranslateMain(argc, argv, "P-Code translation driver\n")); -}