Replaced post-link finalizer with pre-link patchcompile

This commit is contained in:
York Jasper Niebuhr 2026-04-04 13:56:17 +02:00
parent 84b1bc9b4e
commit 6b96c725ee
17 changed files with 341 additions and 471 deletions

View File

@ -6,6 +6,6 @@ set(CMAKE_CXX_COMPILER g++-16 CACHE FILEPATH "C++ compiler" FORCE)
project(SelfPatchSLR LANGUAGES C CXX) project(SelfPatchSLR LANGUAGES C CXX)
add_subdirectory(pinpoint) add_subdirectory(pinpoint)
add_subdirectory(finalize) add_subdirectory(patchcompile)
add_subdirectory(selfpatch) add_subdirectory(selfpatch)
add_subdirectory(subject) add_subdirectory(subject)

View File

@ -1,5 +0,0 @@
find_package(LIEF REQUIRED)
add_executable(spslr_finalize finalize.cpp accumulation.cpp symbol_collection.cpp)
target_link_libraries(spslr_finalize PRIVATE LIEF::LIEF)
set_target_properties(spslr_finalize PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO)

View File

@ -1,293 +0,0 @@
#include <iostream>
#include <string>
#include <vector>
#include <cstdint>
#include <getopt.h>
#include <LIEF/LIEF.hpp>
#include <LIEF/ELF/Builder.hpp>
#include "accumulation.h"
#include "symbol_collection.h"
#include <spslr_program.h>
/*
Notes:
Datapins for same var/symbol are randomized in order of their level, from bottom of nest to top
Between CUs, types with the same name HAVE TO HAVE the same layout -> randomized together
To begin with, anonymous types are not allowed for randomization (later solved with hash(type) instead of name)!
*/
using namespace LIEF::ELF;
static bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& program);
int main(int argc, char** argv) {
static option long_options[] = {
{ "help", no_argument, 0, 0 },
{ "bin", required_argument, 0, 0 },
{ "out", required_argument, 0, 0 },
{ "strip", no_argument, 0, 0 },
{ 0, 0, 0, 0 }
};
int option_index = 0;
int c;
std::vector<std::string> spslr_files;
std::string bin_file, out_file;
while ((c = getopt_long(argc, argv, "hb:o:", long_options, &option_index)) == 0) {
const option& opt = long_options[option_index];
std::string optname { opt.name };
if (optname == "help") {
std::cout << "To use spslr_finalize, supply these 3 arguments:" << std::endl;
std::cout << " --bin=<file> (the binary compiled with spslr_pinpoint to be finalized)" << std::endl;
std::cout << " --out=<file> (the finalized binary file to be written)" << std::endl;
std::cout << " <file>... (one or more .spslr metadata files)" << std::endl;
return 0;
} else if (optname == "bin") {
bin_file = std::string{ optarg };
} else if (optname == "out") {
out_file = std::string{ optarg };
} else if (optname == "strip") {
std::cerr << "Symbol stripping (--strip) is not yet implemented!" << std::endl;
return 1;
} else {
std::cerr << "Invalid option, try \"--help\"!" << std::endl;
return 1;
}
}
if (bin_file.empty()) {
std::cerr << "Missing input file path, supply it via --bin=<file>!" << std::endl;
return 1;
}
if (out_file.empty()) {
std::cerr << "Missing output file path, supply it via --out=<file>!" << std::endl;
return 1;
}
for (int i = optind; i < argc; ++i)
spslr_files.emplace_back(argv[i]);
if (spslr_files.empty()) {
std::cerr << "Missing spslr files! Pass one or more .spslr files as positional arguments." << std::endl;
return 1;
}
if (!accumulate(spslr_files)) {
std::cerr << "Failed to accumulate data from spslr directory!" << std::endl;
return 1;
}
std::cout << "Gathered a total of " << targets.size() << " distinct targets from "
<< units.size() << " compilation units!" << std::endl;
std::unique_ptr<Binary> bin = Parser::parse(bin_file);
if (!bin) {
std::cerr << "Failed to parse binary \"" << bin_file << "\"!" << std::endl;
return 1;
}
if (!collect_symbols(bin)) {
std::cerr << "Failed to collect symbols from \"" << bin_file << "\"!" << std::endl;
return 1;
}
if (!associate_symbols()) {
std::cerr << "Failed to associate symbols with accumulated data!" << std::endl;
return 1;
}
// Find __spslr_program as anchor of relative addressing in patcher program
if (!global_syms.symbols.contains("__spslr_program")) {
std::cerr << "Unable to locate \"__spslr_program\"!" << std::endl;
return 1;
}
uint64_t spslr_program_ptr_address = global_syms.symbols.at("__spslr_program");
// Serialize patcher program
std::vector<uint8_t> patcher_program;
if (!assemble_patcher_program(spslr_program_ptr_address, patcher_program)) {
std::cerr << "Failed to assemble patcher program!" << std::endl;
return 1;
}
std::cout << "Generated patcher program of 0x" << std::hex << patcher_program.size()
<< std::dec << " bytes!" << std::endl;
// Add new section ".spslr" with patcher program
uint64_t page_align = 0x1000;
for (const auto& seg : bin->segments()) {
if (seg.type() == Segment::TYPE::LOAD) {
if (seg.alignment() > page_align) page_align = seg.alignment();
}
}
uint64_t max_end_vaddr = 0;
for (const auto& seg : bin->segments()) {
if (seg.type() == Segment::TYPE::LOAD) {
uint64_t endv = seg.virtual_address() + seg.virtual_size();
if (endv > max_end_vaddr) max_end_vaddr = endv;
}
}
uint64_t new_vaddr = max_end_vaddr;
if (new_vaddr % page_align != 0)
new_vaddr += page_align - (new_vaddr % page_align);
std::cout << "Adding patcher program at 0x" << std::hex << new_vaddr << std::dec << std::endl;
Segment new_seg;
new_seg.type(Segment::TYPE::LOAD);
new_seg.flags(Segment::FLAGS::R); //(ELF_SEGMENT_FLAGS::PF_R);
new_seg.alignment(page_align);
new_seg.file_offset(0); // Writer decides where to put it
new_seg.virtual_address(new_vaddr);
new_seg.content(patcher_program);
new_seg.virtual_size(patcher_program.size());
bin->add(new_seg);
// Set __spslr_program to (new_vaddr - &__spslr_program)
for (Segment& seg : bin->segments()) {
uint64_t start = seg.virtual_address();
uint64_t end_mem = start + seg.virtual_size();
if (spslr_program_ptr_address >= start && spslr_program_ptr_address < end_mem) {
uint64_t offset_within_seg = spslr_program_ptr_address - start;
uint64_t min_needed = offset_within_seg + sizeof(uint64_t);
if (min_needed > seg.physical_size())
seg.physical_size(min_needed);
break;
}
}
uint64_t relative_new_vaddr = new_vaddr - spslr_program_ptr_address;
std::vector<uint8_t> program_ptr_bytes;
program_ptr_bytes.resize(8);
std::memcpy(program_ptr_bytes.data(), &relative_new_vaddr, 8);
bin->patch_address(spslr_program_ptr_address, program_ptr_bytes);
// Output final program
Builder builder{ *bin };
builder.build();
builder.write(out_file);
std::cout << "Wrote final binary to \"" << out_file << "\"" << std::endl;
return 0;
}
bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& program) {
uint8_t buf[sizeof(SPSLR_INST)];
auto append_inst = [&](const SPSLR_INST& inst) -> bool {
int n = spslr_inst_dump(&inst, buf);
if (n < 0) {
std::cerr << "Failed to dump patcher instruction!" << std::endl;
return false;
}
std::size_t program_ptr = program.size();
program.resize(program.size() + n);
std::memcpy(program.data() + program_ptr, buf, n);
return true;
};
SPSLR_INST inst;
// Dump all targets, their fields and command randomization
for (const auto& [tuid, target] : targets) {
inst.opcode = SPSLR_TARGET;
inst.op0.target_uid = tuid;
inst.op1.target_size = target.size;
inst.op2.target_fieldcnt = target.fields.size();
if (!append_inst(inst))
return false;
for (const auto& [foff, field] : target.fields) {
inst.opcode = SPSLR_FIELD;
inst.op0.field_offset = field.offset;
inst.op1.field_size = field.size;
inst.op2.field_alignment = field.alignment;
inst.op3.field_flags = field.flags; // Note -> This should be done explicitely with SPSLR_FLAG_FIELD_FIXED
if (!append_inst(inst))
return false;
}
inst.opcode = SPSLR_RANDOMIZE;
inst.op0.randomize_target = tuid;
if (!append_inst(inst))
return false;
}
// Dump ipins with addresses relative to vaddr_pivot
auto append_ipin = [&](const CU& cu, const IPIN& ipin) -> bool {
inst.opcode = SPSLR_IPATCH;
inst.op0.ipatch_ptr = ipin.hit->vaddr - vaddr_pivot;
inst.op1.ipatch_size = ipin.imm_size;
inst.op2.ipatch_target = cu.local_targets.at(ipin.local_target);
std::cout << "IPIN found at 0x" << std::hex << ipin.hit->vaddr << std::dec << std::endl;
if (!targets.at(inst.op2.ipatch_target).fields.contains(ipin.field_offset))
return false;
inst.op3.ipatch_field = targets.at(inst.op2.ipatch_target).fields.at(ipin.field_offset).idx;
return append_inst(inst);
};
// Dump dpins with addresses relative to vaddr_pivot and components ordered by level
auto append_dpin = [&](const CU& cu, const DPIN& dpin) -> bool {
std::list<DPIN::COMPONENT> sorted_components = dpin.components;
sorted_components.sort([](const DPIN::COMPONENT& a, const DPIN::COMPONENT& b) {
return a.level > b.level; // Descending order
});
for (const DPIN::COMPONENT& comp : sorted_components) {
inst.opcode = SPSLR_DPATCH;
inst.op0.dpatch_ptr = dpin.hit->vaddr + comp.offset - vaddr_pivot;
inst.op1.dpatch_target = cu.local_targets.at(comp.target);
std::cout << "DPIN found at 0x" << std::hex << dpin.hit->vaddr << std::dec << std::endl;
if (!append_inst(inst))
return false;
}
return true;
};
// Dump all pins
for (const auto& [cu_uid, cu] : units) {
for (const auto& [ipin_sym, ipin] : cu.ipins) {
if (!append_ipin(cu, ipin))
return false;
}
for (const auto& [dpin_sym, dpin] : cu.dpins) {
if (!append_dpin(cu, dpin))
return false;
}
}
// Exit patcher program
inst.opcode = SPSLR_EXIT;
if (!append_inst(inst))
return false;
return true;
}

