From 92671daec5fc300ee83d3c6983b4e638f3562b09 Mon Sep 17 00:00:00 2001 From: Alex Cameron Date: Fri, 10 Dec 2021 15:45:53 +1100 Subject: [PATCH] libsleigh: Add helpers for finding spec files (#24) * libsleigh: Add helpers for finding spec files * doc: Update README * support: Move spec file helpers to separate `support` lib * support: Use `std::filesystem::path` over `std::string` * support: Generate helpers with CMake configure instead of macros * sleigh-lift: Rework optional args and include new SLA path arg * README: Document support helper * sleigh-lift: Fix compile warning * sleigh-lift: Rework default args to `FindSpecFile` * Apply suggestions from code review Co-authored-by: Eric Kilmer * cmake: Fix quotes * support: Move build/install directories into separate header * support: Add missing copyright notice * support: Mark spec file dirs as static * cmake: Fix source list since we're generating a header now * support: Fix search path * sleigh-lift: Rename arg to `root_sla_dir` * README: Update usage text * sleight-lift: Rename option * Update support/Support.cpp Co-authored-by: Eric Kilmer Co-authored-by: Eric Kilmer --- CMakeLists.txt | 8 +++ README.md | 21 ++++-- include/libsleigh.hh | 3 + support/CMakeLists.txt | 29 ++++++++ support/SpecFilePaths.h.in | 16 +++++ support/Support.cpp | 70 +++++++++++++++++++ support/Support.h | 25 +++++++ tools/sleigh-lift/CMakeLists.txt | 1 + tools/sleigh-lift/src/main.cpp | 111 +++++++++++++++++++++++-------- 9 files changed, 251 insertions(+), 33 deletions(-) create mode 100644 support/CMakeLists.txt create mode 100644 support/SpecFilePaths.h.in create mode 100644 support/Support.cpp create mode 100644 support/Support.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 87e2017..1086942 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ endif() set(public_include_header_list "${PROJECT_SOURCE_DIR}/include/libsleigh.hh" + "${PROJECT_SOURCE_DIR}/support/Support.h" "${library_root}/address.hh" "${library_root}/context.hh" "${library_root}/emulate.hh" @@ -192,6 +193,9 @@ target_compile_features(sleigh_settings INTERFACE cxx_std_17 ) +set(SLEIGH_SPEC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/sleigh/specfiles/") +set(SLEIGH_SPEC_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/specfiles/") + set_target_properties(sleigh_settings PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON ) @@ -585,6 +589,9 @@ if(SLEIGH_ENABLE_EXAMPLES) ) endif() +# Add support library before configuring install targets +add_subdirectory(support) + # # Install targets # @@ -630,6 +637,7 @@ if(SLEIGH_ENABLE_INSTALL) TARGETS sla decomp + support EXPORT sleigh diff --git a/README.md b/README.md index 80aff65..0f6d482 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ cd sleigh # Configure CMake cmake -B build -S . \ - -DSLEIGH_ENABLE_INSTALL=ON + -DSLEIGH_ENABLE_INSTALL=ON # Build SLEIGH cmake --build build -j @@ -78,20 +78,20 @@ cmake --build build --target package An example program called `sleigh-lift` has been included to demonstrate how to use the SLEIGH API. It takes a hexadecimal string of bytes and either disassembles it or lifts it to p-code. The program can be invoked like so, where the `action` argument must be either `disassemble` or `pcode`: ```sh -sleigh-lift [action] [sla_file] [bytes] [address:OPTIONAL] +sleigh-lift [action] [sla_file] [bytes] [-a address] [-p root_sla_dir] ``` For example, to disassemble the following byte string: ```sh -$ sleigh-lift disassemble /share/sleigh/Processors/x86/data/languages/x86-64.sla 4881ecc00f0000 +$ sleigh-lift disassemble x86-64.sla 4881ecc00f0000 0x00000000: SUB RSP,0xfc0 ``` And to lift it to p-code: ```sh -$ sleigh-lift pcode /share/sleigh/Processors/x86/data/languages/x86-64.sla 4881ecc00f0000 +$ sleigh-lift pcode x86-64.sla 4881ecc00f0000 (register,0x200,1) = INT_LESS (register,0x20,8) (const,0xfc0,8) (register,0x20b,1) = INT_SBORROW (register,0x20,8) (const,0xfc0,8) (register,0x20,8) = INT_SUB (register,0x20,8) (const,0xfc0,8) @@ -105,6 +105,19 @@ $ sleigh-lift pcode /share/sleigh/Processors/x86 The `SLEIGH_ENABLE_EXAMPLES` option must be set to `ON` during the configuration step in order to build `sleigh-lift`. +## Helpers + +This repository contains a helper that is not part of SLEIGH/GHIDRA, which can be found under `support/`. It has the following signature and can help the user find the location of a given spec file on the system: + +```c++ +std::optional +FindSpecFile(std::string_view file_name, + const std::vector &search_paths = + gDefaultSearchPaths); +``` + +The `sleigh::FindSpecFile` function will search the the paths provided by the user via the `search_paths` argument for a spec file with the name `file_name`. The default argument for `search_paths` is `sleigh::gDefaultSearchPaths` which contains the install/build directories that the CMake project was configured with as well as a set of common installation locations. + ## License See the LICENSE file in the top directory of this repo. diff --git a/include/libsleigh.hh b/include/libsleigh.hh index 144b21a..2ab9a97 100644 --- a/include/libsleigh.hh +++ b/include/libsleigh.hh @@ -6,6 +6,7 @@ the LICENSE file found in the root directory of this source tree. */ +#pragma once #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated" #pragma GCC diagnostic ignored "-Wsign-compare" @@ -33,3 +34,5 @@ #include "types.h" #include "xml.hh" #pragma GCC diagnostic pop + +#include "Support.h" diff --git a/support/CMakeLists.txt b/support/CMakeLists.txt new file mode 100644 index 0000000..20c946b --- /dev/null +++ b/support/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright (c) 2021-present, 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. +# + +# Generate the build and install directories to use in the support helpers +configure_file( + "${CMAKE_CURRENT_LIST_DIR}/SpecFilePaths.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/SpecFilePaths.h" + @ONLY +) + +set(sleigh_support_source_list + Support.cpp +) + +add_library(support + ${sleigh_support_source_list} +) + +add_library(sleigh::support ALIAS support) + +target_link_libraries(support PUBLIC + sleigh::sleigh_settings +) +target_include_directories(support PRIVATE "$") diff --git a/support/SpecFilePaths.h.in b/support/SpecFilePaths.h.in new file mode 100644 index 0000000..5ad47e2 --- /dev/null +++ b/support/SpecFilePaths.h.in @@ -0,0 +1,16 @@ +/* + Copyright (c) 2021-present, 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. +*/ + +#pragma once + +namespace sleigh { + +static const char *kSleighSpecInstallDir = "@SLEIGH_SPEC_INSTALL_DIR@"; +static const char *kSleighSpecBuildDir = "@SLEIGH_SPEC_BUILD_DIR@"; + +} // namespace sleigh diff --git a/support/Support.cpp b/support/Support.cpp new file mode 100644 index 0000000..18c5a93 --- /dev/null +++ b/support/Support.cpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2021-present, 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 "SpecFilePaths.h" + +#include "Support.h" + +namespace sleigh { + +namespace { + +std::optional +FindSpecFileInSearchPath(std::string_view file_name, + std::filesystem::path search_path) { + search_path.append("Ghidra").append("Processors"); + // Check whether a SLEIGH installation exists at this path + if (!std::filesystem::is_directory(search_path)) { + return {}; + } + // Each directory under Processors/ represents a family of architectures + // + // Spec files should reside under: + // /Ghidra/Processors//data/languages + std::filesystem::directory_iterator install_iter(search_path); + for (auto &dir_entry : install_iter) { + if (!dir_entry.is_directory()) { + continue; + } + // Check whether the spec file exists under data/languages/ + auto dir_path = dir_entry.path(); + dir_path.append("data").append("languages").append(file_name); + if (!std::filesystem::exists(dir_path)) { + continue; + } + return dir_path; + } + return {}; +} + +} // namespace + +const std::vector gDefaultSearchPaths = { + // Derived from the installation + kSleighSpecInstallDir, + // Derived from the build + kSleighSpecBuildDir, + // Common install locations + "/usr/local/share/sleigh/specfiles", "/usr/share/sleigh/specfiles", + "/share/sleigh/specfiles"}; + +std::optional +FindSpecFile(std::string_view file_name, + const std::vector &search_paths) { + // Search each path for spec files + for (const auto &path : search_paths) { + auto file_path = FindSpecFileInSearchPath(file_name, path); + if (file_path) { + return file_path; + } + } + // Cannot find the spec file + return {}; +} + +} // namespace sleigh diff --git a/support/Support.h b/support/Support.h new file mode 100644 index 0000000..9b43f41 --- /dev/null +++ b/support/Support.h @@ -0,0 +1,25 @@ +/* + Copyright (c) 2021-present, 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. +*/ + +#pragma once + +#include +#include +#include +#include + +namespace sleigh { + +extern const std::vector gDefaultSearchPaths; + +std::optional +FindSpecFile(std::string_view file_name, + const std::vector &search_paths = + gDefaultSearchPaths); + +} // namespace sleigh diff --git a/tools/sleigh-lift/CMakeLists.txt b/tools/sleigh-lift/CMakeLists.txt index e24b4f6..2a58a63 100644 --- a/tools/sleigh-lift/CMakeLists.txt +++ b/tools/sleigh-lift/CMakeLists.txt @@ -29,6 +29,7 @@ endif() target_link_libraries(sleigh-lift PRIVATE sleigh::sla sleigh::decomp + sleigh::support ) if(SLEIGH_ENABLE_INSTALL) diff --git a/tools/sleigh-lift/src/main.cpp b/tools/sleigh-lift/src/main.cpp index 9fe1379..e70d8ce 100644 --- a/tools/sleigh-lift/src/main.cpp +++ b/tools/sleigh-lift/src/main.cpp @@ -13,9 +13,9 @@ #include static void PrintUsage(void) { - std::cerr - << "Usage: sleigh-lift [action] [sla_file] [bytes] [address:OPTIONAL]" - << std::endl; + std::cerr << "Usage: sleigh-lift [action] [sla_file] [bytes] [-a address] " + "[-p root_sla_dir]" + << std::endl; } class InMemoryLoadImage : public LoadImage { @@ -133,39 +133,92 @@ static void PrintPcode(Sleigh &engine, uint64_t addr, size_t len) { } } -int main(int argc, char *argv[]) { - if (argc < 4 || argc > 5) { - PrintUsage(); - return -1; +struct LiftArgs { + const std::string action; + const std::string sla_file_name; + const std::string bytes; + const std::optional addr; + const std::optional root_sla_dir; +}; + +std::optional ParseArgs(int argc, char *argv[]) { + // Too few args + if (argc < 4) { + return {}; } - // Parse arguments - const std::string action = argv[1]; - const char *sla_file_path = argv[2]; - const std::string bytes = argv[3]; - const char *addr_str = argc == 5 ? argv[4] : nullptr; + + // Get positional args + int arg_index = 1; + std::string action = argv[arg_index++]; + std::string sla_file_name = argv[arg_index++]; + std::string bytes = argv[arg_index++]; if (bytes.size() % 2 != 0) { std::cerr << "Must provide an even number of bytes: " << bytes << std::endl; - return -1; + return {}; } - // Get the address as an integer. - uint64_t addr = 0; - if (addr_str) { - try { - addr = std::stoul(addr_str); - } catch (std::invalid_argument &ia) { - std::cerr << "Invalid address argument: " << addr_str << std::endl; - return EXIT_FAILURE; - } catch (std::out_of_range &oor) { - std::cerr << "Address argument out of range: " << addr_str << std::endl; - return EXIT_FAILURE; + + // Get optional args + std::optional addr; + std::optional root_sla_dir; + while (arg_index < argc) { + const std::string flag = argv[arg_index++]; + if (arg_index == argc) { + std::cerr << "Flag " << flag << " has no value" << std::endl; + return {}; } + if (flag == "-a") { + if (addr) { + std::cerr << "-a flag provided multiple times" << std::endl; + return {}; + } + const char *addr_str = argv[arg_index++]; + try { + addr = std::stoul(addr_str); + } catch (const std::invalid_argument &ia) { + std::cerr << "Invalid address argument: " << addr_str << std::endl; + return {}; + } catch (const std::out_of_range &oor) { + std::cerr << "Address argument out of range: " << addr_str << std::endl; + return {}; + } + } else if (flag == "-p") { + if (root_sla_dir) { + std::cerr << "-p flag provided multiple times" << std::endl; + return {}; + } + root_sla_dir = argv[arg_index++]; + } else { + std::cerr << "Unrecognised optional flag: " << flag << std::endl; + return {}; + } + } + return LiftArgs{std::move(action), std::move(sla_file_name), std::move(bytes), + addr, std::move(root_sla_dir)}; +} + +int main(int argc, char *argv[]) { + const auto args = ParseArgs(argc, argv); + if (!args) { + PrintUsage(); + return EXIT_FAILURE; + } + const uint64_t addr = args->addr ? *args->addr : 0; + // Find SLA file path + const auto sla_file_path = + args->root_sla_dir + ? sleigh::FindSpecFile(args->sla_file_name, {*args->root_sla_dir}) + : sleigh::FindSpecFile(args->sla_file_name); + if (!sla_file_path) { + std::cerr << "Could not find SLA file: " << args->sla_file_name + << std::endl; + return EXIT_FAILURE; } // Put together SLEIGH components InMemoryLoadImage load_image(addr); ContextInternal ctx; Sleigh engine(&load_image, &ctx); DocumentStorage storage; - Element *root = storage.openDocument(sla_file_path)->getRoot(); + Element *root = storage.openDocument(*sla_file_path)->getRoot(); storage.registerTag(root); engine.initialize(storage); // In order to parse and validate the byte string properly, we need to get the @@ -174,15 +227,15 @@ int main(int argc, char *argv[]) { // // Ensure that we don't start disassembling until we've set the image buffer. std::string image_buffer = - ParseHexBytes(bytes, addr, engine.getDefaultSize()); + ParseHexBytes(args->bytes, addr, engine.getDefaultSize()); const size_t len = image_buffer.size(); load_image.SetImageBuffer(std::move(image_buffer)); - if (action == "disassemble") { + if (args->action == "disassemble") { PrintAssembly(engine, addr, len); - } else if (action == "pcode") { + } else if (args->action == "pcode") { PrintPcode(engine, addr, len); } else { - std::cerr << "Invalid action: " << action << std::endl; + std::cerr << "Invalid action: " << args->action << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS;