diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 8a470ff52..0764e95c7 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -182,6 +182,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_tls.py $) add_test(NAME elf_header COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_elf_header.py $) + add_test(NAME input_magic COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_input_magic.py $) else() message(STATUS "Python3 interpreter not found; skipping Python relinker tests") endif() diff --git a/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp b/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp index 85540b25d..1ed4ca889 100644 --- a/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp +++ b/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp @@ -29,6 +29,7 @@ private: std::uint16_t _readU16At(FileByteOffset offset) const; std::uint8_t _readU8At(FileByteOffset offset) const; std::string _resolveShdrName(std::uint32_t nameOffset, const ElfHeader& header) const; + std::string formatMagic() const; }; } diff --git a/core/relinker/relinker/src/parsing/ElfReader.cpp b/core/relinker/relinker/src/parsing/ElfReader.cpp index 51881ae99..ba8992b17 100644 --- a/core/relinker/relinker/src/parsing/ElfReader.cpp +++ b/core/relinker/relinker/src/parsing/ElfReader.cpp @@ -21,6 +21,17 @@ const std::vector& ElfReader::GetRawBytes() const { return _fileBuffer; } +std::string ElfReader::formatMagic() const { + static constexpr char digits[] = "0123456789abcdef"; + std::string text; + for (std::size_t index = 0; index < 4; ++index) { + if (index != 0) text += ' '; + text += digits[_fileBuffer[index] >> 4]; + text += digits[_fileBuffer[index] & 0x0f]; + } + return text; +} + std::uint8_t ElfReader::_readU8At(FileByteOffset fileByteOffset) const { if (fileByteOffset >= _fileBuffer.size()) { throw RelinkerException("FileByteOffset out of bounds", fileByteOffset); @@ -60,9 +71,13 @@ ElfHeader ElfReader::ReadHeader() const { throw RelinkerException("File too small for ELF header"); } + if (_fileBuffer[0] == 0x4f && _fileBuffer[1] == 0x15 && _fileBuffer[2] == 0x3d && _fileBuffer[3] == 0x1d) { + throw RelinkerException("The input is a SELF container, not an ELF"); + } + if (_fileBuffer[0] != 0x7f || _fileBuffer[1] != 'E' || _fileBuffer[2] != 'L' || _fileBuffer[3] != 'F') { - throw RelinkerException("Invalid ELF magic number"); + throw RelinkerException("Invalid ELF magic number: " + formatMagic()); } ElfHeader header{}; diff --git a/core/relinker/relinker/tests/test_input_magic.py b/core/relinker/relinker/tests/test_input_magic.py new file mode 100644 index 000000000..348df5b15 --- /dev/null +++ b/core/relinker/relinker/tests/test_input_magic.py @@ -0,0 +1,29 @@ +"""Reject inputs that are not plain ELF files with a message that names what was read.""" + +from pathlib import Path +import subprocess +import sys +import tempfile + + +def main(): + relinker = Path(sys.argv[1]).resolve() + with tempfile.TemporaryDirectory(prefix="anyps5-magic-") as directory: + work = Path(directory) + + def convert(name, content, error): + source = work / name + source.write_bytes(content) + output = work / (name + ".out") + result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)], + capture_output=True, text=True, timeout=20) + assert result.returncode == 2 and error in result.stderr and not output.exists(), (name, result) + + convert("eboot.self", b"\x4f\x15\x3d\x1d" + bytes(0x1000), "The input is a SELF container, not an ELF") + convert("eboot.pkg", b"\x7fCNT" + bytes(0x1000), "Invalid ELF magic number: 7f 43 4e 54") + convert("eboot.short", b"\x7fEL", "File too small for ELF header") + print("Input magic diagnostics tests passed") + + +if __name__ == "__main__": + main()