View File

@ -1,65 +0,0 @@
#include "symbol_collection.h"
#include "accumulation.h"
#include <iostream>
using namespace LIEF::ELF;
GlobalSymtab global_syms;
bool collect_symbols(const std::unique_ptr<LIEF::ELF::Binary>& bin) {
if (!bin)
return false;
global_syms.symbols.clear();
for (const Symbol& sym : bin->symbols()) {
if (sym.name().empty())
continue;
if (sym.type() != Symbol::TYPE::OBJECT &&
sym.type() != Symbol::TYPE::NOTYPE)
continue;
if (sym.binding() != Symbol::BINDING::GLOBAL)
continue;
VADDR vaddr = sym.value();
if (global_syms.symbols.contains(sym.name())) {
std::cerr << "Duplicate global symbol \"" << sym.name() << "\"!" << std::endl;
return false;
}
global_syms.symbols.emplace(sym.name(), vaddr);
}
return true;
}
bool associate_symbols() {
for (auto& [cu_uid, cu] : units) {
for (auto& [ipin_sym, ipin] : cu.ipins) {
if (!global_syms.symbols.contains(ipin_sym)) {
std::cerr << "Unable to locate global ipin symbol \"" << ipin_sym << "\"" << std::endl;
return false;
}
ipin.hit.emplace();
ipin.hit->vaddr = global_syms.symbols.at(ipin_sym);
}
for (auto& [dpin_sym, dpin] : cu.dpins) {
if (!global_syms.symbols.contains(dpin_sym)) {
std::cerr << "Unable to locate global dpin symbol \"" << dpin_sym << "\"" << std::endl;
return false;
}
dpin.hit.emplace();
dpin.hit->vaddr = global_syms.symbols.at(dpin_sym);
}
}
return true;
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <LIEF/LIEF.hpp>
#include <string>
#include <unordered_map>
#include <cstdint>
using VADDR = uint64_t;
struct GlobalSymtab {
std::unordered_map<std::string, VADDR> symbols;
};
extern GlobalSymtab global_syms;
bool collect_symbols(const std::unique_ptr<LIEF::ELF::Binary>& bin);
bool associate_symbols();

View File

@ -0,0 +1,2 @@
add_executable(spslr_patchcompile patchcompile.cpp accumulation.cpp emit.cpp)
set_target_properties(spslr_patchcompile PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO)

View File

@ -37,8 +37,6 @@ static bool global_target_field_cmp(const TARGET& a, const TARGET& b) {
} }
static bool global_target_cmp(const TARGET& a, const TARGET& b) { static bool global_target_cmp(const TARGET& a, const TARGET& b) {
// Note -> Later, use hash of type (generated by pinpoint plugin)
if (a.name != b.name) if (a.name != b.name)
return false; return false;

View File

@ -9,23 +9,13 @@
#include <vector> #include <vector>
struct IPIN { struct IPIN {
struct HIT {
uint64_t vaddr;
};
std::string symbol; std::string symbol;
std::size_t local_target; std::size_t local_target;
std::size_t field_offset; std::size_t field_offset;
std::size_t imm_size; std::size_t imm_size;
std::optional<HIT> hit;
}; };
struct DPIN { struct DPIN {
struct HIT {
uint64_t vaddr;
};
struct COMPONENT { struct COMPONENT {
std::size_t offset; std::size_t offset;
std::size_t level; std::size_t level;
@ -34,8 +24,6 @@ struct DPIN {
std::string symbol; std::string symbol;
std::list<COMPONENT> components; std::list<COMPONENT> components;
std::optional<HIT> hit;
}; };
struct FIELD { struct FIELD {

144
patchcompile/emit.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "emit.h"
#include "accumulation.h"
#include <spslr_program.h>
static bool emit_header(std::ostream& out);
static bool emit_target_header(std::ostream& out, std::size_t uid, std::size_t size, std::size_t fieldcnt);
static bool emit_target_field(std::ostream& out, std::size_t offset, std::size_t size, std::size_t alignment, std::size_t flags);
static bool emit_target_randomize(std::ostream& out, std::size_t uid);
static bool emit_target(std::ostream& out, std::size_t uid, const TARGET& target);
static bool emit_ipin(std::ostream& out, const CU& cu, const IPIN& ipin);
static bool emit_dpin_component(std::ostream& out, std::size_t target, const std::string& sym, std::size_t offset);
static bool emit_dpin(std::ostream& out, const CU& cu, const DPIN& dpin);
static bool emit_exit(std::ostream& out);
bool emit_patcher_program_asm(std::ostream& out) {
if (!emit_header(out))
return false;
// Dump all targets
for (const auto& [uid, target] : targets) {
if (!emit_target(out, uid, target))
return false;
}
// Dump all pins
for (const auto& [uid, cu] : units) {
for (const auto& [sym, pin] : cu.ipins) {
if (!emit_ipin(out, cu, pin))
return false;
}
for (const auto& [sym, pin] : cu.dpins) {
if (!emit_dpin(out, cu, pin))
return false;
}
}
// Exit patcher program
if (!emit_exit(out))
return false;
return true;
}
bool emit_header(std::ostream& out) {
out << ".section .spslr,\"a\",@progbits\n";
out << ".balign 1\n";
out << ".globl __spslr_program\n";
out << ".type __spslr_program, @object\n";
out << "__spslr_program:\n";
return !!out;
}
bool emit_target_header(std::ostream& out, std::size_t uid, std::size_t size, std::size_t fieldcnt) {
out << "\t.byte " << OPCODE_SPSLR_TARGET << "\n";
out << "\t.long " << uid << "\n";
out << "\t.long " << size << "\n";
out << "\t.long " << fieldcnt << "\n";
return !!out;
}
bool emit_target_field(std::ostream& out, std::size_t offset, std::size_t size, std::size_t alignment, std::size_t flags) {
out << "\t.byte " << OPCODE_SPSLR_FIELD << "\n";
out << "\t.long " << offset << "\n";
out << "\t.long " << size << "\n";
out << "\t.long " << alignment << "\n";
out << "\t.long " << flags << "\n";
return !!out;
}
bool emit_target_randomize(std::ostream& out, std::size_t uid) {
out << "\t.byte " << OPCODE_SPSLR_RANDOMIZE << "\n";
out << "\t.long " << uid << "\n";
return !!out;
}
bool emit_target(std::ostream& out, std::size_t uid, const TARGET& target) {
if (!emit_target_header(out, uid, target.size, target.fields.size()))
return false;
for (const auto& [off, field] : target.fields) {
// Note -> Might want to do explicit flag mapping from pinpoint to selfpatch
if (!emit_target_field(out, field.offset, field.size, field.alignment, field.flags))
return false;
}
if (!emit_target_randomize(out, uid))
return false;
return true;
}
bool emit_ipin(std::ostream& out, const CU& cu, const IPIN& ipin) {
std::size_t global_target = cu.local_targets.at(ipin.local_target);
if (!targets.contains(global_target))
return false;
const TARGET& target = targets.at(global_target);
if (!target.fields.contains(ipin.field_offset))
return false;
const FIELD& field = target.fields.at(ipin.field_offset);
out << "\t.byte " << OPCODE_SPSLR_IPATCH << "\n";
out << "\t.quad " << ipin.symbol << "\n";
out << "\t.long " << ipin.imm_size << "\n";
out << "\t.long " << global_target << "\n";
out << "\t.long " << field.idx << "\n";
return !!out;
}
bool emit_dpin_component(std::ostream& out, std::size_t target, const std::string& sym, std::size_t offset) {
out << "\t.byte " << OPCODE_SPSLR_DPATCH << "\n";
out << "\t.quad " << sym;
if (offset != 0)
out << " + " << offset;
out << "\n";
out << "\t.long " << target << "\n";
return !!out;
}
bool emit_dpin(std::ostream& out, const CU& cu, const DPIN& dpin) {
std::list<DPIN::COMPONENT> sorted_components = dpin.components;
sorted_components.sort([](const DPIN::COMPONENT& a, const DPIN::COMPONENT& b) {
return a.level > b.level; // Descending order to handle deeper nested first
});
for (const DPIN::COMPONENT& component : sorted_components) {
std::size_t global_target = cu.local_targets.at(component.target);
if (!emit_dpin_component(out, global_target, dpin.symbol, component.offset))
return false;
}
return true;
}
bool emit_exit(std::ostream& out) {
out << "\t.byte " << OPCODE_SPSLR_EXIT << "\n";
return !!out;
}

4
patchcompile/emit.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include <fstream>
bool emit_patcher_program_asm(std::ostream& out);

View File

@ -0,0 +1,93 @@
#include <iostream>
#include <string>
#include <vector>
#include <cstdint>
#include <getopt.h>
#include <filesystem>
#include "accumulation.h"
#include "emit.h"
/*
Notes:
Datapins for same var/symbol are randomized in order of their level, from bottom of nest to top
Between CUs, types with the same name HAVE TO HAVE the same layout -> randomized together
*/
int main(int argc, char** argv) {
static option long_options[] = {
{ "help", no_argument, 0, 0 },
{ "out", required_argument, 0, 0 },
{ 0, 0, 0, 0 }
};
int option_index = 0;
int c;
std::vector<std::string> spslr_files;
std::string out_file;
while ((c = getopt_long(argc, argv, "h:o:", long_options, &option_index)) == 0) {
const option& opt = long_options[option_index];
std::string optname { opt.name };
if (optname == "help") {
std::cout << "To use spslr_patchcompile, supply these arguments:" << std::endl;
std::cout << " --out=<file> (the compiled asm file to be written)" << std::endl;
std::cout << " <file>... (one or more .spslr metadata files)" << std::endl;
return 0;
} else if (optname == "out") {
out_file = std::string{ optarg };
} else {
std::cerr << "Invalid option, try \"--help\"!" << std::endl;
return 1;
}
}
if (out_file.empty()) {
std::cerr << "Missing output file path, supply it via --out=<file>!" << std::endl;
return 1;
}
for (int i = optind; i < argc; ++i)
spslr_files.emplace_back(argv[i]);
if (spslr_files.empty()) {
std::cerr << "Missing spslr files! Pass one or more .spslr files as positional arguments." << std::endl;
return 1;
}
if (!accumulate(spslr_files)) {
std::cerr << "Failed to accumulate data from spslr directory!" << std::endl;
return 1;
}
std::cout << "Gathered a total of " << targets.size() << " distinct targets from "
<< units.size() << " compilation units!" << std::endl;
std::filesystem::path out_path { out_file };
if (out_path.has_parent_path()) {
std::error_code ec;
std::filesystem::create_directories(out_path.parent_path(), ec);
if (ec) {
std::cerr << "failed to create output directory '"
<< out_path.parent_path().string()
<< "': " << ec.message() << "\n";
return 1;
}
}
std::ofstream out(out_path);
if (!out) {
std::cerr << "Failed to open output file!" << std::endl;
return 1;
}
if (!emit_patcher_program_asm(out)) {
std::cerr << "Failed to write emit patcher program!" << std::endl;
return 1;
}
return 0;
}

View File

@ -271,6 +271,7 @@ static bool field_map_add(std::map<std::size_t, TargetType::Field>& map, const T
TargetType::Field tmp_field; TargetType::Field tmp_field;
tmp_field.offset = field.offset; tmp_field.offset = field.offset;
tmp_field.size = (field.size == 0 ? 1 : field.size); tmp_field.size = (field.size == 0 ? 1 : field.size);
tmp_field.alignment = (field.alignment == 0 ? 1 : field.alignment);
tmp_field.flags = (field.size == 0 ? TargetType::Field::FLAG_DANGEROUS : 0) | field.flags; tmp_field.flags = (field.size == 0 ? TargetType::Field::FLAG_DANGEROUS : 0) | field.flags;
// Overlaps are dangerous -> remove and integrate into member // Overlaps are dangerous -> remove and integrate into member
@ -289,6 +290,7 @@ static bool field_map_add(std::map<std::size_t, TargetType::Field>& map, const T
tmp_field.flags |= (existing_field.flags | TargetType::Field::FLAG_DANGEROUS); tmp_field.flags |= (existing_field.flags | TargetType::Field::FLAG_DANGEROUS);
tmp_field.offset = combined_offset; tmp_field.offset = combined_offset;
tmp_field.alignment = std::min(tmp_field.alignment, existing_field.alignment);
tmp_field.size = combined_size; tmp_field.size = combined_size;
// Erase overlapping member // Erase overlapping member

View File

@ -1,3 +0,0 @@
Move patcher generation to pre-link stage
- Aggregate meta data files
- Generate patcher object file than links against symbols

View File

@ -5,4 +5,4 @@ target_include_directories(spslr_selfpatch PUBLIC
$<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:include>
) )
target_include_directories(spslr_finalize PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) # For spslr_program.h target_include_directories(spslr_patchcompile PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) # For spslr_program.h

View File

@ -6,18 +6,7 @@
#include "targets.h" #include "targets.h"
#include "patcher.h" #include "patcher.h"
const uint8_t* __spslr_program = NULL; extern const uint8_t __spslr_program[];
static void* spslr_ptr_absolute(uint64_t relative) {
// To allow ASLR, all patcher addresses are relative to &__spslr_program
return ((uint8_t*)&__spslr_program) + relative;
}
static void spslr_init_program_ptr() {
// Finalizer patches __spslr_program to be the relative offset from &__spslr_program to the program
uint64_t relative = (uint64_t)__spslr_program;
__spslr_program = (const uint8_t*)spslr_ptr_absolute(relative);
}
static int spslr_do(const struct SPSLR_INST* inst) { static int spslr_do(const struct SPSLR_INST* inst) {
if (!inst) if (!inst)
@ -27,18 +16,18 @@ static int spslr_do(const struct SPSLR_INST* inst) {
static uint32_t pending_fields_target = 0; static uint32_t pending_fields_target = 0;
if (pending_fields) { if (pending_fields) {
if (inst->opcode != SPSLR_FIELD) { if (inst->op != SPSLR_FIELD) {
fprintf(stderr, "spslr_do encountered non field instruction where a field instruction was expected\n"); fprintf(stderr, "spslr_do encountered non field instruction where a field instruction was expected\n");
return -1; return -1;
} }
pending_fields--; pending_fields--;
} else if (inst->opcode == SPSLR_FIELD) { } else if (inst->op == SPSLR_FIELD) {
fprintf(stderr, "spslr_do encountered field instruction where none was expected\n"); fprintf(stderr, "spslr_do encountered field instruction where none was expected\n");
return -1; return -1;
} }
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
pending_fields = inst->op2.target_fieldcnt; pending_fields = inst->op2.target_fieldcnt;
pending_fields_target = inst->op0.target_uid; pending_fields_target = inst->op0.target_uid;
@ -49,27 +38,22 @@ static int spslr_do(const struct SPSLR_INST* inst) {
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
return spslr_randomize(inst->op0.randomize_target); return spslr_randomize(inst->op0.randomize_target);
case SPSLR_IPATCH: case SPSLR_IPATCH:
return spslr_ipatch(spslr_ptr_absolute(inst->op0.ipatch_ptr), inst->op1.ipatch_size, return spslr_ipatch((void*)(uintptr_t)inst->op0.ipatch_ptr, inst->op1.ipatch_size,
inst->op2.ipatch_target, inst->op3.ipatch_field); inst->op2.ipatch_target, inst->op3.ipatch_field);
case SPSLR_DPATCH: case SPSLR_DPATCH:
return spslr_dpatch(spslr_ptr_absolute(inst->op0.dpatch_ptr), inst->op1.dpatch_target); return spslr_dpatch((void*)(uintptr_t)inst->op0.dpatch_ptr, inst->op1.dpatch_target);
default: default:
return -1; return -1;
} }
} }
void spslr_selfpatch() { void spslr_selfpatch() {
if (!__spslr_program) { const uint8_t* pc = __spslr_program;
fprintf(stderr, "spslr_selfpatch has no patcher program (finalize this binary to use SPSLR)\n");
return;
}
spslr_init_program_ptr();
int sz; int sz;
struct SPSLR_INST inst; struct SPSLR_INST inst;
while ((sz = spslr_inst_load(&inst, __spslr_program)) > 0) { while ((sz = spslr_inst_load(&inst, pc)) > 0) {
if (inst.opcode == SPSLR_EXIT) { if (inst.op == SPSLR_EXIT) {
spslr_targets_clear(); spslr_targets_clear();
return; return;
} }
@ -79,7 +63,7 @@ void spslr_selfpatch() {
exit(1); exit(1);
} }
__spslr_program += sz; pc += sz;
} }
fprintf(stderr, "spslr_selfpatch encountered invalid instruction\n"); fprintf(stderr, "spslr_selfpatch encountered invalid instruction\n");

View File

@ -6,7 +6,14 @@
#define SPSLR_FLAG_FIELD_FIXED 1 #define SPSLR_FLAG_FIELD_FIXED 1
enum SPSLR_OPCODE { #define OPCODE_SPSLR_TARGET 1
#define OPCODE_SPSLR_FIELD 2
#define OPCODE_SPSLR_RANDOMIZE 3
#define OPCODE_SPSLR_IPATCH 4
#define OPCODE_SPSLR_DPATCH 5
#define OPCODE_SPSLR_EXIT 6
enum SPSLR_OP {
SPSLR_TARGET, // uid, size, field count SPSLR_TARGET, // uid, size, field count
SPSLR_FIELD, // offset, size, alignment, flags SPSLR_FIELD, // offset, size, alignment, flags
SPSLR_RANDOMIZE, // target uid SPSLR_RANDOMIZE, // target uid
@ -16,7 +23,7 @@ enum SPSLR_OPCODE {
}; };
struct SPSLR_INST { struct SPSLR_INST {
enum SPSLR_OPCODE opcode; enum SPSLR_OP op;
union { union {
uint32_t target_uid; uint32_t target_uid;
@ -49,19 +56,19 @@ static inline int spslr_inst_dump_opcode(const struct SPSLR_INST* inst, uint8_t*
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
*buf = 1; return 1; *buf = OPCODE_SPSLR_TARGET; return 1;
case SPSLR_FIELD: case SPSLR_FIELD:
*buf = 2; return 1; *buf = OPCODE_SPSLR_FIELD; return 1;
case SPSLR_RANDOMIZE: case SPSLR_RANDOMIZE:
*buf = 3; return 1; *buf = OPCODE_SPSLR_RANDOMIZE; return 1;
case SPSLR_IPATCH: case SPSLR_IPATCH:
*buf = 4; return 1; *buf = OPCODE_SPSLR_IPATCH; return 1;
case SPSLR_DPATCH: case SPSLR_DPATCH:
*buf = 5; return 1; *buf = OPCODE_SPSLR_DPATCH; return 1;
case SPSLR_EXIT: case SPSLR_EXIT:
*buf = 6; return 1; *buf = OPCODE_SPSLR_EXIT; return 1;
default: default:
return -1; return -1;
} }
@ -72,18 +79,18 @@ static inline int spslr_inst_load_opcode(struct SPSLR_INST* inst, const uint8_t*
return -1; return -1;
switch (*buf) { switch (*buf) {
case 1: case OPCODE_SPSLR_TARGET:
inst->opcode = SPSLR_TARGET; return 1; inst->op = SPSLR_TARGET; return 1;
case 2: case OPCODE_SPSLR_FIELD:
inst->opcode = SPSLR_FIELD; return 1; inst->op = SPSLR_FIELD; return 1;
case 3: case OPCODE_SPSLR_RANDOMIZE:
inst->opcode = SPSLR_RANDOMIZE; return 1; inst->op = SPSLR_RANDOMIZE; return 1;
case 4: case OPCODE_SPSLR_IPATCH:
inst->opcode = SPSLR_IPATCH; return 1; inst->op = SPSLR_IPATCH; return 1;
case 5: case OPCODE_SPSLR_DPATCH:
inst->opcode = SPSLR_DPATCH; return 1; inst->op = SPSLR_DPATCH; return 1;
case 6: case OPCODE_SPSLR_EXIT:
inst->opcode = SPSLR_EXIT; return 1; inst->op = SPSLR_EXIT; return 1;
default: default:
return -1; return -1;
} }
@ -96,7 +103,7 @@ static inline int spslr_inst_dump_op0(const struct SPSLR_INST* inst, uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
DUMP_OP_RET(inst->op0.target_uid); DUMP_OP_RET(inst->op0.target_uid);
case SPSLR_FIELD: case SPSLR_FIELD:
@ -118,7 +125,7 @@ static inline int spslr_inst_load_op0(struct SPSLR_INST* inst, const uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
LOAD_OP_RET(inst->op0.target_uid); LOAD_OP_RET(inst->op0.target_uid);
case SPSLR_FIELD: case SPSLR_FIELD:
@ -140,7 +147,7 @@ static inline int spslr_inst_dump_op1(const struct SPSLR_INST* inst, uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
DUMP_OP_RET(inst->op1.target_size); DUMP_OP_RET(inst->op1.target_size);
case SPSLR_FIELD: case SPSLR_FIELD:
@ -161,7 +168,7 @@ static inline int spslr_inst_load_op1(struct SPSLR_INST* inst, const uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
LOAD_OP_RET(inst->op1.target_size); LOAD_OP_RET(inst->op1.target_size);
case SPSLR_FIELD: case SPSLR_FIELD:
@ -182,7 +189,7 @@ static inline int spslr_inst_dump_op2(const struct SPSLR_INST* inst, uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
DUMP_OP_RET(inst->op2.target_fieldcnt); DUMP_OP_RET(inst->op2.target_fieldcnt);
case SPSLR_FIELD: case SPSLR_FIELD:
@ -202,7 +209,7 @@ static inline int spslr_inst_load_op2(struct SPSLR_INST* inst, const uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_TARGET: case SPSLR_TARGET:
LOAD_OP_RET(inst->op2.target_fieldcnt); LOAD_OP_RET(inst->op2.target_fieldcnt);
case SPSLR_FIELD: case SPSLR_FIELD:
@ -222,7 +229,7 @@ static inline int spslr_inst_dump_op3(const struct SPSLR_INST* inst, uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_FIELD: case SPSLR_FIELD:
DUMP_OP_RET(inst->op3.field_flags); DUMP_OP_RET(inst->op3.field_flags);
case SPSLR_IPATCH: case SPSLR_IPATCH:
@ -241,7 +248,7 @@ static inline int spslr_inst_load_op3(struct SPSLR_INST* inst, const uint8_t* bu
if (!inst || !buf) if (!inst || !buf)
return -1; return -1;
switch (inst->opcode) { switch (inst->op) {
case SPSLR_FIELD: case SPSLR_FIELD:
LOAD_OP_RET(inst->op3.field_flags); LOAD_OP_RET(inst->op3.field_flags);
case SPSLR_IPATCH: case SPSLR_IPATCH:

View File

@ -1,37 +1,68 @@
add_executable(subject main.c second.c sub/second.c) set(SUBJECT_SRC
target_include_directories(subject PRIVATE .) main.c
add_dependencies(subject spslr_pinpoint spslr_finalize spslr_selfpatch) second.c
target_link_libraries(subject PRIVATE spslr_selfpatch) sub/second.c
)
set(SUBJECT_SPSLR_METADIR "${CMAKE_CURRENT_BINARY_DIR}/spslr") set(SUBJECT_SPSLR_METADIR "${CMAKE_CURRENT_BINARY_DIR}/spslr")
set(SUBJECT_SPSLR_SRCROOT "${CMAKE_CURRENT_SOURCE_DIR}") set(SUBJECT_SPSLR_SRCROOT "${CMAKE_CURRENT_SOURCE_DIR}")
target_compile_options(subject PRIVATE -O1 -fplugin=$<TARGET_FILE:spslr_pinpoint> -fdump-tree-separate_offset -fdump-tree-asm_offset
-fplugin-arg-spslr_pinpoint-srcroot=${SUBJECT_SPSLR_SRCROOT}
-fplugin-arg-spslr_pinpoint-metadir=${SUBJECT_SPSLR_METADIR})
file(MAKE_DIRECTORY "${SUBJECT_SPSLR_METADIR}") file(MAKE_DIRECTORY "${SUBJECT_SPSLR_METADIR}")
# Apply spslr_finalizer to subject add_library(subject_objs OBJECT ${SUBJECT_SRC})
target_include_directories(subject_objs PRIVATE .)
target_link_libraries(subject_objs PRIVATE spslr_selfpatch)
add_dependencies(subject_objs spslr_pinpoint)
get_target_property(SUBJECT_SOURCES subject SOURCES) target_compile_options(subject_objs PRIVATE
-O1
-fplugin=$<TARGET_FILE:spslr_pinpoint>
-fplugin-arg-spslr_pinpoint-srcroot=${SUBJECT_SPSLR_SRCROOT}
-fplugin-arg-spslr_pinpoint-metadir=${SUBJECT_SPSLR_METADIR}
)
set(SUBJECT_SPSLR_FILES "") set(SUBJECT_SPSLR_FILES "")
foreach(src IN LISTS SUBJECT_SOURCES) foreach(src IN LISTS SUBJECT_SRC)
get_filename_component(abs_src "${src}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") get_filename_component(abs_src "${src}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
file(RELATIVE_PATH rel_src "${SUBJECT_SPSLR_SRCROOT}" "${abs_src}") file(RELATIVE_PATH rel_src "${SUBJECT_SPSLR_SRCROOT}" "${abs_src}")
file(TO_CMAKE_PATH "${rel_src}" rel_src) file(TO_CMAKE_PATH "${rel_src}" rel_src)
list(APPEND SUBJECT_SPSLR_FILES "${SUBJECT_SPSLR_METADIR}/${rel_src}.spslr") list(APPEND SUBJECT_SPSLR_FILES "${SUBJECT_SPSLR_METADIR}/${rel_src}.spslr")
endforeach() endforeach()
set(SUBJECT_SPSLR_ASM "${CMAKE_CURRENT_BINARY_DIR}/subject_spslr_program.S")
set(SUBJECT_SPSLR_OBJ "${CMAKE_CURRENT_BINARY_DIR}/subject_spslr_program.o")
add_custom_command( add_custom_command(
TARGET subject OUTPUT "${SUBJECT_SPSLR_ASM}"
POST_BUILD COMMAND $<TARGET_FILE:spslr_patchcompile>
COMMAND $<TARGET_FILE:spslr_finalize> --out=${SUBJECT_SPSLR_ASM}
--bin=$<TARGET_FILE:subject>
--out=${CMAKE_BINARY_DIR}/subject_final
${SUBJECT_SPSLR_FILES} ${SUBJECT_SPSLR_FILES}
COMMAND chmod +x ${CMAKE_BINARY_DIR}/subject_final DEPENDS
spslr_patchcompile
$<TARGET_OBJECTS:subject_objs>
VERBATIM VERBATIM
) )
add_custom_command(
OUTPUT "${SUBJECT_SPSLR_OBJ}"
COMMAND ${CMAKE_C_COMPILER}
-c "${SUBJECT_SPSLR_ASM}"
-o "${SUBJECT_SPSLR_OBJ}"
DEPENDS "${SUBJECT_SPSLR_ASM}"
VERBATIM
)
set_source_files_properties("${SUBJECT_SPSLR_OBJ}" PROPERTIES
GENERATED TRUE
EXTERNAL_OBJECT TRUE
)
add_executable(subject
$<TARGET_OBJECTS:subject_objs>
"${SUBJECT_SPSLR_OBJ}"
)
target_include_directories(subject PRIVATE .)
target_link_libraries(subject PRIVATE spslr_selfpatch)
add_dependencies(subject spslr_selfpatch)