371 lines
9.1 KiB
C++
371 lines
9.1 KiB
C++
#include "accumulation.h"
|
|
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
|
|
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, CU> units;
|
|
|
|
// Two different types with exactly same field structure will not cause problems if simply randomized together
|
|
static bool global_target_field_cmp(const TARGET& a, const TARGET& b) {
|
|
if (a.fields.size() != b.fields.size())
|
|
return false;
|
|
|
|
auto ita = a.fields.begin();
|
|
auto itb = b.fields.begin();
|
|
|
|
for (; ita != a.fields.end() && itb != b.fields.end(); ++ita, ++itb) {
|
|
const FIELD& fa = ita->second;
|
|
const FIELD& fb = itb->second;
|
|
|
|
if (fa.offset != fb.offset)
|
|
return false;
|
|
if (fa.size != fb.size)
|
|
return false;
|
|
if (fa.alignment != fb.alignment)
|
|
return false;
|
|
if (fa.flags != fb.flags)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool global_target_cmp(const TARGET& a, const TARGET& b) {
|
|
if (a.name != b.name)
|
|
return false;
|
|
|
|
if (a.size != b.size || !global_target_field_cmp(a, b)) {
|
|
std::cerr << "WARNING: Got different definitions of \"" << a.name << "\" -> detached randomization" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static std::size_t accumulate_global_target(TARGET&& target, bool& was_new) {
|
|
was_new = false;
|
|
for (const auto& [guid, gtarget] : targets) {
|
|
if (global_target_cmp(gtarget, target))
|
|
return guid;
|
|
}
|
|
|
|
was_new = true;
|
|
|
|
std::size_t guid = next_global_target_uid++;
|
|
targets.emplace(guid, std::move(target));
|
|
return guid;
|
|
}
|
|
|
|
/*
|
|
<sourcefile>.spslr:
|
|
|
|
SPSLR <CU filename> <CU uid symbol>
|
|
target <name> <local uid> <size> <field count>
|
|
f <offset> <size> <alignment> <flags>
|
|
f <offset> <size> <alignment> <flags>
|
|
...
|
|
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 <symbol> <offset> <level> <target uid>
|
|
dpin <symbol> <offset> <level> <target uid>
|
|
...
|
|
*/
|
|
static bool accumulate_file(const fs::path& path, bool no_new_targets) {
|
|
std::ifstream infile(path);
|
|
if (!infile)
|
|
return false;
|
|
|
|
std::string hdr_line;
|
|
if (!std::getline(infile, hdr_line))
|
|
return false;
|
|
|
|
std::istringstream hdr_iss(hdr_line);
|
|
|
|
std::string hdr_magic, hdr_cu_file, hdr_cu_uid;
|
|
|
|
if (!(hdr_iss >> hdr_magic) || hdr_magic != "SPSLR")
|
|
return false;
|
|
|
|
if (!(hdr_iss >> hdr_cu_file) || !(hdr_iss >> hdr_cu_uid))
|
|
return false;
|
|
|
|
std::cout << "Parsing " << path << std::endl;
|
|
std::cout << " Source file -> " << hdr_cu_file << std::endl;
|
|
std::cout << " Unit UID -> " << hdr_cu_uid << std::endl;
|
|
|
|
if (units.contains(hdr_cu_uid)) {
|
|
std::cerr << "Duplicate unit UID -> " << hdr_cu_uid << std::endl;
|
|
return false;
|
|
}
|
|
|
|
units.emplace(hdr_cu_uid, CU{});
|
|
CU& cu = units.at(hdr_cu_uid);
|
|
|
|
std::string line;
|
|
while (std::getline(infile, line)) {
|
|
if (line.empty())
|
|
continue;
|
|
|
|
std::istringstream iss(line);
|
|
std::string type;
|
|
|
|
if (!(iss >> type))
|
|
return false;
|
|
|
|
if (type == "target") {
|
|
TARGET target;
|
|
|
|
std::size_t local_uid, field_count;
|
|
|
|
if (!(iss >> target.name) || !(iss >> local_uid) || !(iss >> target.size) || !(iss >> field_count)) {
|
|
std::cerr << "Invalid target line" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (cu.local_targets.contains(local_uid)) {
|
|
std::cerr << "Duplicate local target uid -> " << local_uid << std::endl;
|
|
return false;
|
|
}
|
|
|
|
for (std::size_t i = 0; i < field_count; i++) {
|
|
std::string fline;
|
|
if (!std::getline(infile, fline)) {
|
|
std::cerr << "Missing field declaration line!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::istringstream fiss(fline);
|
|
|
|
std::string ftype;
|
|
if (!(fiss >> ftype) || ftype != "f") {
|
|
std::cerr << "Missing field declaration line, got \"" << ftype << "\" instead!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
FIELD field;
|
|
if (!(fiss >> field.offset) || !(fiss >> field.size) || !(fiss >> field.alignment) || !(fiss >> field.flags)) {
|
|
std::cerr << "Failed to parse field declaration line!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Note -> could do sanity checks here
|
|
|
|
if (target.fields.contains(field.offset)) {
|
|
std::cerr << "Duplicate field offset!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
target.fields.emplace(field.offset, field);
|
|
}
|
|
|
|
auto fit = target.fields.begin();
|
|
for (std::size_t i = 0; i < field_count; i++) {
|
|
fit->second.idx = i;
|
|
fit++;
|
|
}
|
|
|
|
bool was_new = false;
|
|
std::size_t global_target_uid = accumulate_global_target(std::move(target), was_new);
|
|
|
|
if (no_new_targets && was_new) {
|
|
std::cerr << "Encountered new target but --no-new-targets is set!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
cu.local_targets.emplace(local_uid, global_target_uid);
|
|
continue;
|
|
} else if (type == "ipin") {
|
|
IPIN ipin;
|
|
if (!(iss >> ipin.symbol) || !(iss >> ipin.local_target) ||
|
|
!(iss >> ipin.field_offset) || !(iss >> ipin.imm_size)) {
|
|
std::cerr << "Failed to parse ipin declaration!" << std::endl;
|
|
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;
|
|
}
|
|
|
|
cu.ipins.emplace(ipin.symbol, ipin);
|
|
continue;
|
|
} else if (type == "dpin") {
|
|
std::string symbol;
|
|
DPIN::COMPONENT comp;
|
|
|
|
if (!(iss >> symbol) || !(iss >> comp.offset) ||
|
|
!(iss >> comp.level) || !(iss >> comp.target)) {
|
|
std::cerr << "Failed to parse dpin declaration!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (!cu.dpins.contains(symbol))
|
|
cu.dpins.emplace(symbol, DPIN{ .symbol=symbol });
|
|
|
|
DPIN& dpin = cu.dpins.at(symbol);
|
|
|
|
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;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool accumulate(const std::vector<std::string>& spslr_files, bool no_new_targets) {
|
|
for (const std::string& spslr_file : spslr_files) {
|
|
fs::path p { spslr_file };
|
|
|
|
if (!p.string().ends_with(".spslr")) {
|
|
std::cerr << "The file " << p << " does not appear to be a metadata file!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (!fs::exists(p) || !fs::is_regular_file(p)) {
|
|
std::cerr << "Failed to open metadata file " << p << "!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (!accumulate_file(p, no_new_targets)) {
|
|
std::cerr << "Failed to parse metadata file " << p << "!" << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dump_target_map(const std::string& path) {
|
|
std::filesystem::path p{path};
|
|
if (p.has_parent_path()) {
|
|
std::error_code ec;
|
|
std::filesystem::create_directories(p.parent_path(), ec);
|
|
if (ec) {
|
|
std::cerr << "Failed to create target-map directory: " << ec.message() << "\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::ofstream out(p);
|
|
if (!out)
|
|
return false;
|
|
|
|
out << "SPSLR_TARGETS 1\n";
|
|
|
|
for (const auto& [uid, t] : targets) {
|
|
out << "target " << t.name << " " << uid << " " << t.size << " " << t.fields.size() << "\n";
|
|
|
|
for (const auto& [off, f] : t.fields) {
|
|
(void)off;
|
|
out << "f " << f.offset << " " << f.size << " " << f.alignment << " " << f.flags << "\n";
|
|
}
|
|
}
|
|
|
|
return !!out;
|
|
}
|
|
|
|
bool load_target_map(const std::string& path) {
|
|
std::ifstream in(path);
|
|
if (!in)
|
|
return false;
|
|
|
|
std::string magic;
|
|
std::size_t version = 0;
|
|
if (!(in >> magic >> version) || magic != "SPSLR_TARGETS" || version != 1) {
|
|
std::cerr << "Invalid target map header\n";
|
|
return false;
|
|
}
|
|
|
|
std::string line;
|
|
std::getline(in, line); // consume rest of header line
|
|
|
|
std::size_t max_uid = 0;
|
|
bool have_any = false;
|
|
|
|
while (std::getline(in, line)) {
|
|
if (line.empty())
|
|
continue;
|
|
|
|
std::istringstream iss(line);
|
|
std::string tag;
|
|
iss >> tag;
|
|
|
|
if (tag != "target") {
|
|
std::cerr << "Expected target entry in target map\n";
|
|
return false;
|
|
}
|
|
|
|
TARGET t{};
|
|
std::size_t uid = 0;
|
|
std::size_t field_count = 0;
|
|
|
|
if (!(iss >> t.name >> uid >> t.size >> field_count)) {
|
|
std::cerr << "Malformed target entry in target map\n";
|
|
return false;
|
|
}
|
|
|
|
for (std::size_t i = 0; i < field_count; ++i) {
|
|
std::string fline;
|
|
if (!std::getline(in, fline)) {
|
|
std::cerr << "Missing field entry in target map\n";
|
|
return false;
|
|
}
|
|
|
|
std::istringstream fiss(fline);
|
|
std::string ftag;
|
|
FIELD f{};
|
|
|
|
if (!(fiss >> ftag) || ftag != "f" || !(fiss >> f.offset >> f.size >> f.alignment >> f.flags)) {
|
|
std::cerr << "Malformed field entry in target map\n";
|
|
return false;
|
|
}
|
|
|
|
f.idx = i;
|
|
if (t.fields.contains(f.offset)) {
|
|
std::cerr << "Duplicate field offset in target map\n";
|
|
return false;
|
|
}
|
|
t.fields.emplace(f.offset, f);
|
|
}
|
|
|
|
if (targets.contains(uid)) {
|
|
std::cerr << "Duplicate target uid in target map\n";
|
|
return false;
|
|
}
|
|
|
|
targets.emplace(uid, std::move(t));
|
|
|
|
if (!have_any || uid > max_uid) {
|
|
max_uid = uid;
|
|
have_any = true;
|
|
}
|
|
}
|
|
|
|
if (have_any)
|
|
next_global_target_uid = (max_uid + 1);
|
|
|
|
return true;
|
|
}
|
|
|