From 36f228815efeae5d3a543baf015649ebb8e88fb2 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Sun, 16 Mar 2025 17:06:59 +0200 Subject: [PATCH] psyq-obj-parser: Handle comm symbols properly Convert .comm symbols into the proper ELF equivalent, instead of converting them to local symbol in the .bss segment. This makes it possible use the converted PSY-Q libraries to recreate a bit-exact executable of Frogger - without proper comm symbol support controlling the order of some .bss symbols in the libraries is impossible. Even though this is more correct than before there are some known users of psyq-obj-parser that are relying on the old behaviour thus a command line option is added which can be used to restore the previous behaviour. --- tools/psyq-obj-parser/psyq-obj-parser.cc | 27 +++++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tools/psyq-obj-parser/psyq-obj-parser.cc b/tools/psyq-obj-parser/psyq-obj-parser.cc index 6f5eb4b64..efe24be75 100644 --- a/tools/psyq-obj-parser/psyq-obj-parser.cc +++ b/tools/psyq-obj-parser/psyq-obj-parser.cc @@ -125,7 +125,8 @@ struct PsyqLnkFile { struct Expression; /* The main parser entry point; will return nullptr on error */ - static std::unique_ptr parse(PCSX::IO file, bool verbose, bool sorted); + static std::unique_ptr parse(PCSX::IO file, bool verbose, bool sorted, + bool convertCommToBss); static std::string readPsyqString(PCSX::IO file) { return file->readString(file->byte()); } /* Our list of sections and symbols will be keyed by their id from the LNK file */ @@ -164,6 +165,7 @@ struct PsyqLnkFile { EXPORTED, IMPORTED, UNINITIALIZED, + COMM, } symbolType; uint16_t sectionIndex; uint32_t offset = 0; @@ -172,7 +174,7 @@ struct PsyqLnkFile { ELFIO::Elf_Word elfSym; uint16_t getKey() { return getLow(); } uint32_t getOffset(PsyqLnkFile* psyq) const { - if (symbolType == Type::UNINITIALIZED) { + if (symbolType == Type::UNINITIALIZED || symbolType == Type::COMM) { auto section = psyq->sections.find(sectionIndex); assert(section != psyq->sections.end()); return section->data.size() + section->zeroes + offset; @@ -243,7 +245,8 @@ struct PsyqLnkFile { }; /* The psyq LNK parser code */ -std::unique_ptr PsyqLnkFile::parse(PCSX::IO file, bool verbose, bool sorted) { +std::unique_ptr PsyqLnkFile::parse(PCSX::IO file, bool verbose, bool sorted, + bool convertCommToBss) { std::unique_ptr ret = std::make_unique(); vprint(":: Reading signature.\n"); std::string signature = file->readString(3); @@ -291,7 +294,7 @@ std::unique_ptr PsyqLnkFile::parse(PCSX::IO file, bool // 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) { + if (symbol.size > 0 && symbol.symbolType != PsyqLnkFile::Symbol::Type::COMM) { 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; @@ -498,7 +501,11 @@ std::unique_ptr PsyqLnkFile::parse(PCSX::IO file, bool std::string name = readPsyqString(file); Symbol* symbol = new Symbol(); - symbol->symbolType = Symbol::Type::UNINITIALIZED; + if (convertCommToBss) { + symbol->symbolType = Symbol::Type::UNINITIALIZED; + } else { + symbol->symbolType = Symbol::Type::COMM; + } symbol->sectionIndex = sectionIndex; symbol->size = size; symbol->name = name; @@ -970,7 +977,9 @@ bool PsyqLnkFile::Symbol::generateElfSymbol(PsyqLnkFile* psyq, ELFIO::string_sec bool isWeak = false; fmt::print(" :: Generating symbol {} {} {}\n", name, getOffset(psyq), sectionIndex); - if (symbolType != Type::IMPORTED) { + if (symbolType == Type::COMM) { + elfSectionIndex = ELFIO::SHN_COMMON; + } else 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(), @@ -1387,7 +1396,8 @@ bool PsyqLnkFile::Relocation::generateElf(ElfRelocationPass pass, const std::str psyq->setElfConversionError("Couldn't find symbol {} for relocation.", expr->symbolIndex); return false; } - if (symbol->symbolType != PsyqLnkFile::Symbol::Type::IMPORTED) { + if (symbol->symbolType != PsyqLnkFile::Symbol::Type::IMPORTED && + symbol->symbolType != PsyqLnkFile::Symbol::Type::COMM) { return localSymbolReloc(symbol->sectionIndex, symbol->getOffset(psyq) + addend); } if (pass == ElfRelocationPass::PASS1) { @@ -1536,6 +1546,7 @@ Usage: {} input.obj [input2.obj...] [-h] [-v] [-d] [-n] [-p prefix] [-o output.o -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. + -c converts comm symbols into .bss symbols )", argv[0]); return -1; @@ -1551,7 +1562,7 @@ Usage: {} input.obj [input2.obj...] [-h] [-v] [-d] [-n] [-p prefix] [-o output.o fmt::print(stderr, "Unable to open file: {}\n", input); ret = -2; } else { - auto psyq = PsyqLnkFile::parse(file, verbose, !!args.get("s")); + auto psyq = PsyqLnkFile::parse(file, verbose, !!args.get("s"), !!args.get("c")); if (!psyq) { ret = -3; } else {