mirror of
https://github.com/lifting-bits/sleigh
synced 2026-06-21 13:56:12 +00:00
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 <eric.d.kilmer@gmail.com> * 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 <eric.d.kilmer@gmail.com> Co-authored-by: Eric Kilmer <eric.d.kilmer@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 <path where SLEIGH is installed>/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 <path where SLEIGH is installed>/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 <path where SLEIGH is installed>/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<std::filesystem::path>
|
||||
FindSpecFile(std::string_view file_name,
|
||||
const std::vector<std::filesystem::path> &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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>")
|
||||
@@ -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
|
||||
@@ -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<std::filesystem::path>
|
||||
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:
|
||||
// <root_sla_dir>/Ghidra/Processors/<arch>/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<std::filesystem::path> 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<std::filesystem::path>
|
||||
FindSpecFile(std::string_view file_name,
|
||||
const std::vector<std::filesystem::path> &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
|
||||
@@ -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 <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sleigh {
|
||||
|
||||
extern const std::vector<std::filesystem::path> gDefaultSearchPaths;
|
||||
|
||||
std::optional<std::filesystem::path>
|
||||
FindSpecFile(std::string_view file_name,
|
||||
const std::vector<std::filesystem::path> &search_paths =
|
||||
gDefaultSearchPaths);
|
||||
|
||||
} // namespace sleigh
|
||||
@@ -29,6 +29,7 @@ endif()
|
||||
target_link_libraries(sleigh-lift PRIVATE
|
||||
sleigh::sla
|
||||
sleigh::decomp
|
||||
sleigh::support
|
||||
)
|
||||
|
||||
if(SLEIGH_ENABLE_INSTALL)
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
#include <string>
|
||||
|
||||
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<uint64_t> addr;
|
||||
const std::optional<std::string> root_sla_dir;
|
||||
};
|
||||
|
||||
std::optional<LiftArgs> 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<uint64_t> addr;
|
||||
std::optional<std::string> 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;
|
||||
|
||||
Reference in New Issue
Block a user