mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2025-04-02 10:41:54 -04:00
1581 lines
69 KiB
C++
1581 lines
69 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2020 PCSX-Redux authors *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
|
***************************************************************************/
|
|
|
|
#include <assert.h>
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#include "elfio/elfio.hpp"
|
|
#include "flags.h"
|
|
#include "fmt/format.h"
|
|
#include <magic_enum_all.hpp>
|
|
#include "support/djbhash.h"
|
|
#include "support/file.h"
|
|
#include "support/list.h"
|
|
#include "support/tree.h"
|
|
#include "support/slice.h"
|
|
#include "support/windowswrapper.h"
|
|
|
|
#define SHF_MIPS_GPREL 0x10000000
|
|
|
|
#define vprint(...) \
|
|
if (verbose) fmt::print(__VA_ARGS__)
|
|
|
|
/* The constants from the psyq link files */
|
|
enum class PsyqOpcode : uint8_t {
|
|
END = 0,
|
|
BYTES = 2,
|
|
SWITCH = 6,
|
|
ZEROES = 8,
|
|
RELOCATION = 10,
|
|
EXPORTED_SYMBOL = 12,
|
|
IMPORTED_SYMBOL = 14,
|
|
SECTION = 16,
|
|
LOCAL_SYMBOL = 18,
|
|
FILENAME = 28,
|
|
PROGRAMTYPE = 46,
|
|
UNINITIALIZED = 48,
|
|
INC_SLD_LINENUM = 50,
|
|
INC_SLD_LINENUM_BY_BYTE = 52,
|
|
INC_SLD_LINENUM_BY_WORD = 54,
|
|
SET_SLD_LINENUM = 56,
|
|
SET_SLD_LINENUM_FILE = 58,
|
|
END_SLD = 60,
|
|
FUNCTION = 74,
|
|
FUNCTION_END = 76,
|
|
BLOCK_START = 78,
|
|
BLOCK_END = 80,
|
|
SECTION_DEF = 82,
|
|
SECTION_DEF2 = 84,
|
|
FUNCTION_START2 = 86,
|
|
};
|
|
|
|
enum class PsyqRelocType : uint8_t {
|
|
REL32_BE = 8,
|
|
GPREL16_BE = 12,
|
|
REL32 = 16,
|
|
GPREL16_LE = 30,
|
|
REL26 = 74,
|
|
HI16 = 82,
|
|
LO16 = 84,
|
|
REL26_BE = 92,
|
|
HI16_BE = 96,
|
|
LO16_BE = 98,
|
|
GPREL16 = 100,
|
|
};
|
|
|
|
enum class PsyqExprOpcode : uint8_t {
|
|
VALUE = 0,
|
|
SYMBOL = 2,
|
|
SECTION_BASE = 4,
|
|
SECTION_START = 12,
|
|
SECTION_END = 22,
|
|
ADD = 44,
|
|
SUB = 46,
|
|
DIV = 50,
|
|
};
|
|
|
|
/* Constants to indicate which rewrite pass we're at */
|
|
enum class ElfRelocationPass {
|
|
PASS1,
|
|
PASS2,
|
|
};
|
|
|
|
/* ELFIO isn't providing those */
|
|
enum class elf_mips_reloc_type : unsigned char {
|
|
R_MIPS_NONE = 0,
|
|
R_MIPS_16,
|
|
R_MIPS_32,
|
|
R_MIPS_REL32,
|
|
R_MIPS_26,
|
|
R_MIPS_HI16,
|
|
R_MIPS_LO16,
|
|
R_MIPS_GPREL16,
|
|
R_MIPS_LITERAL,
|
|
R_MIPS_GOT16,
|
|
R_MIPS_PC16,
|
|
R_MIPS_CALL16,
|
|
R_MIPS_GPREL32,
|
|
};
|
|
|
|
/* The main structure to hold a psyq LNK (.OBJ) file */
|
|
struct PsyqLnkFile {
|
|
struct Section;
|
|
struct Symbol;
|
|
struct Relocation;
|
|
struct Expression;
|
|
|
|
/* The main parser entry point; will return nullptr on error */
|
|
static std::unique_ptr<PsyqLnkFile> parse(PCSX::IO<PCSX::File> file, bool verbose, bool sorted);
|
|
static std::string readPsyqString(PCSX::IO<PCSX::File> file) { return file->readString(file->byte()); }
|
|
|
|
/* Our list of sections and symbols will be keyed by their id from the LNK file */
|
|
typedef PCSX::Intrusive::Tree<uint16_t, Section> SectionTree;
|
|
typedef PCSX::Intrusive::List<Section> SectionList;
|
|
typedef PCSX::Intrusive::Tree<uint16_t, Symbol> SymbolTree;
|
|
typedef PCSX::Intrusive::List<Symbol> SymbolList;
|
|
|
|
struct Section : public SectionTree::Node, public SectionList::Node {
|
|
uint16_t group;
|
|
uint8_t alignment;
|
|
std::string name;
|
|
uint32_t zeroes = 0;
|
|
uint32_t uninitializedOffset = 0;
|
|
PCSX::Slice data;
|
|
std::list<Relocation> relocations;
|
|
uint32_t getFullSize() { return data.size() + zeroes + uninitializedOffset; }
|
|
uint32_t pointer = 0;
|
|
uint16_t getKey() { return getLow(); }
|
|
|
|
ELFIO::section* section = nullptr;
|
|
ELFIO::section* rel_sec = nullptr;
|
|
|
|
void display(PsyqLnkFile* lnk);
|
|
void displayRelocs(PsyqLnkFile* lnk);
|
|
bool isBss() { return (name == ".bss") || (name == ".sbss"); }
|
|
bool isText() { return (name == ".text"); }
|
|
bool generateElfSection(PsyqLnkFile* psyq, ELFIO::elfio& writer);
|
|
bool generateElfRelocations(ElfRelocationPass pass, const std::string& prefix, PsyqLnkFile* psyq,
|
|
ELFIO::elfio& writer, ELFIO::Elf_Word symbolSectionIndex,
|
|
ELFIO::string_section_accessor& stra, ELFIO::symbol_section_accessor& syma);
|
|
};
|
|
struct Symbol : public SymbolTree::Node, public SymbolList::Node {
|
|
enum class Type {
|
|
LOCAL,
|
|
EXPORTED,
|
|
IMPORTED,
|
|
UNINITIALIZED,
|
|
} symbolType;
|
|
uint16_t sectionIndex;
|
|
uint32_t offset = 0;
|
|
uint32_t size = 0;
|
|
std::string name;
|
|
ELFIO::Elf_Word elfSym;
|
|
uint16_t getKey() { return getLow(); }
|
|
uint32_t getOffset(PsyqLnkFile* psyq) const {
|
|
if (symbolType == Type::UNINITIALIZED) {
|
|
auto section = psyq->sections.find(sectionIndex);
|
|
assert(section != psyq->sections.end());
|
|
return section->data.size() + section->zeroes + offset;
|
|
} else {
|
|
return offset;
|
|
}
|
|
}
|
|
void display(PsyqLnkFile* lnk);
|
|
bool generateElfSymbol(PsyqLnkFile* psyq, ELFIO::string_section_accessor& stra,
|
|
ELFIO::symbol_section_accessor& syma);
|
|
};
|
|
struct Relocation {
|
|
PsyqRelocType type;
|
|
uint32_t offset;
|
|
int32_t addend;
|
|
std::unique_ptr<Expression> expression;
|
|
void display(PsyqLnkFile* lnk, PsyqLnkFile::Section* sec);
|
|
bool generateElf(ElfRelocationPass pass, const std::string& prefix, PsyqLnkFile* psyq,
|
|
PsyqLnkFile::Section* section, ELFIO::elfio& writer, ELFIO::string_section_accessor& stra,
|
|
ELFIO::symbol_section_accessor& syma, ELFIO::relocation_section_accessor& rela);
|
|
};
|
|
struct Expression {
|
|
PsyqExprOpcode type;
|
|
std::unique_ptr<Expression> left = nullptr;
|
|
std::unique_ptr<Expression> right = nullptr;
|
|
uint32_t value;
|
|
uint16_t symbolIndex;
|
|
uint16_t sectionIndex;
|
|
static std::unique_ptr<Expression> parse(PCSX::IO<PCSX::File> file, bool verbose, int level = 0);
|
|
void display(PsyqLnkFile* lnk, bool top = false);
|
|
};
|
|
|
|
SectionTree sections;
|
|
SectionList sectionsList;
|
|
SectionList unseenSectionsList;
|
|
SymbolTree symbols;
|
|
SymbolList symbolsList;
|
|
int localIndex = 0;
|
|
|
|
/* There's some state we need to maintain during parsing */
|
|
Section* getCurrentSection() {
|
|
auto section = sections.find(currentSection);
|
|
if (section == sections.end()) return nullptr;
|
|
return &*section;
|
|
}
|
|
uint16_t currentSection = 0xffff;
|
|
bool gotProgramSeven = false;
|
|
|
|
/* And there's some state we need to maintain during elf conversion */
|
|
std::map<std::string, ELFIO::Elf_Word> localElfSymbols;
|
|
std::map<std::string, uint32_t> functionSizes;
|
|
std::string elfConversionError;
|
|
|
|
void display();
|
|
bool writeElf(const std::string& prefix, const std::string& out, bool abiNone, bool bigEndian);
|
|
template <typename... Args>
|
|
inline void setElfConversionError(std::string_view formatStr, Args&&... args) {
|
|
elfConversionError = fmt::format(fmt::runtime(formatStr), args...);
|
|
#ifdef _WIN32
|
|
if (IsDebuggerPresent()) __debugbreak();
|
|
#endif
|
|
}
|
|
|
|
~PsyqLnkFile() {
|
|
sectionsList.destroyAll();
|
|
symbolsList.destroyAll();
|
|
}
|
|
};
|
|
|
|
/* The psyq LNK parser code */
|
|
std::unique_ptr<PsyqLnkFile> PsyqLnkFile::parse(PCSX::IO<PCSX::File> file, bool verbose, bool sorted) {
|
|
std::unique_ptr<PsyqLnkFile> ret = std::make_unique<PsyqLnkFile>();
|
|
vprint(":: Reading signature.\n");
|
|
std::string signature = file->readString(3);
|
|
if (signature != "LNK") {
|
|
fmt::print(stderr, "Wrong signature: {}\n", signature);
|
|
return nullptr;
|
|
}
|
|
vprint(" --> Signature ok.\n");
|
|
|
|
vprint(":: Reading version: ");
|
|
uint8_t version = file->byte();
|
|
vprint("{}\n", version);
|
|
if (version != 2) {
|
|
fmt::print(stderr, "Unknown version {}\n", version);
|
|
return nullptr;
|
|
}
|
|
uint32_t curFunctionStart = 0;
|
|
std::string curFunctionName = "";
|
|
|
|
vprint(":: Parsing file...\n");
|
|
while (!file->eof()) {
|
|
uint8_t opcode = file->byte();
|
|
vprint(" :: Read opcode {} --> ", opcode);
|
|
switch (opcode) {
|
|
case (uint8_t)PsyqOpcode::END: {
|
|
vprint("EOF\n");
|
|
while (!ret->unseenSectionsList.empty()) {
|
|
ret->sectionsList.push_back(&*ret->unseenSectionsList.begin());
|
|
}
|
|
if (sorted) {
|
|
ret->sectionsList.clear();
|
|
ret->symbolsList.clear();
|
|
for (auto& section : ret->sections) {
|
|
ret->sectionsList.push_back(§ion);
|
|
}
|
|
for (auto& symbol : ret->symbols) {
|
|
ret->symbolsList.push_back(&symbol);
|
|
}
|
|
}
|
|
// Determine bss symbol placement
|
|
// This has to be done after parsing the whole psyq object, as bss may be out of order in the file.
|
|
// Doing it here ensures that we process symbols in their id order, instead of by psyq object file
|
|
// order, if the user requested ordering by id - otherwise, it'll indeed be order of appearance.
|
|
for (auto& symbol : ret->symbolsList) {
|
|
// Static bss symbols will be represented as a ZEROES opcode instead of UNINITIALIZED.
|
|
// This will cause them to have a size of zero, so ignore size zero symbols here.
|
|
// Their relocs will resolve to an offset of the local .bss instead, so this causes no issues.
|
|
if (symbol.size > 0) {
|
|
auto section = ret->sections.find(symbol.sectionIndex);
|
|
if (section != ret->sections.end() && section->isBss()) {
|
|
auto align = std::min((uint32_t)section->alignment, symbol.size) - 1;
|
|
section->uninitializedOffset += align;
|
|
section->uninitializedOffset &= ~align;
|
|
symbol.offset = section->uninitializedOffset;
|
|
section->uninitializedOffset += symbol.size;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
case (uint8_t)PsyqOpcode::BYTES: {
|
|
uint16_t size = file->read<uint16_t>();
|
|
vprint("Bytes ({:04x})\n", size);
|
|
PCSX::Slice slice = file->read(size);
|
|
std::string hex = slice.toHexString();
|
|
vprint("{}\n", hex.c_str());
|
|
auto section = ret->getCurrentSection();
|
|
if (!section) {
|
|
fmt::print("Section {} not found\n", ret->currentSection);
|
|
return nullptr;
|
|
}
|
|
if (!ret->sectionsList.isLinked(section)) {
|
|
ret->sectionsList.push_back(section);
|
|
}
|
|
section->pointer = section->getFullSize();
|
|
if (section->zeroes) {
|
|
void* ptr = calloc(section->zeroes, 1);
|
|
PCSX::Slice zeroes;
|
|
zeroes.acquire(ptr, section->zeroes);
|
|
section->zeroes = 0;
|
|
section->data.concatenate(zeroes);
|
|
}
|
|
section->data.concatenate(slice);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::SWITCH: {
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
vprint("Switch to section {}\n", sectionIndex);
|
|
ret->currentSection = sectionIndex;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::ZEROES: {
|
|
uint32_t size = file->read<uint32_t>();
|
|
vprint("Zeroes ({:04x})\n", size);
|
|
auto section = ret->getCurrentSection();
|
|
if (!section) {
|
|
fmt::print("Section {} not found\n", ret->currentSection);
|
|
return nullptr;
|
|
}
|
|
section->zeroes += size;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::RELOCATION: {
|
|
uint8_t relocType = file->read<uint8_t>();
|
|
vprint("Relocation {} ", relocType);
|
|
switch (relocType) {
|
|
case (uint8_t)PsyqRelocType::REL32: {
|
|
vprint("(REL32), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::REL26: {
|
|
vprint("(REL26), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::HI16: {
|
|
vprint("(HI16), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::LO16: {
|
|
vprint("(LO16), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::GPREL16: {
|
|
vprint("(GPREL16), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::GPREL16_LE: {
|
|
vprint("(GPREL16 LE), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::GPREL16_BE: {
|
|
vprint("(GPREL16 BE), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::HI16_BE: {
|
|
vprint("(HI16 BE), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::LO16_BE: {
|
|
vprint("(LO16 BE), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::REL26_BE: {
|
|
vprint("(REL26 BE), ");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqRelocType::REL32_BE: {
|
|
vprint("(REL32 BE), ");
|
|
break;
|
|
}
|
|
default: {
|
|
fmt::print("Unknown relocation type {}\n", relocType);
|
|
return nullptr;
|
|
}
|
|
}
|
|
uint16_t offset = file->read<uint16_t>();
|
|
auto section = ret->getCurrentSection();
|
|
if (!section) {
|
|
fmt::print("Section {} not found\n", ret->currentSection);
|
|
return nullptr;
|
|
}
|
|
vprint("offset {:04x}+{:08x}, expression: \n", offset, section->pointer);
|
|
std::unique_ptr<Expression> expression = Expression::parse(file, verbose);
|
|
if (!expression) return nullptr;
|
|
// Addend will be populated later during expression evaluation
|
|
section->relocations.emplace_back(
|
|
Relocation{PsyqRelocType(relocType), offset + section->pointer, 0, std::move(expression)});
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::EXPORTED_SYMBOL: {
|
|
uint16_t symbolIndex = file->read<uint16_t>();
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
std::string name = readPsyqString(file);
|
|
vprint("Export: id {}, section {}, offset {:08x}, name {}\n", symbolIndex, sectionIndex, offset, name);
|
|
Symbol* symbol = new Symbol();
|
|
symbol->symbolType = Symbol::Type::EXPORTED;
|
|
symbol->sectionIndex = sectionIndex;
|
|
symbol->offset = offset;
|
|
symbol->name = name;
|
|
ret->symbols.insert(symbolIndex, symbol);
|
|
ret->symbolsList.push_back(symbol);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::IMPORTED_SYMBOL: {
|
|
uint16_t symbolIndex = file->read<uint16_t>();
|
|
std::string name = readPsyqString(file);
|
|
vprint("Import: id {}, name {}\n", symbolIndex, name);
|
|
Symbol* symbol = new Symbol();
|
|
symbol->symbolType = Symbol::Type::IMPORTED;
|
|
symbol->name = name;
|
|
ret->symbols.insert(symbolIndex, symbol);
|
|
ret->symbolsList.push_back(symbol);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::SECTION: {
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
uint16_t group = file->read<uint16_t>();
|
|
uint8_t alignment = file->read<uint8_t>();
|
|
std::string name = readPsyqString(file);
|
|
vprint("Section: id {}, group {}, alignment {}, name {}\n", sectionIndex, group, alignment, name);
|
|
Section* section = new Section();
|
|
section->group = group;
|
|
section->alignment = alignment;
|
|
section->name = name;
|
|
ret->sections.insert(sectionIndex, section);
|
|
ret->unseenSectionsList.push_back(section);
|
|
if ((alignment - 1) & alignment) {
|
|
fmt::print(stderr, "Section alignment {} isn't a power of two.\n", alignment);
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::LOCAL_SYMBOL: {
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
std::string name = readPsyqString(file);
|
|
vprint("Local: section {}, offset {}, name {}\n", sectionIndex, offset, name);
|
|
Symbol* symbol = new Symbol();
|
|
symbol->symbolType = Symbol::Type::LOCAL;
|
|
symbol->sectionIndex = sectionIndex;
|
|
symbol->offset = offset;
|
|
symbol->name = name;
|
|
ret->symbols.insert(--ret->localIndex, symbol);
|
|
ret->symbolsList.push_back(symbol);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::FILENAME: {
|
|
uint16_t index = file->read<uint16_t>();
|
|
std::string name = readPsyqString(file);
|
|
vprint("File {}: {}\n", index, name);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::PROGRAMTYPE: {
|
|
uint8_t type = file->read<uint8_t>();
|
|
vprint("Program type: {}\n", type);
|
|
if (type != 7 && type != 9) {
|
|
fmt::print(stderr, "Unknown program type {}.\n", type);
|
|
return nullptr;
|
|
}
|
|
if (ret->gotProgramSeven) {
|
|
fmt::print(stderr, "Already got program type.\n");
|
|
return nullptr;
|
|
}
|
|
ret->gotProgramSeven = true;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::UNINITIALIZED: {
|
|
uint16_t symbolIndex = file->read<uint16_t>();
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
uint32_t size = file->read<uint32_t>();
|
|
std::string name = readPsyqString(file);
|
|
|
|
Symbol* symbol = new Symbol();
|
|
symbol->symbolType = Symbol::Type::UNINITIALIZED;
|
|
symbol->sectionIndex = sectionIndex;
|
|
symbol->size = size;
|
|
symbol->name = name;
|
|
vprint("Uninitialized: id {}, section {}, size {}, name {}\n", symbolIndex, sectionIndex, size, name);
|
|
auto section = ret->sections.find(sectionIndex);
|
|
if (section == ret->sections.end()) {
|
|
fmt::print(stderr, "Section {} not found for {}.\n", sectionIndex, name);
|
|
return nullptr;
|
|
}
|
|
symbol->offset = 0; // Filled in later
|
|
ret->symbols.insert(symbolIndex, symbol);
|
|
ret->symbolsList.push_back(symbol);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::INC_SLD_LINENUM: {
|
|
uint16_t offset = file->read<uint16_t>();
|
|
vprint("INC_SLD_LINENUM: offset {}\n", offset);
|
|
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::INC_SLD_LINENUM_BY_BYTE: {
|
|
uint16_t offset = file->read<uint16_t>();
|
|
uint8_t value = file->read<uint8_t>();
|
|
vprint("INC_SLD_LINENUM_BY_BYTE: offset {}, value {}\n", offset, value);
|
|
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::INC_SLD_LINENUM_BY_WORD: {
|
|
uint16_t offset = file->read<uint16_t>();
|
|
uint16_t value = file->read<uint16_t>();
|
|
vprint("INC_SLD_LINENUM_BY_WORD: offset {}, value {}\n", offset, value);
|
|
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::SET_SLD_LINENUM: {
|
|
uint16_t offset = file->read<uint16_t>();
|
|
uint32_t lineNum = file->read<uint32_t>();
|
|
vprint("SET_SLD_LINENUM lineNum: {}, offset {}\n", lineNum, offset);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::SET_SLD_LINENUM_FILE: {
|
|
uint16_t offset = file->read<uint16_t>();
|
|
uint32_t lineNum = file->read<uint32_t>();
|
|
uint16_t fileId = file->read<uint16_t>();
|
|
vprint("SET_SLD_LINENUM_FILE: lineNum {}, offset {}, fileId {}\n", lineNum, offset, fileId);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::END_SLD: {
|
|
// 2 bytes of nothing
|
|
uint16_t zero = file->read<uint16_t>();
|
|
assert(zero == 0);
|
|
vprint("END_SLD\n");
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::FUNCTION: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
uint16_t fileId = file->read<uint16_t>();
|
|
uint32_t startLine = file->read<uint32_t>();
|
|
uint16_t frameReg = file->read<uint16_t>();
|
|
uint32_t frameSize = file->read<uint32_t>();
|
|
uint16_t retnPcReg = file->read<uint16_t>();
|
|
uint32_t mask = file->read<uint32_t>();
|
|
uint32_t maskOffset = file->read<uint32_t>();
|
|
std::string name = readPsyqString(file);
|
|
curFunctionStart = offset;
|
|
vprint(
|
|
"Function: section {}, offset {}, fileId {}, startLine {}, frameReg {}, frameSize {}, retnPcReg {}, "
|
|
"mask {}, maskOffset {}, name {}\n",
|
|
section, offset, fileId, startLine, frameReg, frameSize, retnPcReg, mask, maskOffset, name);
|
|
curFunctionName = std::move(name);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::FUNCTION_END: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
uint32_t endLine = file->read<uint32_t>();
|
|
ret->functionSizes[curFunctionName] = offset - curFunctionStart;
|
|
vprint("Function end: section {}, offset {}, endLine {}\n", section, offset, endLine);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::BLOCK_START: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
uint32_t start = file->read<uint32_t>();
|
|
vprint("Block start at line {} in section {} with offset {:X}\n", start, section, offset);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::BLOCK_END: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
uint32_t end = file->read<uint32_t>();
|
|
vprint("Block end at line {} in section {} with offset {:X}\n", end, section, offset);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::SECTION_DEF: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t value = file->read<uint32_t>();
|
|
uint16_t classId = file->read<uint16_t>();
|
|
uint16_t type = file->read<uint16_t>();
|
|
uint32_t size = file->read<uint32_t>();
|
|
std::string name = readPsyqString(file);
|
|
vprint("Section Definition: section {}, value {}, _class {}, type {}, size {}\n", section, value, classId, type,
|
|
size);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::SECTION_DEF2: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t value = file->read<uint32_t>();
|
|
uint16_t _class = file->read<uint16_t>();
|
|
uint16_t type = file->read<uint16_t>();
|
|
uint32_t size = file->read<uint32_t>();
|
|
|
|
uint16_t dims = file->read<uint16_t>();
|
|
while (dims-- > 0) {
|
|
// ignore for now
|
|
uint16_t dim = file->read<uint16_t>();
|
|
}
|
|
|
|
std::string tag = readPsyqString(file);
|
|
std::string name = readPsyqString(file);
|
|
vprint("SECTION_DEF2: section {}, value {}, _class {}, type {}, size {}, dims {}, tag {}, name {}\n",
|
|
section, value, _class, type, size, dims, tag, name);
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqOpcode::FUNCTION_START2: {
|
|
uint16_t section = file->read<uint16_t>();
|
|
uint32_t offset = file->read<uint32_t>();
|
|
uint16_t fileId = file->read<uint16_t>();
|
|
uint32_t startLine = file->read<uint32_t>();
|
|
uint16_t frameReg = file->read<uint16_t>();
|
|
uint32_t frameSize = file->read<uint32_t>();
|
|
uint16_t retnPcReg = file->read<uint16_t>();
|
|
uint32_t mask = file->read<uint32_t>();
|
|
uint32_t maskOffset = file->read<uint32_t>();
|
|
uint32_t unk1 = file->read<uint32_t>();
|
|
uint32_t unk2 = file->read<uint32_t>();
|
|
std::string name = readPsyqString(file);
|
|
curFunctionStart = offset;
|
|
vprint(
|
|
"Function (2): section {}, offset {}, fileId {}, startLine {}, frameReg {}, frameSize {}, retnPcReg {}, "
|
|
"mask {}, maskOffset {}, name {}, unk1 {:08x}, unk2 {:08x}\n",
|
|
section, offset, fileId, startLine, frameReg, frameSize, retnPcReg, mask, maskOffset, name, unk1, unk2);
|
|
curFunctionName = std::move(name);
|
|
break;
|
|
}
|
|
default: {
|
|
fmt::print(stderr, "Unknown opcode {}.\n", opcode);
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt::print(stderr, "Got actual end of file before EOF command.\n");
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<PsyqLnkFile::Expression> PsyqLnkFile::Expression::parse(PCSX::IO<PCSX::File> file, bool verbose,
|
|
int level) {
|
|
std::unique_ptr<PsyqLnkFile::Expression> ret = std::make_unique<PsyqLnkFile::Expression>();
|
|
uint8_t exprOp = file->read<uint8_t>();
|
|
ret->type = PsyqExprOpcode(exprOp);
|
|
vprint(" ");
|
|
for (int i = 0; i < level; i++) vprint(" ");
|
|
switch (exprOp) {
|
|
case (uint8_t)PsyqExprOpcode::VALUE: {
|
|
uint32_t value = file->read<uint32_t>();
|
|
vprint("Value: {:08x}\n", value);
|
|
ret->value = value;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::SYMBOL: {
|
|
uint16_t import = file->read<uint16_t>();
|
|
vprint("Import: {}\n", import);
|
|
ret->symbolIndex = import;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::SECTION_BASE: {
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
vprint("Base of section {}\n", sectionIndex);
|
|
ret->sectionIndex = sectionIndex;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::SECTION_START: {
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
vprint("Start of section {}\n", sectionIndex);
|
|
ret->sectionIndex = sectionIndex;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::SECTION_END: {
|
|
uint16_t sectionIndex = file->read<uint16_t>();
|
|
vprint("End of section {}\n", sectionIndex);
|
|
ret->sectionIndex = sectionIndex;
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::ADD: {
|
|
vprint("Add:\n");
|
|
ret->right = parse(file, verbose, level + 1);
|
|
ret->left = parse(file, verbose, level + 1);
|
|
if ((ret->right->type == PsyqExprOpcode::ADD) && (ret->left->type == PsyqExprOpcode::VALUE)) {
|
|
auto addend = ret->left->value;
|
|
if (ret->right->right->type == PsyqExprOpcode::VALUE) {
|
|
ret->right->right->value += addend;
|
|
return std::move(ret->right);
|
|
} else if (ret->right->left->type == PsyqExprOpcode::VALUE) {
|
|
ret->right->left->value += addend;
|
|
return std::move(ret->right);
|
|
}
|
|
} else if ((ret->left->type == PsyqExprOpcode::ADD) && (ret->right->type == PsyqExprOpcode::VALUE)) {
|
|
auto addend = ret->right->value;
|
|
if (ret->left->right->type == PsyqExprOpcode::VALUE) {
|
|
ret->left->right->value += addend;
|
|
return std::move(ret->left);
|
|
} else if (ret->left->left->type == PsyqExprOpcode::VALUE) {
|
|
ret->left->left->value += addend;
|
|
return std::move(ret->left);
|
|
}
|
|
}
|
|
if (!ret->left || !ret->right) {
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::SUB: {
|
|
vprint("Sub:\n");
|
|
ret->right = parse(file, verbose, level + 1);
|
|
ret->left = parse(file, verbose, level + 1);
|
|
if (!ret->left || !ret->right) {
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
case (uint8_t)PsyqExprOpcode::DIV: {
|
|
vprint("Div:\n");
|
|
ret->right = parse(file, verbose, level + 1);
|
|
ret->left = parse(file, verbose, level + 1);
|
|
if (!ret->left || !ret->right) {
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
fmt::print(stderr, "Unknown expression type {}\n", exprOp);
|
|
return nullptr;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* The display functions */
|
|
void PsyqLnkFile::display() {
|
|
fmt::print(" :: Symbols\n\n");
|
|
fmt::print(" {:^4} {:^6} {:^6} {:^12} {:^8} {:^8} {}\n", "indx", "type", "sectn", "", "offset",
|
|
"size", "name");
|
|
fmt::print(" -----------------------------------------------------------------\n");
|
|
for (auto& symbol : symbolsList) {
|
|
symbol.display(this);
|
|
}
|
|
fmt::print("\n\n\n :: Sections\n\n");
|
|
fmt::print(" {:4} {:4} {:8} {:8} {:8} {:8} {:8} {}\n", "indx", "grp", "alignmnt", "size", "data",
|
|
"zeroes", "alloc", "name");
|
|
fmt::print(" -------------------------------------------------------------------------\n");
|
|
for (auto& section : sectionsList) {
|
|
section.display(this);
|
|
}
|
|
fmt::print("\n\n\n :: Relocations\n\n");
|
|
fmt::print(" {:10} {:>10}::{:8} {}\n", "type", "section", "offset", "expression");
|
|
fmt::print(" ------------------------------------------\n");
|
|
for (auto& section : sectionsList) {
|
|
section.displayRelocs(this);
|
|
}
|
|
}
|
|
|
|
void PsyqLnkFile::Symbol::display(PsyqLnkFile* lnk) {
|
|
if (symbolType == Type::EXPORTED) {
|
|
auto section = lnk->sections.find(sectionIndex);
|
|
if (section == lnk->sections.end()) {
|
|
fmt::print("** BROKEN SYMBOL AT INDEX {:04x} **\n", getKey());
|
|
} else {
|
|
fmt::print(" {:04x} {:6} ({:04x}) {:12} {:08x} {:8} {}\n", getKey(), "EXPORT", sectionIndex,
|
|
section->name, getOffset(lnk), "", name);
|
|
}
|
|
} else if (symbolType == Type::IMPORTED) {
|
|
fmt::print(" {:04x} {:6} {:6} {:12} {:8} {:8} {}\n", getKey(), "IMPORT", "", "", "", "", name);
|
|
} else {
|
|
auto section = lnk->sections.find(sectionIndex);
|
|
if (section == lnk->sections.end()) {
|
|
fmt::print("** BROKEN SYMBOL AT INDEX {:04x} **\n", getKey());
|
|
} else {
|
|
fmt::print(" {:04x} {:6} ({:04x}) {:12} ({:08x}) {:08x} {}\n", getKey(), "UNDEFN", sectionIndex,
|
|
section->name, getOffset(lnk), size, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PsyqLnkFile::Section::display(PsyqLnkFile* lnk) {
|
|
fmt::print(" {:04x} {:04x} {:8} {:08x} {:08x} {:08x} {:08x} {}\n", getKey(), group, alignment,
|
|
getFullSize(), data.size(), zeroes, uninitializedOffset, name);
|
|
}
|
|
|
|
void PsyqLnkFile::Section::displayRelocs(PsyqLnkFile* lnk) {
|
|
for (auto& reloc : relocations) {
|
|
reloc.display(lnk, this);
|
|
fmt::print("\n");
|
|
}
|
|
}
|
|
|
|
void PsyqLnkFile::Relocation::display(PsyqLnkFile* lnk, PsyqLnkFile::Section* sec) {
|
|
static const std::map<PsyqRelocType, std::string> typeStr = {
|
|
{PsyqRelocType::REL32, "REL32"}, {PsyqRelocType::REL26, "REL26"},
|
|
{PsyqRelocType::HI16, "HI16"}, {PsyqRelocType::LO16, "LO16"},
|
|
{PsyqRelocType::GPREL16, "GPREL16"}, {PsyqRelocType::GPREL16_LE, "GPREL16 LE"},
|
|
{PsyqRelocType::GPREL16_BE, "GPREL16 BE"}, {PsyqRelocType::REL32_BE, "REL32 BE"},
|
|
{PsyqRelocType::REL26_BE, "REL26 BE"}, {PsyqRelocType::HI16_BE, "HI16 BE"},
|
|
{PsyqRelocType::LO16_BE, "LO16 BE"},
|
|
};
|
|
fmt::print(" {:10} {:>10}::{:08x} ", typeStr.find(type)->second, sec->name, offset);
|
|
expression->display(lnk, true);
|
|
}
|
|
|
|
void PsyqLnkFile::Expression::display(PsyqLnkFile* lnk, bool top) {
|
|
switch (type) {
|
|
case PsyqExprOpcode::VALUE: {
|
|
fmt::print("{}", value);
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::SYMBOL: {
|
|
auto symbol = lnk->symbols.find(symbolIndex);
|
|
fmt::print("{}", symbol == lnk->symbols.end() ? "**ERR**" : symbol->name);
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::SECTION_BASE: {
|
|
auto section = lnk->sections.find(sectionIndex);
|
|
fmt::print("{}__base", section == lnk->sections.end() ? "**ERR**" : section->name);
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::SECTION_START: {
|
|
auto section = lnk->sections.find(sectionIndex);
|
|
fmt::print("{}__start", section == lnk->sections.end() ? "**ERR**" : section->name);
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::SECTION_END: {
|
|
auto section = lnk->sections.find(sectionIndex);
|
|
fmt::print("{}__end", section == lnk->sections.end() ? "**ERR**" : section->name);
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::ADD: {
|
|
if (left->type == PsyqExprOpcode::VALUE) {
|
|
if (left->value == 0) {
|
|
right->display(lnk);
|
|
} else {
|
|
if (!top) fmt::print("(");
|
|
right->display(lnk);
|
|
fmt::print(" + ");
|
|
left->display(lnk);
|
|
if (!top) fmt::print(")");
|
|
}
|
|
} else {
|
|
if (!top) fmt::print("(");
|
|
left->display(lnk);
|
|
fmt::print(" + ");
|
|
right->display(lnk);
|
|
if (!top) fmt::print(")");
|
|
}
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::SUB: {
|
|
if (!top) fmt::print("(");
|
|
left->display(lnk);
|
|
fmt::print(" - ");
|
|
right->display(lnk);
|
|
if (!top) fmt::print(")");
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::DIV: {
|
|
if (!top) fmt::print("(");
|
|
left->display(lnk);
|
|
fmt::print(" / ");
|
|
right->display(lnk);
|
|
if (!top) fmt::print(")");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The ELF writer code */
|
|
bool PsyqLnkFile::writeElf(const std::string& prefix, const std::string& out, bool abiNone, bool bigEndian) {
|
|
ELFIO::elfio writer;
|
|
writer.create(ELFIO::ELFCLASS32, bigEndian ? ELFIO::ELFDATA2MSB : ELFIO::ELFDATA2LSB);
|
|
writer.set_os_abi(abiNone ? ELFIO::ELFOSABI_NONE : ELFIO::ELFOSABI_LINUX);
|
|
writer.set_type(ELFIO::ET_REL);
|
|
writer.set_machine(ELFIO::EM_MIPS);
|
|
|
|
// conflate bigEndian with PSX vs N64
|
|
if (bigEndian) {
|
|
writer.set_flags(0x20001101);
|
|
} else {
|
|
writer.set_flags(0x1000); // ?!
|
|
}
|
|
|
|
fmt::print(" :: Generating sections\n");
|
|
for (auto& section : sectionsList) {
|
|
bool success = section.generateElfSection(this, writer);
|
|
if (!success) return false;
|
|
}
|
|
|
|
ELFIO::section* str_sec = writer.sections.add(".strtab");
|
|
str_sec->set_type(ELFIO::SHT_STRTAB);
|
|
ELFIO::string_section_accessor stra(str_sec);
|
|
ELFIO::section* sym_sec = writer.sections.add(".symtab");
|
|
sym_sec->set_type(ELFIO::SHT_SYMTAB);
|
|
sym_sec->set_addr_align(0x4);
|
|
sym_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_SYMTAB));
|
|
sym_sec->set_link(str_sec->get_index());
|
|
ELFIO::symbol_section_accessor syma(writer, sym_sec);
|
|
|
|
syma.add_symbol(stra, out.c_str(), 0, ELFIO::STB_LOCAL, ELFIO::STT_FILE, 0, ELFIO::SHN_ABS);
|
|
|
|
fmt::print(" :: Generating relocations - pass 1, local only\n");
|
|
for (auto& section : sectionsList) {
|
|
bool success = section.generateElfRelocations(ElfRelocationPass::PASS1, prefix, this, writer,
|
|
sym_sec->get_index(), stra, syma);
|
|
if (!success) return false;
|
|
}
|
|
|
|
fmt::print(" :: Generating symbols\n");
|
|
// Generate local symbols first
|
|
for (auto& symbol : symbolsList) {
|
|
if (symbol.symbolType == Symbol::Type::LOCAL) {
|
|
bool success = symbol.generateElfSymbol(this, stra, syma);
|
|
if (!success) return false;
|
|
}
|
|
}
|
|
|
|
sym_sec->set_info(syma.get_symbols_num());
|
|
|
|
// Generate all other symbols afterwards
|
|
for (auto& symbol : symbolsList) {
|
|
if (symbol.symbolType != Symbol::Type::LOCAL) {
|
|
bool success = symbol.generateElfSymbol(this, stra, syma);
|
|
if (!success) return false;
|
|
}
|
|
}
|
|
|
|
fmt::print(" :: Generating relocations - pass 2, globals only\n");
|
|
for (auto& section : sectionsList) {
|
|
bool success = section.generateElfRelocations(ElfRelocationPass::PASS2, prefix, this, writer,
|
|
sym_sec->get_index(), stra, syma);
|
|
if (!success) return false;
|
|
}
|
|
|
|
ELFIO::section* note = writer.sections.add(".note");
|
|
note->set_type(ELFIO::SHT_NOTE);
|
|
|
|
ELFIO::note_section_accessor noteWriter(writer, note);
|
|
noteWriter.add_note(0x01, "psyq-obj-parser", 0, 0);
|
|
noteWriter.add_note(0x01, "pcsx-redux project", 0, 0);
|
|
noteWriter.add_note(0x01, "https://github.com/grumpycoders/pcsx-redux", 0, 0);
|
|
|
|
writer.save(out);
|
|
return true;
|
|
}
|
|
|
|
bool PsyqLnkFile::Symbol::generateElfSymbol(PsyqLnkFile* psyq, ELFIO::string_section_accessor& stra,
|
|
ELFIO::symbol_section_accessor& syma) {
|
|
ELFIO::Elf_Half elfSectionIndex = 0;
|
|
bool isText = false;
|
|
bool isWeak = false;
|
|
|
|
fmt::print(" :: Generating symbol {} {} {}\n", name, getOffset(psyq), sectionIndex);
|
|
if (symbolType != Type::IMPORTED) {
|
|
auto section = psyq->sections.find(sectionIndex);
|
|
if (section == psyq->sections.end()) {
|
|
psyq->setElfConversionError("Couldn't find section index {} for symbol {} ('{}')", sectionIndex, getKey(),
|
|
name);
|
|
return false;
|
|
}
|
|
elfSectionIndex = section->section->get_index();
|
|
isText = section->isText();
|
|
isWeak = symbolType != Type::EXPORTED;
|
|
}
|
|
uint32_t functionSize = 0;
|
|
if (isText) {
|
|
auto functionSizeIter = psyq->functionSizes.find(name);
|
|
if (functionSizeIter != psyq->functionSizes.end()) {
|
|
functionSize = functionSizeIter->second;
|
|
}
|
|
}
|
|
elfSym = syma.add_symbol(stra, name.c_str(), getOffset(psyq), isText ? functionSize : size,
|
|
isWeak ? ELFIO::STB_WEAK
|
|
: symbolType == Type::LOCAL ? ELFIO::STB_LOCAL
|
|
: ELFIO::STB_GLOBAL,
|
|
isText ? ELFIO::STT_FUNC : ELFIO::STT_NOTYPE, 0, elfSectionIndex);
|
|
return true;
|
|
}
|
|
|
|
bool PsyqLnkFile::Section::generateElfSection(PsyqLnkFile* psyq, ELFIO::elfio& writer) {
|
|
if (getFullSize() == 0) return true;
|
|
fmt::print(" :: Generating section {}\n", name);
|
|
static const std::map<std::string, ELFIO::Elf_Xword> flagsMap = {
|
|
{".text", ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR},
|
|
{".rdata", ELFIO::SHF_ALLOC},
|
|
{".data", ELFIO::SHF_ALLOC | ELFIO::SHF_WRITE},
|
|
{".sdata", ELFIO::SHF_ALLOC | ELFIO::SHF_WRITE | SHF_MIPS_GPREL},
|
|
{".bss", ELFIO::SHF_ALLOC | ELFIO::SHF_WRITE},
|
|
{".sbss", ELFIO::SHF_ALLOC | ELFIO::SHF_WRITE | SHF_MIPS_GPREL},
|
|
};
|
|
auto flags = flagsMap.find(name);
|
|
if (flags == flagsMap.end()) {
|
|
psyq->setElfConversionError("Unknown section type '{}'", name);
|
|
return false;
|
|
}
|
|
if (isBss() && data.size()) {
|
|
psyq->setElfConversionError("Section {} looks like bss, but has data", name);
|
|
return false;
|
|
}
|
|
section = writer.sections.add(name);
|
|
section->set_type(isBss() ? ELFIO::SHT_NOBITS : ELFIO::SHT_PROGBITS);
|
|
section->set_flags(flags->second);
|
|
section->set_addr_align(alignment);
|
|
if (isBss()) {
|
|
section->set_size(getFullSize());
|
|
} else {
|
|
section->set_data((const char*)data.data(), ELFIO::Elf_Word(data.size()));
|
|
ELFIO::Elf_Word z = zeroes + uninitializedOffset;
|
|
if (z) {
|
|
void* ptr = calloc(z, 1);
|
|
section->append_data((char*)ptr, z);
|
|
free(ptr);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static const std::map<PsyqRelocType, elf_mips_reloc_type> typeMap = {
|
|
{PsyqRelocType::REL32, elf_mips_reloc_type::R_MIPS_32},
|
|
{PsyqRelocType::REL26, elf_mips_reloc_type::R_MIPS_26},
|
|
{PsyqRelocType::HI16, elf_mips_reloc_type::R_MIPS_HI16},
|
|
{PsyqRelocType::LO16, elf_mips_reloc_type::R_MIPS_LO16},
|
|
{PsyqRelocType::GPREL16, elf_mips_reloc_type::R_MIPS_GPREL16},
|
|
{PsyqRelocType::GPREL16_LE, elf_mips_reloc_type::R_MIPS_GPREL16},
|
|
{PsyqRelocType::GPREL16_BE, elf_mips_reloc_type::R_MIPS_GPREL16},
|
|
{PsyqRelocType::REL26_BE, elf_mips_reloc_type::R_MIPS_26},
|
|
{PsyqRelocType::HI16_BE, elf_mips_reloc_type::R_MIPS_HI16},
|
|
{PsyqRelocType::LO16_BE, elf_mips_reloc_type::R_MIPS_LO16},
|
|
{PsyqRelocType::REL32_BE, elf_mips_reloc_type::R_MIPS_32},
|
|
};
|
|
|
|
bool PsyqLnkFile::Section::generateElfRelocations(ElfRelocationPass pass, const std::string& prefix, PsyqLnkFile* psyq,
|
|
ELFIO::elfio& writer, ELFIO::Elf_Word symbolSectionIndex,
|
|
ELFIO::string_section_accessor& stra,
|
|
ELFIO::symbol_section_accessor& syma) {
|
|
if (relocations.size() == 0) return true;
|
|
if (pass == ElfRelocationPass::PASS1) {
|
|
rel_sec = writer.sections.add(fmt::format(".rel{}", name));
|
|
rel_sec->set_type(ELFIO::SHT_REL);
|
|
rel_sec->set_info(section->get_index());
|
|
rel_sec->set_addr_align(0x4);
|
|
rel_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_REL));
|
|
rel_sec->set_link(symbolSectionIndex);
|
|
}
|
|
ELFIO::relocation_section_accessor rela(writer, rel_sec);
|
|
|
|
for (auto& relocation : relocations) {
|
|
bool success = relocation.generateElf(pass, prefix, psyq, this, writer, stra, syma, rela);
|
|
if (!success) return false;
|
|
}
|
|
|
|
// Pair any stray HI16 relocs with a previous matching one so that relocation addends are handled correctly
|
|
if (pass == ElfRelocationPass::PASS2) {
|
|
ELFIO::Elf_Xword num_relocs = rela.get_entries_num();
|
|
// Create a dummy type to represent a reloc, which is 8 bytes
|
|
using reloc_t = std::array<char, 8>;
|
|
// Get the pointer to the reloc table's data
|
|
const reloc_t* reloc_table = (const reloc_t*)rel_sec->get_data();
|
|
// Allocate a buffer to hold a copy of the reloc table when modifying it
|
|
std::vector<reloc_t> reloc_table_copy(num_relocs);
|
|
// Vector to hold the true addend of each reloc table entry
|
|
std::vector<int32_t> reloc_addends(num_relocs);
|
|
// Copy the reloc table into the working buffer
|
|
std::copy_n(reloc_table, num_relocs, reloc_table_copy.begin());
|
|
|
|
// Lambda that finds a reloc from relocations given a type and offset, writing a reference to it to `out`
|
|
// Returns the index of the reloc in relocations if a reloc was found, -1 if not
|
|
auto find_reloc = [&](elf_mips_reloc_type type, uint32_t offset, int32_t& out) {
|
|
int idx = 0;
|
|
for (auto& cur_reloc : relocations) {
|
|
elf_mips_reloc_type corresponding_reloc_type = typeMap.at(cur_reloc.type);
|
|
if (cur_reloc.offset == offset && corresponding_reloc_type == (elf_mips_reloc_type)type) {
|
|
out = cur_reloc.addend;
|
|
return idx;
|
|
}
|
|
++idx;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
// Do an initial pass to get the full addend for each reloc
|
|
for (ELFIO::Elf_Xword reloc_idx = 0; reloc_idx < num_relocs; ++reloc_idx) {
|
|
ELFIO::Elf64_Addr offset;
|
|
ELFIO::Elf_Word symbol;
|
|
ELFIO::Elf_Word type;
|
|
ELFIO::Elf_Sxword fake_addend; // Addend isn't encoded in the reloc, so this value is useless
|
|
rela.get_entry(reloc_idx, offset, symbol, type, fake_addend);
|
|
|
|
// We need to correlate Relocation objects to elf reloc table entries in order to get full addends, since
|
|
// they're not in the same order
|
|
int found_idx = find_reloc((elf_mips_reloc_type)type, (uint32_t)offset, reloc_addends[reloc_idx]);
|
|
}
|
|
|
|
// Check every entry to see if it's a stray HI16
|
|
for (ELFIO::Elf_Xword reloc_idx = 0; reloc_idx < num_relocs; ++reloc_idx) {
|
|
ELFIO::Elf64_Addr offset;
|
|
ELFIO::Elf_Word symbol;
|
|
ELFIO::Elf_Word type;
|
|
ELFIO::Elf_Sxword fake_addend; // Addend isn't encoded in the reloc, so this value is useless
|
|
int32_t addend = reloc_addends[reloc_idx]; // This will instead contain the full 32 bit addend
|
|
rela.get_entry(reloc_idx, offset, symbol, type, fake_addend);
|
|
|
|
// Check if this is a HI16
|
|
if ((elf_mips_reloc_type)type == elf_mips_reloc_type::R_MIPS_HI16) {
|
|
bool stray_reloc;
|
|
if (reloc_idx + 1 >= num_relocs) {
|
|
// Make sure that we don't overrun the reloc table
|
|
stray_reloc = true;
|
|
} else {
|
|
ELFIO::Elf_Xword checked_idx = reloc_idx + 1;
|
|
ELFIO::Elf64_Addr checked_offset;
|
|
ELFIO::Elf_Word checked_symbol;
|
|
ELFIO::Elf_Word checked_type;
|
|
ELFIO::Elf_Sxword checked_fake_addend;
|
|
int32_t checked_addend;
|
|
// Loop through relocs until we hit one that isn't identical to the current one or until we hit the
|
|
// end of the symbol table
|
|
do {
|
|
rela.get_entry(checked_idx, checked_offset, checked_symbol, checked_type, checked_fake_addend);
|
|
checked_addend = reloc_addends[checked_idx];
|
|
++checked_idx;
|
|
} while (checked_idx + 1 < num_relocs &&
|
|
(elf_mips_reloc_type)checked_type == elf_mips_reloc_type::R_MIPS_HI16 &&
|
|
symbol == checked_symbol && addend == checked_addend);
|
|
|
|
// Check if we ended up at a LO16 with the same symbol and full addend as the HI16
|
|
// If so, then this HI16 (and any that we passed over) is paired correctly
|
|
if ((elf_mips_reloc_type)checked_type == elf_mips_reloc_type::R_MIPS_LO16 &&
|
|
symbol == checked_symbol && addend == checked_addend) {
|
|
stray_reloc = false;
|
|
// We can skip straight to the next reloc, as we've also verified all of the ones up the LO16 we
|
|
// found
|
|
reloc_idx = checked_idx + 1;
|
|
} else {
|
|
stray_reloc = true;
|
|
}
|
|
}
|
|
if (stray_reloc) {
|
|
// Find the matching LO16
|
|
ELFIO::Elf_Xword checked_idx;
|
|
ELFIO::Elf64_Addr checked_offset;
|
|
ELFIO::Elf_Word checked_symbol;
|
|
ELFIO::Elf_Word checked_type;
|
|
ELFIO::Elf_Sxword checked_fake_addend;
|
|
int32_t checked_addend;
|
|
|
|
ELFIO::Elf_Xword matching_idx = -1;
|
|
|
|
// Loop through relocs until we find a LO16 with a matching offset, symbol, and addend
|
|
for (checked_idx = 0; checked_idx < num_relocs; ++checked_idx) {
|
|
rela.get_entry(checked_idx, checked_offset, checked_symbol, checked_type, checked_fake_addend);
|
|
checked_addend = reloc_addends[checked_idx];
|
|
// Check if this is a LO16 that matches
|
|
if (checked_symbol == symbol && checked_addend == addend &&
|
|
(elf_mips_reloc_type)checked_type == elf_mips_reloc_type::R_MIPS_LO16) {
|
|
matching_idx = checked_idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (matching_idx != -1) {
|
|
if (matching_idx < reloc_idx) {
|
|
// Move the HI16 backwards so it's before the LO16
|
|
// This is effectively rotating the subset of the reloc table to the right
|
|
// so that the HI16 ends up at the start of the rotated output
|
|
std::rotate(std::make_reverse_iterator(reloc_table_copy.begin() + reloc_idx + 1),
|
|
std::make_reverse_iterator(reloc_table_copy.begin() + reloc_idx),
|
|
std::make_reverse_iterator(reloc_table_copy.begin() + matching_idx));
|
|
std::rotate(std::make_reverse_iterator(reloc_addends.begin() + reloc_idx + 1),
|
|
std::make_reverse_iterator(reloc_addends.begin() + reloc_idx),
|
|
std::make_reverse_iterator(reloc_addends.begin() + matching_idx));
|
|
} else {
|
|
// Shift the HI16 forwards so it's before the LO16
|
|
// This is effectively rotating the subset of the reloc table to the left
|
|
// so that the HI16 ends up at the start of the rotated output
|
|
std::rotate(reloc_table_copy.begin() + reloc_idx, reloc_table_copy.begin() + reloc_idx + 1,
|
|
reloc_table_copy.begin() + matching_idx);
|
|
std::rotate(reloc_addends.begin() + reloc_idx, reloc_addends.begin() + reloc_idx + 1,
|
|
reloc_addends.begin() + matching_idx);
|
|
}
|
|
|
|
// Update the reloc table so that get_entry is valid for the next iteration
|
|
rel_sec->set_data((char*)reloc_table_copy.data(), num_relocs * sizeof(reloc_t));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PsyqLnkFile::Relocation::generateElf(ElfRelocationPass pass, const std::string& prefix, PsyqLnkFile* psyq,
|
|
PsyqLnkFile::Section* section, ELFIO::elfio& writer,
|
|
ELFIO::string_section_accessor& stra, ELFIO::symbol_section_accessor& syma,
|
|
ELFIO::relocation_section_accessor& rela) {
|
|
fmt::print(" :: Generating relocation ");
|
|
display(psyq, section);
|
|
fmt::print("\n");
|
|
struct SkippedDisplay {
|
|
~SkippedDisplay() {
|
|
if (skipped) fmt::print(" :: Skipped for this pass\n");
|
|
}
|
|
bool skipped = false;
|
|
} skipped;
|
|
auto simpleSymbolReloc = [&, this](Expression* expr, ELFIO::Elf_Word elfSym = 0, int32_t symbolOffset = 0) {
|
|
if (expr) {
|
|
auto symbol = psyq->symbols.find(expr->symbolIndex);
|
|
if (symbol == psyq->symbols.end()) {
|
|
psyq->setElfConversionError("Couldn't find symbol {} for relocation.", expr->symbolIndex);
|
|
return false;
|
|
}
|
|
elfSym = symbol->elfSym;
|
|
}
|
|
if (type == PsyqRelocType::HI16_BE || type == PsyqRelocType::LO16_BE || type == PsyqRelocType::GPREL16_BE) {
|
|
offset -= 0x2;
|
|
}
|
|
auto elfType = typeMap.find(type);
|
|
// TODO get_entries_num to get the new entry index and insert the symbolOffset into a vector for referencing
|
|
// later to be used for reloc pairing
|
|
rela.add_entry(offset, elfSym, (unsigned char)elfType->second);
|
|
ELFIO::Elf_Xword size = section->section->get_size();
|
|
uint8_t* sectionData = (uint8_t*)malloc(size);
|
|
memcpy(sectionData, section->section->get_data(), size);
|
|
switch (type) {
|
|
case PsyqRelocType::REL32: {
|
|
sectionData[offset + 0] = (uint8_t)(symbolOffset >> 0x00);
|
|
sectionData[offset + 1] = (uint8_t)(symbolOffset >> 0x08);
|
|
sectionData[offset + 2] = (uint8_t)(symbolOffset >> 0x10);
|
|
sectionData[offset + 3] = (uint8_t)(symbolOffset >> 0x18);
|
|
break;
|
|
}
|
|
case PsyqRelocType::REL26: {
|
|
sectionData[offset + 0] = (uint8_t)(symbolOffset >> 0x02);
|
|
sectionData[offset + 1] = (uint8_t)(symbolOffset >> 0x0A);
|
|
sectionData[offset + 2] = (uint8_t)(symbolOffset >> 0x12);
|
|
sectionData[offset + 3] &= 0xfc;
|
|
sectionData[offset + 3] |= (uint8_t)(symbolOffset >> 0x1A);
|
|
break;
|
|
}
|
|
case PsyqRelocType::HI16: {
|
|
// Calculate the high reloc, accounting for the signedness of the corresponding low reloc
|
|
uint16_t hi = symbolOffset >> 16;
|
|
if (symbolOffset & 0x8000) {
|
|
hi += 1;
|
|
}
|
|
sectionData[offset + 0] = (uint8_t)(hi >> 0);
|
|
sectionData[offset + 1] = (uint8_t)(hi >> 8);
|
|
break;
|
|
}
|
|
case PsyqRelocType::LO16: {
|
|
// Calculate the low reloc
|
|
uint16_t lo = symbolOffset & 0xFFFF;
|
|
sectionData[offset + 0] = (uint8_t)(lo >> 0);
|
|
sectionData[offset + 1] = (uint8_t)(lo >> 8);
|
|
break;
|
|
}
|
|
case PsyqRelocType::GPREL16: {
|
|
sectionData[offset + 0] = 0;
|
|
sectionData[offset + 1] = 0;
|
|
break;
|
|
}
|
|
case PsyqRelocType::GPREL16_LE: {
|
|
uint16_t lo = symbolOffset & 0xFFFF;
|
|
sectionData[offset + 0] = (uint8_t)(lo >> 0);
|
|
sectionData[offset + 1] = (uint8_t)(lo >> 8);
|
|
break;
|
|
}
|
|
case PsyqRelocType::GPREL16_BE: {
|
|
uint16_t lo = symbolOffset & 0xFFFF;
|
|
sectionData[offset + 3] = (uint8_t)(lo >> 0);
|
|
sectionData[offset + 2] = (uint8_t)(lo >> 8);
|
|
break;
|
|
}
|
|
case PsyqRelocType::REL32_BE: {
|
|
sectionData[offset + 3] = (uint8_t)(symbolOffset >> 0x00);
|
|
sectionData[offset + 2] = (uint8_t)(symbolOffset >> 0x08);
|
|
sectionData[offset + 1] = (uint8_t)(symbolOffset >> 0x10);
|
|
sectionData[offset + 0] = (uint8_t)(symbolOffset >> 0x18);
|
|
break;
|
|
}
|
|
case PsyqRelocType::REL26_BE: {
|
|
sectionData[offset + 3] = (uint8_t)(symbolOffset >> 0x02);
|
|
sectionData[offset + 2] = (uint8_t)(symbolOffset >> 0x0A);
|
|
sectionData[offset + 1] = (uint8_t)(symbolOffset >> 0x12);
|
|
sectionData[offset + 0] &= 0xfc;
|
|
sectionData[offset + 0] |= (uint8_t)(symbolOffset >> 0x1A);
|
|
break;
|
|
}
|
|
case PsyqRelocType::HI16_BE: {
|
|
// Calculate the high reloc, accounting for the signedness of the corresponding low reloc
|
|
uint16_t hi = symbolOffset >> 16;
|
|
if (symbolOffset & 0x8000) {
|
|
hi += 1;
|
|
}
|
|
sectionData[offset + 3] = (uint8_t)(hi >> 0);
|
|
sectionData[offset + 2] = (uint8_t)(hi >> 8);
|
|
break;
|
|
}
|
|
case PsyqRelocType::LO16_BE: {
|
|
// Calculate the low reloc
|
|
uint16_t lo = symbolOffset & 0xFFFF;
|
|
sectionData[offset + 3] = (uint8_t)(lo >> 0);
|
|
sectionData[offset + 2] = (uint8_t)(lo >> 8);
|
|
break;
|
|
}
|
|
default:
|
|
psyq->setElfConversionError("Unsupported relocation type {}.", magic_enum::enum_integer(type));
|
|
return false;
|
|
}
|
|
section->section->set_data((char*)sectionData, size);
|
|
free(sectionData);
|
|
return true;
|
|
};
|
|
auto localSymbolReloc = [&, this](uint16_t sectionIndex, int32_t symbolOffset) {
|
|
if (pass == ElfRelocationPass::PASS2) {
|
|
skipped.skipped = true;
|
|
return true;
|
|
}
|
|
auto section = psyq->sections.find(sectionIndex);
|
|
if (section == psyq->sections.end()) {
|
|
psyq->setElfConversionError("Section {} not found in relocation", sectionIndex);
|
|
return false;
|
|
}
|
|
bool useLocalSymOffsets = true;
|
|
std::string symbolName =
|
|
useLocalSymOffsets ? section->name : fmt::format("${}.rel{}@{:08x}", prefix, section->name, symbolOffset);
|
|
auto existing = psyq->localElfSymbols.find(symbolName);
|
|
ELFIO::Elf_Word elfSym;
|
|
if (existing == psyq->localElfSymbols.end()) {
|
|
elfSym = syma.add_symbol(stra, symbolName.c_str(), useLocalSymOffsets ? 0 : symbolOffset, 0,
|
|
ELFIO::STB_LOCAL, ELFIO::STT_SECTION, 0, section->section->get_index());
|
|
psyq->localElfSymbols.insert(std::make_pair(symbolName, elfSym));
|
|
} else {
|
|
elfSym = existing->second;
|
|
}
|
|
return simpleSymbolReloc(nullptr, elfSym, symbolOffset);
|
|
};
|
|
auto checkZero = [&, this](Expression* expr) {
|
|
this->addend = 0;
|
|
switch (expr->type) {
|
|
case PsyqExprOpcode::SECTION_BASE: {
|
|
return localSymbolReloc(expr->sectionIndex, 0);
|
|
}
|
|
case PsyqExprOpcode::SECTION_START:
|
|
case PsyqExprOpcode::SYMBOL: {
|
|
if (pass == ElfRelocationPass::PASS1) {
|
|
skipped.skipped = true;
|
|
return true;
|
|
}
|
|
return simpleSymbolReloc(expr);
|
|
}
|
|
default: {
|
|
psyq->setElfConversionError("Unsupported relocation expression type: {}",
|
|
magic_enum::enum_integer(expr->type));
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
auto check = [&, this](Expression* expr, int32_t addend) {
|
|
this->addend = addend;
|
|
switch (expr->type) {
|
|
case PsyqExprOpcode::SECTION_BASE: {
|
|
return localSymbolReloc(expr->sectionIndex, addend);
|
|
}
|
|
case PsyqExprOpcode::SYMBOL: {
|
|
auto symbol = psyq->symbols.find(expr->symbolIndex);
|
|
if (symbol == psyq->symbols.end()) {
|
|
psyq->setElfConversionError("Couldn't find symbol {} for relocation.", expr->symbolIndex);
|
|
return false;
|
|
}
|
|
if (symbol->symbolType != PsyqLnkFile::Symbol::Type::IMPORTED) {
|
|
return localSymbolReloc(symbol->sectionIndex, symbol->getOffset(psyq) + addend);
|
|
}
|
|
if (pass == ElfRelocationPass::PASS1) {
|
|
skipped.skipped = true;
|
|
return true;
|
|
}
|
|
ELFIO::Elf_Word elfSym = symbol->elfSym;
|
|
// this is the most complex case, as the psyq format can do
|
|
// relocations that have addend from a symbol, but ELF can't,
|
|
// which means we need to alter the code's byte stream to
|
|
// compute the proper addend.
|
|
switch (type) {
|
|
case PsyqRelocType::HI16: {
|
|
bool success = simpleSymbolReloc(nullptr, elfSym);
|
|
if (!success) return false;
|
|
ELFIO::Elf_Xword size = section->section->get_size();
|
|
uint8_t* sectionData = (uint8_t*)malloc(size);
|
|
memcpy(sectionData, section->section->get_data(), size);
|
|
fmt::print(" :: Altering bytestream to account for HI symbol+addend relocation\n");
|
|
if (addend < 0) {
|
|
fmt::print(" :: Adjusting for negative addend\n");
|
|
addend += 0x10000;
|
|
}
|
|
addend >>= 16;
|
|
sectionData[offset + 0] = addend & 0xff;
|
|
addend >>= 8;
|
|
sectionData[offset + 1] = addend & 0xff;
|
|
addend >>= 8;
|
|
section->section->set_data((char*)sectionData, size);
|
|
free(sectionData);
|
|
return true;
|
|
}
|
|
case PsyqRelocType::LO16: {
|
|
bool success = simpleSymbolReloc(nullptr, elfSym);
|
|
if (!success) return false;
|
|
ELFIO::Elf_Xword size = section->section->get_size();
|
|
uint8_t* sectionData = (uint8_t*)malloc(size);
|
|
memcpy(sectionData, section->section->get_data(), size);
|
|
fmt::print(" :: Altering bytestream to account for LO symbol+addend relocation\n");
|
|
sectionData[offset + 0] = addend & 0xff;
|
|
addend >>= 8;
|
|
sectionData[offset + 1] = addend & 0xff;
|
|
addend >>= 8;
|
|
section->section->set_data((char*)sectionData, size);
|
|
free(sectionData);
|
|
return true;
|
|
}
|
|
// Handled already
|
|
case PsyqRelocType::REL32_BE:
|
|
case PsyqRelocType::HI16_BE:
|
|
case PsyqRelocType::LO16_BE: {
|
|
bool success = simpleSymbolReloc(nullptr, elfSym, addend);
|
|
return true;
|
|
}
|
|
default: {
|
|
psyq->setElfConversionError("Unsupported relocation from a symbol with an addend");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
default: {
|
|
psyq->setElfConversionError("Unsupported relocation expression type: {}",
|
|
magic_enum::enum_integer(expr->type));
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
switch (expression->type) {
|
|
case PsyqExprOpcode::ADD: {
|
|
if (expression->right->type == PsyqExprOpcode::VALUE) {
|
|
if ((expression->left->type == PsyqExprOpcode::SYMBOL) && (expression->right->value == 0)) {
|
|
return checkZero(expression->left.get());
|
|
} else {
|
|
return check(expression->left.get(), expression->right->value);
|
|
}
|
|
} else if (expression->left->type == PsyqExprOpcode::VALUE) {
|
|
if ((expression->right->type == PsyqExprOpcode::SYMBOL) && (expression->left->value == 0)) {
|
|
return checkZero(expression->right.get());
|
|
} else {
|
|
return check(expression->right.get(), expression->left->value);
|
|
}
|
|
} else {
|
|
psyq->setElfConversionError("Unsupported ADD operation in relocation");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::SUB: {
|
|
if (expression->right->type == PsyqExprOpcode::VALUE) {
|
|
// Why
|
|
if (expression->left->type == PsyqExprOpcode::ADD) {
|
|
if (expression->left->left->type == PsyqExprOpcode::VALUE) {
|
|
return check(expression->left->right.get(),
|
|
expression->left->left->value - expression->right->value);
|
|
}
|
|
} else {
|
|
return check(expression->left.get(), -((int32_t)expression->right->value));
|
|
}
|
|
} else if (expression->right->type == PsyqExprOpcode::SECTION_START) {
|
|
// Why
|
|
if (expression->left->type == PsyqExprOpcode::ADD) {
|
|
if (expression->left->left->type == PsyqExprOpcode::VALUE) {
|
|
return check(expression->left->right.get(),
|
|
expression->left->left->value - expression->right->value);
|
|
}
|
|
} else {
|
|
return checkZero(expression->left.get());
|
|
}
|
|
} else {
|
|
psyq->setElfConversionError("Unsupported SUB operation in relocation");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case PsyqExprOpcode::DIV: {
|
|
psyq->setElfConversionError("Unsupported DIV operation in relocation");
|
|
return false;
|
|
}
|
|
default: {
|
|
return checkZero(expression.get());
|
|
}
|
|
}
|
|
psyq->setElfConversionError("Shouldn't happen");
|
|
return false;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
CommandLine::args args(argc, argv);
|
|
auto output = args.get<std::string>("o");
|
|
|
|
auto inputs = args.positional();
|
|
const bool asksForHelp = args.get<bool>("h") || args.get<bool>("help");
|
|
const bool noInput = inputs.size() == 0;
|
|
const bool hasOutput = output.has_value();
|
|
const bool oneInput = inputs.size() == 1;
|
|
if (asksForHelp || noInput || (hasOutput && !oneInput)) {
|
|
fmt::print(R"(
|
|
Usage: {} input.obj [input2.obj...] [-h] [-v] [-d] [-n] [-p prefix] [-o output.o]
|
|
input.obj mandatory: specifies the input psyq LNK object file.
|
|
-h displays this help information and exit.
|
|
-v turns on verbose mode for the parser.
|
|
-d displays the parsed input file.
|
|
-n uses "none" ABI instead of Linux.
|
|
-p prefix uses this prefix for local symbols.
|
|
-s sorts the sections and symbols by id, instead of order of appearance.
|
|
-o output.o tries to dump the parsed psyq LNK file into an ELF file;
|
|
can only work with a single input file.
|
|
-b outputs a big-endian ELF file.
|
|
)",
|
|
argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
bool verbose = !!args.get<bool>("v");
|
|
|
|
int ret = 0;
|
|
|
|
for (auto& input : inputs) {
|
|
PCSX::IO<PCSX::File> file(new PCSX::PosixFile(input));
|
|
if (file->failed()) {
|
|
fmt::print(stderr, "Unable to open file: {}\n", input);
|
|
ret = -2;
|
|
} else {
|
|
auto psyq = PsyqLnkFile::parse(file, verbose, !!args.get<bool>("s"));
|
|
if (!psyq) {
|
|
ret = -3;
|
|
} else {
|
|
if (args.get<bool>("d").value_or(false)) {
|
|
fmt::print(":: Displaying {}\n", input);
|
|
psyq->display();
|
|
fmt::print("\n\n\n");
|
|
}
|
|
if (hasOutput) {
|
|
fmt::print(":: Converting {} to {}...\n", input, output.value());
|
|
std::string prefix = args.get<std::string>("p").value_or("");
|
|
bool success = psyq->writeElf(prefix, output.value(), !!args.get<bool>("n"),
|
|
!!args.get<bool>("b"));
|
|
if (success) {
|
|
fmt::print(":: Conversion completed.\n");
|
|
} else {
|
|
fmt::print(stderr, ":: Conversion failed: {}\n", psyq->elfConversionError);
|
|
ret = -4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
file.reset();
|
|
}
|
|
|
|
return ret;
|
|
}
|