Making labels globally unique

This commit is contained in:
York Jasper Niebuhr 2026-04-03 20:35:11 +02:00
parent ce7e75ecc9
commit 273bf2e461
15 changed files with 152 additions and 257 deletions

View File

@ -9,7 +9,6 @@ namespace fs = std::filesystem;
static std::size_t next_global_target_uid = 0;
std::unordered_map<std::size_t, TARGET> targets;
std::unordered_map<std::string, DPIN> global_dpins;
std::unordered_map<std::string, CU> units;
static bool global_target_field_cmp(const TARGET& a, const TARGET& b) {
@ -54,8 +53,8 @@ ipin <label> <target uid> <field offset> <immediate size>
ipin <label> <target uid> <field offset> <immediate size>
ipin <label> <target uid> <field offset> <immediate size>
...
dpin <local/global> <symbol> <offset> <level> <target uid>
dpin <local/global> <symbol> <offset> <level> <target uid>
dpin <symbol> <offset> <level> <target uid>
dpin <symbol> <offset> <level> <target uid>
...
*/
static bool accumulate_file(const fs::path& path) {
@ -163,6 +162,11 @@ static bool accumulate_file(const fs::path& path) {
return false;
}
if (!cu.local_targets.contains(ipin.local_target)) {
std::cerr << "Ipin references target that has not yet been parsed!" << std::endl;
return false;
}
if (cu.ipins.contains(ipin.symbol)) {
std::cerr << "Duplicate ipin for symbol \"" << ipin.symbol << "\"!" << std::endl;
return false;
@ -172,47 +176,25 @@ static bool accumulate_file(const fs::path& path) {
continue;
} else if (type == "dpin") {
std::string symbol;
std::string scope;
DPIN::COMPONENT comp;
std::size_t local_target;
if (!(iss >> scope) || !(iss >> symbol) || !(iss >> comp.offset) ||
!(iss >> comp.level) || !(iss >> local_target)) {
if (!(iss >> symbol) || !(iss >> comp.offset) ||
!(iss >> comp.level) || !(iss >> comp.target)) {
std::cerr << "Failed to parse dpin declaration!" << std::endl;
return false;
}
if (scope == "l" || scope == "L") {
if (!cu.dpins.contains(symbol))
cu.dpins.emplace(symbol, DPIN{ .symbol=symbol });
if (!cu.dpins.contains(symbol))
cu.dpins.emplace(symbol, DPIN{ .symbol=symbol });
DPIN& dpin = cu.dpins.at(symbol);
DPIN& dpin = cu.dpins.at(symbol);
if (!cu.local_targets.contains(local_target)) {
std::cerr << "Local datapin references target that has not yet been parsed!" << std::endl;
return false;
}
comp.target = local_target;
dpin.components.push_back(comp);
} else if (scope == "g" || scope == "G") {
if (!global_dpins.contains(symbol))
global_dpins.emplace(symbol, DPIN{ .symbol=symbol });
DPIN& dpin = global_dpins.at(symbol);
if (!cu.local_targets.contains(local_target)) {
std::cerr << "Global datapin references target that has not yet been parsed!" << std::endl;
return false;
}
comp.target = cu.local_targets.at(local_target);
dpin.components.push_back(comp);
} else {
std::cerr << "Unexpected scope of dpin (\"" << scope << "\"), expected \"l\" or \"g\"!" << std::endl;
if (!cu.local_targets.contains(comp.target)) {
std::cerr << "Dpin references target that has not yet been parsed!" << std::endl;
return false;
}
dpin.components.push_back(comp);
continue;
} else {
std::cerr << "Invalid spslr file line beginning: \"" << type << "\"" << std::endl;

View File

@ -28,7 +28,7 @@ struct DPIN {
struct COMPONENT {
std::size_t offset;
std::size_t level;
std::size_t target; // local pin -> local target, global pin -> global target
std::size_t target; // local pin -> local target
};
std::string symbol;
@ -58,7 +58,6 @@ struct CU {
};
extern std::unordered_map<std::size_t, TARGET> targets;
extern std::unordered_map<std::string, DPIN> global_dpins;
extern std::unordered_map<std::string, CU> units;
bool accumulate(const std::string& spslr_dir);

View File

@ -15,7 +15,6 @@
/*
Notes:
Datapins for same var/symbol are randomized in order of their level, from bottom of nest to top
The CU uid symbol helps differentiating between e.g. "file.c" and "sub/file.c" (symbtab has no idea)
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)!
*/
@ -103,16 +102,6 @@ int main(int argc, char** argv) {
return 1;
}
// Make sure all ipins were located
for (auto& [cu_uid, cu] : units) {
for (auto& [_, ipin] : cu.ipins) {
if (!ipin.hit.has_value()) {
std::cerr << "Encountered ipin without vaddr!" << 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;
@ -261,7 +250,7 @@ bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& progra
};
// Dump dpins with addresses relative to vaddr_pivot and components ordered by level
auto append_dpin = [&](const DPIN& dpin) -> bool {
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
@ -270,7 +259,9 @@ bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& progra
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 = comp.target;
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;
@ -279,7 +270,7 @@ bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& progra
return true;
};
// Dump all local pins
// Dump all pins
for (const auto& [cu_uid, cu] : units) {
for (const auto& [ipin_sym, ipin] : cu.ipins) {
if (!append_ipin(cu, ipin))
@ -287,21 +278,11 @@ bool assemble_patcher_program(uint64_t vaddr_pivot, std::vector<uint8_t>& progra
}
for (const auto& [dpin_sym, dpin] : cu.dpins) {
DPIN global_dpin = dpin;
for (DPIN::COMPONENT& comp : global_dpin.components)
comp.target = cu.local_targets.at(comp.target);
if (!append_dpin(global_dpin))
if (!append_dpin(cu, dpin))
return false;
}
}
// Dump global dpins
for (const auto& [dpin_sym, dpin] : global_dpins) {
if (!append_dpin(dpin))
return false;
}
// Exit patcher program
inst.opcode = SPSLR_EXIT;
if (!append_inst(inst))

View File

@ -3,126 +3,63 @@
#include <iostream>
#define SPSLR_PINPOINT_CU_UID_LABEL "__spslr_cu_" /* suffixed with "<uid>" */
using namespace LIEF::ELF;
GlobalSymtab global_syms;
std::unordered_map<std::string, LocalSymtab> local_syms;
bool collect_symbols(const std::unique_ptr<LIEF::ELF::Binary>& bin) {
if (!bin)
return false;
bool cu_uid_found = false;
LocalSymtab tmp_local_syms;
auto finish_cu_symbols = [&]() -> bool {
if (cu_uid_found) {
if (local_syms.contains(tmp_local_syms.cu)) {
std::cerr << "Duplicate CU UID -> " << tmp_local_syms.cu << std::endl;
return false;
}
local_syms.emplace(tmp_local_syms.cu, std::move(tmp_local_syms));
}
tmp_local_syms.cu.clear();
tmp_local_syms.symbols.clear();
cu_uid_found = false;
return true;
};
global_syms.symbols.clear();
for (const Symbol& sym : bin->symbols()) {
if (sym.type() == Symbol::TYPE::FILE) {
if (!finish_cu_symbols())
return false;
}
if (sym.name().empty())
continue;
if (sym.type() != Symbol::TYPE::OBJECT && sym.type() != Symbol::TYPE::NOTYPE)
if (sym.type() != Symbol::TYPE::OBJECT &&
sym.type() != Symbol::TYPE::NOTYPE)
continue;
if (sym.binding() != Symbol::BINDING::GLOBAL)
continue;
VADDR vaddr = sym.value();
if (sym.binding() == Symbol::BINDING::GLOBAL) {
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);
continue;
}
if (sym.binding() != Symbol::BINDING::LOCAL)
continue;
if (sym.name().starts_with(SPSLR_PINPOINT_CU_UID_LABEL)) {
tmp_local_syms.cu = sym.name().substr(std::strlen(SPSLR_PINPOINT_CU_UID_LABEL));
cu_uid_found = true;
continue;
}
if (tmp_local_syms.symbols.contains(sym.name())) {
std::cerr << "Duplicate local symbol \"" << sym.name() << "\"!" << std::endl;
if (global_syms.symbols.contains(sym.name())) {
std::cerr << "Duplicate global symbol \"" << sym.name() << "\"!" << std::endl;
return false;
}
tmp_local_syms.symbols.emplace(sym.name(), vaddr);
continue;
global_syms.symbols.emplace(sym.name(), vaddr);
}
if (!finish_cu_symbols())
return false;
return true;
}
bool associate_symbols() {
// Fetch symbols for each accumulated CU
for (auto& [cu_uid, cu] : units) {
if (!local_syms.contains(cu_uid)) {
std::cerr << "Could not find symbols for unit UID " << cu_uid << std::endl;
return false;
}
const LocalSymtab& lsyms = local_syms.at(cu_uid);
for (auto& [ipin_sym, ipin] : cu.ipins) {
if (!lsyms.symbols.contains(ipin_sym)) {
std::cerr << "Unable to locate symbol \"" << ipin_sym << "\" in unit " << cu_uid << std::endl;
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 = lsyms.symbols.at(ipin_sym);
ipin.hit->vaddr = global_syms.symbols.at(ipin_sym);
}
for (auto& [dpin_sym, dpin] : cu.dpins) {
if (!lsyms.symbols.contains(dpin_sym)) {
std::cerr << "Unable to locate symbol \"" << dpin_sym << "\" in unit " << cu_uid << std::endl;
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 = lsyms.symbols.at(dpin_sym);
dpin.hit->vaddr = global_syms.symbols.at(dpin_sym);
}
}
// Fetch global symbols for global dpins
for (auto& [dpin_sym, dpin] : global_dpins) {
if (!global_syms.symbols.contains(dpin_sym)) {
std::cerr << "Unable to locate global symbol \"" << dpin_sym << "\"" << std::endl;
return false;
}
dpin.hit.emplace();
dpin.hit->vaddr = global_syms.symbols.at(dpin_sym);
}
return true;
}

View File

@ -10,13 +10,7 @@ struct GlobalSymtab {
std::unordered_map<std::string, VADDR> symbols;
};
struct LocalSymtab {
std::string cu;
std::unordered_map<std::string, VADDR> symbols;
};
extern GlobalSymtab global_syms;
extern std::unordered_map<std::string, LocalSymtab> local_syms;
bool collect_symbols(const std::unique_ptr<LIEF::ELF::Binary>& bin);
bool associate_symbols();

View File

@ -2,11 +2,12 @@
void on_finish_unit(void* plugin_data, void* user_data);
void set_src_dir(const char* dir);
void set_dst_dir(const char* dir);
void set_meta_dir(const char* dir);
bool has_meta_dir();
const char* get_meta_dir();
bool has_src_dir();
bool has_dst_dir();
bool init_src_file();
const char* get_src_file();
const char* get_src_dir();
const char* get_dst_dir();
bool init_cu_hash();
const char* get_cu_hash();

View File

@ -12,96 +12,83 @@
#include <pinpoint_config.h>
#include <pinpoint_error.h>
static bool src_dir_known = false, dst_dir_known = false;
static std::string src_dir, dst_dir;
static std::string src_file, cu_hash;
void set_src_dir(const char* dir) {
if (!dir) {
src_dir_known = false;
return;
}
src_dir = std::string{ dir };
src_dir_known = true;
}
void set_dst_dir(const char* dir) {
if (!dir) {
dst_dir_known = false;
return;
}
dst_dir = std::string{ dir };
dst_dir_known = true;
}
bool has_src_dir() {
return src_dir_known;
}
bool has_dst_dir() {
return dst_dir_known;
}
const char* get_src_dir() {
if (!src_dir_known)
return nullptr;
return src_dir.c_str();
}
const char* get_dst_dir() {
if (!dst_dir_known)
return nullptr;
return dst_dir.c_str();
}
static std::filesystem::path relative_src_path() {
const char* src_root = get_src_dir();
if (!src_root)
pinpoint_fatal("relative_src_path is missing the source root path");
static bool meta_dir_known = false;
static std::string meta_dir;
bool init_src_file() {
if (!main_input_filename)
pinpoint_fatal("relative_src_path is missing the current source file path");
return false;
std::filesystem::path abs_current_src = std::filesystem::absolute(main_input_filename);
std::filesystem::path abs_root_src = std::filesystem::absolute(src_root);
return std::filesystem::relative(abs_current_src, abs_root_src);
std::filesystem::path srcp = std::filesystem::weakly_canonical(
std::filesystem::absolute(main_input_filename)
);
src_file = srcp.string();
return true;
}
static std::filesystem::path spslr_output_file(const std::filesystem::path& infile) {
const char* dst_root = get_dst_dir();
if (!dst_root)
pinpoint_fatal("spslr_output_file is missing the destination root path");
std::filesystem::path abs_root_dst = std::filesystem::absolute(dst_root);
return (abs_root_dst / infile).string() + SPSLR_PINFILE_EXTENSION;
const char* get_src_file() {
return src_file.c_str();
}
static std::string calculate_cu_uid(const std::filesystem::path& infile) {
bool init_cu_hash() {
unsigned char md5_digest[16];
const char* infile_cstr = infile.c_str();
md5_buffer(infile_cstr, strlen(infile_cstr), md5_digest);
const char* s = get_src_file();
md5_buffer(s, strlen(s), md5_digest);
char md5_digest_hex[33] = {};
for (int i = 0; i < 16; i++)
sprintf(md5_digest_hex + i * 2, "%02x", md5_digest[i]);
return std::string{ md5_digest_hex };
cu_hash = std::string{md5_digest_hex};
return true;
}
static void emit_cu_uid_label(const std::string& uid) {
char label[128];
snprintf(label, sizeof(label), SPSLR_PINPOINT_CU_UID_LABEL "%s", uid.c_str());
fprintf(asm_out_file, "%s:\n", label);
const char* get_cu_hash() {
return cu_hash.c_str();
}
static std::ofstream open_spslr_output_file(const std::filesystem::path& p) {
std::filesystem::create_directories(p.parent_path());
std::ofstream out(p);
void set_meta_dir(const char* dir) {
if (!dir) {
meta_dir_known = false;
return;
}
meta_dir = std::string{ dir };
meta_dir_known = true;
}
bool has_meta_dir() {
return meta_dir_known;
}
const char* get_meta_dir() {
if (!meta_dir_known)
return nullptr;
return meta_dir.c_str();
}
static void emit_dpin_alias_labels() {
for (const DataPin& dpin : DataPin::all()) {
if (dpin.pin_symbol.empty() || dpin.symbol.empty())
pinpoint_fatal("emit_dpin_alias_labels got incomplete data pin");
fprintf(asm_out_file, ".globl %s\n", dpin.pin_symbol.c_str());
fprintf(asm_out_file, ".set %s, %s\n", dpin.pin_symbol.c_str(), dpin.symbol.c_str());
}
}
static std::ofstream open_spslr_output_file() {
std::filesystem::path metadir { get_meta_dir() };
std::filesystem::path metafile { std::string{ get_cu_hash() } + SPSLR_PINFILE_EXTENSION };
std::filesystem::create_directories(metadir);
std::cout << "Dumping meta data to " << (metadir / metafile) << std::endl;
std::ofstream out(metadir / metafile);
if (!out)
pinpoint_fatal("open_spslr_output_file failed to open spslr dump file");
@ -109,20 +96,19 @@ static std::ofstream open_spslr_output_file(const std::filesystem::path& p) {
}
void on_finish_unit(void* plugin_data, void* user_data) {
std::filesystem::path infile = relative_src_path();
std::filesystem::path outfile = spslr_output_file(infile);
std::string cu_uid = get_cu_hash();
std::string cu_uid = calculate_cu_uid(infile);
// Emit globally unique data pin label for each static object to be randomized
emit_cu_uid_label(cu_uid);
emit_dpin_alias_labels();
// Dump all accumulated data to spslr file
std::ofstream out = open_spslr_output_file(outfile);
std::ofstream out = open_spslr_output_file();
// Header associates data with compilation unit
out << "SPSLR " << infile.string() << " " << cu_uid << std::endl;
out << "SPSLR " << get_src_file() << " " << cu_uid << std::endl;
// Dump all target structs
@ -145,8 +131,8 @@ void on_finish_unit(void* plugin_data, void* user_data) {
for (const DataPin& dpin : DataPin::all()) {
for (const DataPin::Component& c : dpin.components) {
// dpin <local/global> <symbol> <offset> <level> <target uid>
out << "dpin " << (dpin.global ? "g" : "l") << " " << dpin.symbol << " "
// dpin <symbol> <offset> <level> <target uid>
out << "dpin " << " " << dpin.pin_symbol << " "
<< c.offset << " " << c.level << " " << c.target << std::endl;
}
}

View File

@ -17,19 +17,12 @@ int plugin_init(struct plugin_name_args* plugin_info, struct plugin_gcc_version*
}
for (int i = 0; i < plugin_info->argc; ++i) {
if (!strcmp(plugin_info->argv[i].key, "srcdir"))
set_src_dir(plugin_info->argv[i].value);
else if (!strcmp(plugin_info->argv[i].key, "dstdir"))
set_dst_dir(plugin_info->argv[i].value);
if (!strcmp(plugin_info->argv[i].key, "metadir"))
set_meta_dir(plugin_info->argv[i].value);
}
if (!has_src_dir()) {
std::cerr << "spslr_pinpoint -> missing source directory argument" << std::endl;
return 1;
}
if (!has_dst_dir()) {
std::cerr << "spslr_pinpoint -> missing destination directory argument" << std::endl;
if (!has_meta_dir()) {
std::cerr << "spslr_pinpoint -> missing meta directory argument" << std::endl;
return 1;
}

View File

@ -2,6 +2,6 @@
#define SPSLR_ATTRIBUTE "spslr"
#define SPSLR_PINPOINT_STAGE0_SEPARATOR "__spslr_offsetof"
#define SPSLR_PINPOINT_CU_UID_LABEL "__spslr_cu_" /* suffixed with "<uid>" */
#define SPSLR_PINFILE_EXTENSION ".spslr"
#define SPSLR_PINPOINT_STAGE2_PIN "__spslr_ipin_" /* suffixed with "<uid>" */
#define SPSLR_PINPOINT_STAGE2_PIN "__spslr_ipin_" /* suffixed with "<cuhash>_<uid>" */
#define SPSLR_PINPOINT_DPIN "__spslr_dpin_" /* suffixed with "<cuhash>_<uid>" */

View File

@ -1,17 +1,27 @@
#include <stage0.h>
#include <final.h>
#include <pinpoint_error.h>
#include <pinpoint_config.h>
static UID next_dpin_uid = 0;
static std::list<DataPin> pins;
void DataPin::reset() {
pins.clear();
next_dpin_uid = 0;
}
const std::list<DataPin>& DataPin::all() {
return pins;
}
static std::string make_dpin_symbol() {
return std::string(SPSLR_PINPOINT_DPIN) +
std::string(get_cu_hash()) + "_" +
std::to_string(next_dpin_uid++);
}
static bool compile_datapin(tree type, DataPin& pin, std::size_t offset = 0, std::size_t level = 0) {
bool res = false;
@ -59,7 +69,7 @@ static void on_static_var(tree var) {
return;
DECL_PRESERVE_P(var) = 1;
pin.global = static_cast<bool>(TREE_PUBLIC(var));
// pin.global = static_cast<bool>(TREE_PUBLIC(var));
tree symbol_tree = DECL_ASSEMBLER_NAME(var);
const char* symbol;
@ -67,6 +77,7 @@ static void on_static_var(tree var) {
pinpoint_fatal("on_static_var failed to get symbol of static variable");
pin.symbol = std::string{ symbol };
pin.pin_symbol = make_dpin_symbol();
pins.emplace_back(std::move(pin));
}

View File

@ -1,9 +1,18 @@
#include <stage0.h>
#include <stage1.h>
#include <stage2.h>
#include <final.h>
#include <pinpoint_error.h>
void on_start_unit(void* plugin_data, void* user_data) {
TargetType::reset();
DataPin::reset();
s2_pins_reset();
if (!init_src_file())
pinpoint_fatal("spslr_pinpoint failed to get source file name");
if (!init_cu_hash())
pinpoint_fatal("spslr_pinpoint failed to initialize CU hash");
}

View File

@ -78,8 +78,8 @@ struct DataPin {
UID target;
};
std::string symbol;
bool global;
std::string symbol; // potentially local object symbol
std::string pin_symbol; // global alias symbol
std::list<Component> components;
static void reset();

View File

@ -1,5 +1,6 @@
#include <stage2.h>
#include <stage1.h>
#include <final.h>
#include <pinpoint_config.h>
#include <pinpoint_error.h>
@ -182,16 +183,18 @@ static bool x86_64_encode_mov_imm32_to_reg(unsigned regno, EncodedReg& out) {
return false;
}
static std::string make_final_x86_64_asm(UID pin_uid, const EncodedReg& enc, std::size_t imm) {
char buf[256];
static std::string make_final_x86_64_asm(const std::string& sym, const EncodedReg& enc, std::size_t imm) {
char buf[512];
std::snprintf(
buf, sizeof(buf),
".globl %s\n"
".byte 0x%02x, 0xC7, 0x%02x\n"
SPSLR_PINPOINT_STAGE2_PIN "%lu:\n"
"%s:\n"
".long %zu",
sym.c_str(),
enc.rex,
enc.modrm,
static_cast<unsigned long>(pin_uid),
sym.c_str(),
imm
);
return std::string(buf);
@ -224,9 +227,12 @@ static bool lower_stage1_marker_insn(rtx_insn* insn) {
if (it == pins.end())
pinpoint_fatal("stage2: internal error after s2_pin_allocate");
it->second.symbol = std::string(SPSLR_PINPOINT_STAGE2_PIN) + std::to_string(pin_uid);
it->second.symbol =
std::string(SPSLR_PINPOINT_STAGE2_PIN) +
std::string(get_cu_hash()) + "_" +
std::to_string(pin_uid);
std::string final_asm = make_final_x86_64_asm(pin_uid, enc, offset);
std::string final_asm = make_final_x86_64_asm(it->second.symbol, enc, offset);
ASM_OPERANDS_TEMPLATE(asm_src) = ggc_strdup(final_asm.c_str());
return true;

View File

@ -1,6 +1,3 @@
Make labels globally unique
- Use hash of object file path to identify CU
Collect alignment data on struct members
Fix bit fields and dynamic size fields (at end of structs) in place

View File

@ -3,8 +3,7 @@ target_include_directories(subject PRIVATE .)
add_dependencies(subject spslr_pinpoint spslr_finalize spslr_selfpatch)
target_link_libraries(subject PRIVATE spslr_selfpatch)
target_compile_options(subject PRIVATE -O1 -fplugin=$<TARGET_FILE:spslr_pinpoint> -fdump-tree-separate_offset -fdump-tree-asm_offset
-fplugin-arg-spslr_pinpoint-srcdir=${CMAKE_CURRENT_SOURCE_DIR}
-fplugin-arg-spslr_pinpoint-dstdir=${CMAKE_CURRENT_BINARY_DIR}/spslr)
-fplugin-arg-spslr_pinpoint-metadir=${CMAKE_CURRENT_BINARY_DIR}/spslr)
# Apply spslr_finalizer to subject
add_custom_command(