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:
Alex Cameron
2021-12-10 15:45:53 +11:00
committed by GitHub
parent 6b629cc2ca
commit 92671daec5
9 changed files with 251 additions and 33 deletions
+8
View File
@@ -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
+17 -4
View File
@@ -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.
+3
View File
@@ -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"
+29
View File
@@ -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}>")
+16
View File
@@ -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
+70
View File
@@ -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
+25
View File
@@ -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
+1
View File
@@ -29,6 +29,7 @@ endif()
target_link_libraries(sleigh-lift PRIVATE
sleigh::sla
sleigh::decomp
sleigh::support
)
if(SLEIGH_ENABLE_INSTALL)
+82 -29
View File
@@ -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;