From 3c0c49e3cef38f6ab5ea7fdb09b5b62cb71f0470 Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Thu, 29 Oct 2026 16:44:17 +0100 Subject: [PATCH] Wii U: elf2rpl rewritten in C89 elf2rpl, the host tool that turns the linked Wii U ELF into an RPL, is now a single C89 file, os/wiiu/wut/elf2rpl/main.c, in place of the C++ sources, their own ELF header, CRC-32 and endian helpers, and the Visual Studio project files. Big-endian fields go through retro_endianness.h; the byte swap the old be_val.h picked by PLATFORM_WINDOWS/PLATFORM_APPLE/PLATFORM_LINUX is gone with it, along with those defines - it did not build on any other host and swapped wrongly on a big-endian one. Section CRCs and import signatures come from libretro-common's encoding_crc32 instead of the bundled bytewise table. The input is read once and its sections used where they lie; symbol and import lookups by address are hash tables; the output is laid out in one buffer and written with one call. On a 7 MB core ELF with 41k relocations the RPL comes out byte for byte as before, and so does the log at every -v level, in about a quarter of the time (15 ms against 52 ms; 47 ms against 104 ms at -v -v -v -v). Malformed input now ends in an error instead of a crash: header, section, symbol and relocation offsets are checked against the file, a stub with no symbol behind it is reported, and an unknown option is refused rather than taken for the input file. The tool is built with the host C compiler (HOST_CC, gcc by default), which an exported devkitPPC CC no longer reaches. samples/frontend/wiiu_elf2rpl builds the C tool and adds checks that every section's SHT_RPL_CRCS entry is the zlib CRC-32 of its contents, and that truncated ELFs and an unknown option fail cleanly; it no longer needs the leak and alignment sanitizer exemptions the C++ tool did. --- .gitignore | 1 + Makefile.wiiu | 2 +- os/wiiu/wut/elf2rpl/Makefile | 24 +- os/wiiu/wut/elf2rpl/be_val.h | 72 - os/wiiu/wut/elf2rpl/crc32.h | 53 - os/wiiu/wut/elf2rpl/elf.h | 314 --- os/wiiu/wut/elf2rpl/elf2rpl.vcxproj | 172 -- os/wiiu/wut/elf2rpl/elf2rpl.vcxproj.filters | 36 - os/wiiu/wut/elf2rpl/main.c | 2254 +++++++++++++++++ os/wiiu/wut/elf2rpl/main.cpp | 1794 ------------- os/wiiu/wut/elf2rpl/utils.h | 119 - samples/frontend/wiiu_elf2rpl/.gitignore | 1 + samples/frontend/wiiu_elf2rpl/Makefile | 34 +- .../frontend/wiiu_elf2rpl/wiiu_elf2rpl_test.c | 120 +- 14 files changed, 2398 insertions(+), 2598 deletions(-) delete mode 100644 os/wiiu/wut/elf2rpl/be_val.h delete mode 100644 os/wiiu/wut/elf2rpl/crc32.h delete mode 100644 os/wiiu/wut/elf2rpl/elf.h delete mode 100644 os/wiiu/wut/elf2rpl/elf2rpl.vcxproj delete mode 100644 os/wiiu/wut/elf2rpl/elf2rpl.vcxproj.filters create mode 100644 os/wiiu/wut/elf2rpl/main.c delete mode 100644 os/wiiu/wut/elf2rpl/main.cpp delete mode 100644 os/wiiu/wut/elf2rpl/utils.h diff --git a/.gitignore b/.gitignore index 1b2c4d978d..bae14214c6 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ shaders/shaders_slang *.rpx *.last os/wiiu/wut/elf2rpl/elf2rpl +os/wiiu/wut/elf2rpl/elf2rpl.exe /pkg/wiiu/retroarch /pkg/wiiu/wiiu /pkg/wiiu/rpx diff --git a/Makefile.wiiu b/Makefile.wiiu index 0d2875865c..f6704f90ff 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -365,7 +365,7 @@ $(BUILD_DIR)/%.o: %.s %.depend %.depend: ; %.last: ; -$(ELF2RPL): os/wiiu/wut/elf2rpl/main.cpp os/wiiu/wut/elf2rpl/elf.h +$(ELF2RPL): os/wiiu/wut/elf2rpl/main.c os/wiiu/wut/elf2rpl/Makefile @$(if $(Q), echo MAKE $@,) $(Q)$(MAKE) -C os/wiiu/wut/elf2rpl/ diff --git a/os/wiiu/wut/elf2rpl/Makefile b/os/wiiu/wut/elf2rpl/Makefile index 984df917ee..36341c4d2a 100644 --- a/os/wiiu/wut/elf2rpl/Makefile +++ b/os/wiiu/wut/elf2rpl/Makefile @@ -1,12 +1,17 @@ +# elf2rpl runs on the build host, so it is built with the host's C +# compiler (HOST_CC=... to pick another), never the devkitPPC one. TARGET := elf2rpl -CXX := g++ -LD := g++ +LIBRETRO_COMM_DIR := ../../../../libretro-common -OBJS := main.o -INCLUDES := -I. +HOST_CC ?= gcc -CFLAGS := -O3 -Wall -std=c++14 -DNDEBUG +SOURCES := main.c \ + $(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \ + $(LIBRETRO_COMM_DIR)/features/features_cpu.c \ + $(LIBRETRO_COMM_DIR)/time/rtime.c + +CFLAGS := -O2 -Wall -std=gnu89 -DNDEBUG -I$(LIBRETRO_COMM_DIR)/include LDFLAGS := ifneq ($(findstring Linux,$(shell uname -a)),) @@ -18,10 +23,9 @@ endif all: $(TARGET) clean: - @rm -rf $(TARGET) $(OBJS) + @rm -f $(TARGET) -%.o: %.cpp - $(CXX) -c -o $@ $< $(CFLAGS) $(INCLUDES) +$(TARGET): $(SOURCES) + $(HOST_CC) $(CFLAGS) -o $@ $(SOURCES) $(LDFLAGS) -$(TARGET): $(OBJS) - $(LD) $(OBJS) $(CFLAGS) $(LDFLAGS) -o $@ +.PHONY: all clean diff --git a/os/wiiu/wut/elf2rpl/be_val.h b/os/wiiu/wut/elf2rpl/be_val.h deleted file mode 100644 index 846807b4f3..0000000000 --- a/os/wiiu/wut/elf2rpl/be_val.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include -#include -#include "utils.h" - -template -class be_val -{ -public: - static_assert(!std::is_array::value, "be_val invalid type: array"); - static_assert(!std::is_pointer::value, "be_val invalid type: pointer"); - static_assert(sizeof(Type) == 1 || sizeof(Type) == 2 || sizeof(Type) == 4 || sizeof(Type) == 8, "be_val invalid type size"); - - be_val() - { - } - - be_val(Type value) - { - *this = value; - } - - Type value() const - { - return byte_swap(mValue); - } - - operator Type() const - { - return value(); - } - - template std::enable_if_t::value, be_val&> - operator =(const Other &rhs) - { - mValue = byte_swap(static_cast(rhs)); - return *this; - } - - be_val &operator++() { *this = value() + 1; return *this; } - be_val &operator--() { *this = value() - 1; return *this; } - be_val operator--(int) { auto old = *this; *this = value() - 1; return old; } - be_val operator++(int) { auto old = *this; *this = value() + 1; return old; } - - template bool operator == (const Other &rhs) const { return value() == static_cast(rhs); } - template bool operator != (const Other &rhs) const { return value() != static_cast(rhs); } - template bool operator >= (const Other &rhs) const { return value() >= static_cast(rhs); } - template bool operator <= (const Other &rhs) const { return value() <= static_cast(rhs); } - template bool operator > (const Other &rhs) const { return value() > static_cast(rhs); } - template bool operator < (const Other &rhs) const { return value() < static_cast(rhs); } - - template be_val &operator+=(const Other &rhs) { *this = static_cast(value() + rhs); return *this; } - template be_val &operator-=(const Other &rhs) { *this = static_cast(value() - rhs); return *this; } - template be_val &operator*=(const Other &rhs) { *this = static_cast(value() * rhs); return *this; } - template be_val &operator/=(const Other &rhs) { *this = static_cast(value() / rhs); return *this; } - template be_val &operator%=(const Other &rhs) { *this = static_cast(value() % rhs); return *this; } - template be_val &operator|=(const Other &rhs) { *this = static_cast(value() | rhs); return *this; } - template be_val &operator&=(const Other &rhs) { *this = static_cast(value() & rhs); return *this; } - template be_val &operator^=(const Other &rhs) { *this = static_cast(value() ^ rhs); return *this; } - - template Type operator+(const Other &rhs) const { return static_cast(value() + rhs); } - template Type operator-(const Other &rhs) const { return static_cast(value() - rhs); } - template Type operator*(const Other &rhs) const { return static_cast(value() * rhs); } - template Type operator/(const Other &rhs) const { return static_cast(value() / rhs); } - template Type operator%(const Other &rhs) const { return static_cast(value() % rhs); } - template Type operator|(const Other &rhs) const { return static_cast(value() | rhs); } - template Type operator&(const Other &rhs) const { return static_cast(value() & rhs); } - template Type operator^(const Other &rhs) const { return static_cast(value() ^ rhs); } - -protected: - Type mValue {}; -}; diff --git a/os/wiiu/wut/elf2rpl/crc32.h b/os/wiiu/wut/elf2rpl/crc32.h deleted file mode 100644 index 43f08155d8..0000000000 --- a/os/wiiu/wut/elf2rpl/crc32.h +++ /dev/null @@ -1,53 +0,0 @@ -/* Self-contained CRC-32 (IEEE 802.3, poly 0xEDB88320) for elf2rpl. - * - * Provides a drop-in replacement for the single zlib symbol this tool - * used (crc32), so elf2rpl no longer depends on zlib at all. The - * seed-and-accumulate contract matches zlib exactly: - * - * crc = crc32(0, NULL, 0); // seed - * crc = crc32(crc, buf, len); // accumulate - * - * Output is bit-identical to zlib's crc32() for these inputs. - */ -#ifndef ELF2RPL_CRC32_H -#define ELF2RPL_CRC32_H - -#include - -typedef unsigned long uLong; -typedef unsigned char Bytef; -typedef unsigned int uInt; - -#ifndef Z_NULL -#define Z_NULL 0 -#endif - -static uLong crc32(uLong crc, const Bytef *buf, uInt len) -{ - static uLong table[256]; - static int have_table = 0; - - if (!have_table) - { - uLong c; - int n, k; - for (n = 0; n < 256; n++) - { - c = (uLong)n; - for (k = 0; k < 8; k++) - c = (c & 1) ? (0xEDB88320UL ^ (c >> 1)) : (c >> 1); - table[n] = c; - } - have_table = 1; - } - - if (!buf) - return 0UL; - - crc = crc ^ 0xFFFFFFFFUL; - while (len--) - crc = table[(crc ^ *buf++) & 0xFF] ^ (crc >> 8); - return crc ^ 0xFFFFFFFFUL; -} - -#endif /* ELF2RPL_CRC32_H */ diff --git a/os/wiiu/wut/elf2rpl/elf.h b/os/wiiu/wut/elf2rpl/elf.h deleted file mode 100644 index a476bba825..0000000000 --- a/os/wiiu/wut/elf2rpl/elf.h +++ /dev/null @@ -1,314 +0,0 @@ -#pragma once -#include -#include "be_val.h" - -#pragma pack(push, 1) - -namespace elf -{ - -enum Machine : uint32_t /* e_machine */ -{ - EM_PPC = 20 /* PowerPC */ -}; - -enum Encoding : uint32_t /* e_encoding */ -{ - ELFDATANONE = 0, - ELFDATA2LSB = 1, - ELFDATA2MSB = 2 -}; - -enum Class : uint32_t /* e_class */ -{ - ELFCLASSNONE = 0, - ELFCLASS32 = 1, - ELFCLASS64 = 2 -}; - -enum Version : uint32_t /* e_elf_version */ -{ - EV_NONE = 0, - EV_CURRENT = 1, -}; - -enum FileType : uint32_t /* e_type */ -{ - ET_NONE = 0, /* No file type */ - ET_REL = 1, /* Relocatable file */ - ET_EXEC = 2, /* Executable file */ - ET_DYN = 3, /* Shared object file */ - ET_CORE = 4, /* Core file */ - ET_LOPROC = 0xff00, /* Beginning of processor-specific codes */ - ET_CAFE_RPL = 0xff01, /* Cafe RPL file */ - ET_HIPROC = 0xffff /* Processor-specific */ -}; - -enum EABI : uint32_t /* e_abi */ -{ - EABI_CAFE = 0xcafe /* WiiU CafeOS */ -}; - -enum SectionFlags : uint32_t /* sh_flags */ -{ - SHF_WRITE = 0x1, - SHF_ALLOC = 0x2, - SHF_EXECINSTR = 0x4, - SHF_DEFLATED = 0x08000000, - SHF_MASKPROC = 0xF0000000, -}; - -enum SectionType : uint32_t /* sh_type */ -{ - SHT_NULL = 0, /* No associated section (inactive entry). */ - SHT_PROGBITS = 1, /* Program-defined contents. */ - SHT_SYMTAB = 2, /* Symbol table. */ - SHT_STRTAB = 3, /* String table. */ - SHT_RELA = 4, /* Relocation entries; explicit addends. */ - SHT_HASH = 5, /* Symbol hash table. */ - SHT_DYNAMIC = 6, /* Information for dynamic linking. */ - SHT_NOTE = 7, /* Information about the file. */ - SHT_NOBITS = 8, /* Data occupies no space in the file. */ - SHT_REL = 9, /* Relocation entries; no explicit addends. */ - SHT_SHLIB = 10, /* Reserved. */ - SHT_DYNSYM = 11, /* Symbol table. */ - SHT_INIT_ARRAY = 14, /* Pointers to initialization functions. */ - SHT_FINI_ARRAY = 15, /* Pointers to termination functions. */ - SHT_PREINIT_ARRAY = 16, /* Pointers to pre-init functions. */ - SHT_GROUP = 17, /* Section group. */ - SHT_SYMTAB_SHNDX = 18, /* Indices for SHN_XINDEX entries. */ - SHT_LOPROC = 0x70000000, /* Lowest processor arch-specific type. */ - SHT_HIPROC = 0x7fffffff, /* Highest processor arch-specific type. */ - SHT_LOUSER = 0x80000000, /* Lowest type reserved for applications. */ - SHT_RPL_EXPORTS = 0x80000001, /* RPL Exports */ - SHT_RPL_IMPORTS = 0x80000002, /* RPL Imports */ - SHT_RPL_CRCS = 0x80000003, /* RPL CRCs */ - SHT_RPL_FILEINFO = 0x80000004,/* RPL FileInfo */ - SHT_HIUSER = 0xffffffff /* Highest type reserved for applications. */ -}; - -enum SymbolBinding : uint32_t /* st_info >> 4 */ -{ - STB_LOCAL = 0, /* Local symbol, not visible outside obj file containing def */ - STB_GLOBAL = 1, /* Global symbol, visible to all object files being combined */ - STB_WEAK = 2, /* Weak symbol, like global but lower-precedence */ - STB_GNU_UNIQUE = 10, - STB_LOOS = 10, /* Lowest operating system-specific binding type */ - STB_HIOS = 12, /* Highest operating system-specific binding type */ - STB_LOPROC = 13, /* Lowest processor-specific binding type */ - STB_HIPROC = 15 /* Highest processor-specific binding type */ -}; - -enum SymbolType : uint32_t /* st_info & f */ -{ - STT_NOTYPE = 0, /* Symbol's type is not specified */ - STT_OBJECT = 1, /* Symbol is a data object (variable, array, etc.) */ - STT_FUNC = 2, /* Symbol is executable code (function, etc.) */ - STT_SECTION = 3, /* Symbol refers to a section */ - STT_FILE = 4, /* Local, absolute symbol that refers to a file */ - STT_COMMON = 5, /* An uninitialized common block */ - STT_TLS = 6, /* Thread local data object */ - STT_LOOS = 7, /* Lowest operating system-specific symbol type */ - STT_HIOS = 8, /* Highest operating system-specific symbol type */ - STT_GNU_IFUNC = 10, /* GNU indirect function */ - STT_LOPROC = 13, /* Lowest processor-specific symbol type */ - STT_HIPROC = 15 /* Highest processor-specific symbol type */ -}; - -enum SectionIndex : uint16_t /* st_shndx */ -{ - SHN_UNDEF = 0, /* Undefined */ - SHN_LORESERVE = 0xff00, /* Reserved range */ - SHN_ABS = 0xfff1, /* Absolute symbols */ - SHN_COMMON = 0xfff2, /* Common symbols */ - SHN_XINDEX = 0xffff, /* Escape -- index stored elsewhere */ - SHN_HIRESERVE = 0xffff -}; - -enum RelocationType : uint32_t /* r_info & 0xff */ -{ - R_PPC_NONE = 0, - R_PPC_ADDR32 = 1, - R_PPC_ADDR24 = 2, - R_PPC_ADDR16 = 3, - R_PPC_ADDR16_LO = 4, - R_PPC_ADDR16_HI = 5, - R_PPC_ADDR16_HA = 6, - R_PPC_ADDR14 = 7, - R_PPC_ADDR14_BRTAKEN = 8, - R_PPC_ADDR14_BRNTAKEN = 9, - R_PPC_REL24 = 10, - R_PPC_REL14 = 11, - R_PPC_REL14_BRTAKEN = 12, - R_PPC_REL14_BRNTAKEN = 13, - R_PPC_GOT16 = 14, - R_PPC_GOT16_LO = 15, - R_PPC_GOT16_HI = 16, - R_PPC_GOT16_HA = 17, - R_PPC_PLTREL24 = 18, - R_PPC_JMP_SLOT = 21, - R_PPC_RELATIVE = 22, - R_PPC_LOCAL24PC = 23, - R_PPC_REL32 = 26, - R_PPC_TLS = 67, - R_PPC_DTPMOD32 = 68, - R_PPC_TPREL16 = 69, - R_PPC_TPREL16_LO = 70, - R_PPC_TPREL16_HI = 71, - R_PPC_TPREL16_HA = 72, - R_PPC_TPREL32 = 73, - R_PPC_DTPREL16 = 74, - R_PPC_DTPREL16_LO = 75, - R_PPC_DTPREL16_HI = 76, - R_PPC_DTPREL16_HA = 77, - R_PPC_DTPREL32 = 78, - R_PPC_GOT_TLSGD16 = 79, - R_PPC_GOT_TLSGD16_LO = 80, - R_PPC_GOT_TLSGD16_HI = 81, - R_PPC_GOT_TLSGD16_HA = 82, - R_PPC_GOT_TLSLD16 = 83, - R_PPC_GOT_TLSLD16_LO = 84, - R_PPC_GOT_TLSLD16_HI = 85, - R_PPC_GOT_TLSLD16_HA = 86, - R_PPC_GOT_TPREL16 = 87, - R_PPC_GOT_TPREL16_LO = 88, - R_PPC_GOT_TPREL16_HI = 89, - R_PPC_GOT_TPREL16_HA = 90, - R_PPC_GOT_DTPREL16 = 91, - R_PPC_GOT_DTPREL16_LO = 92, - R_PPC_GOT_DTPREL16_HI = 93, - R_PPC_GOT_DTPREL16_HA = 94, - R_PPC_TLSGD = 95, - R_PPC_TLSLD = 96, - R_PPC_EMB_SDA21 = 109, - R_PPC_REL16 = 249, - R_PPC_REL16_LO = 250, - R_PPC_REL16_HI = 251, - R_PPC_REL16_HA = 252, -}; - -enum RplFileInfoFlag : uint32_t -{ - RPL_IS_RPX = 0x2, -}; - -static const unsigned HeaderMagic = 0x7f454c46; - -struct Header -{ - be_val magic; /* File identification. */ - be_val fileClass; /* File class. */ - be_val encoding; /* Data encoding. */ - be_val elfVersion; /* File version. */ - be_val abi; /* OS/ABI identification. (EABI_*) */ - be_val pad[7]; - - be_val type; /* Type of file (ET_*) */ - be_val machine; /* Required architecture for this file (EM_*) */ - be_val version; /* Must be equal to 1 */ - be_val entry; /* Address to jump to in order to start program */ - be_val phoff; /* Program header table's file offset, in bytes */ - be_val shoff; /* Section header table's file offset, in bytes */ - be_val flags; /* Processor-specific flags */ - be_val ehsize; /* Size of ELF header, in bytes */ - be_val phentsize; /* Size of an entry in the program header table */ - be_val phnum; /* Number of entries in the program header table */ - be_val shentsize; /* Size of an entry in the section header table */ - be_val shnum; /* Number of entries in the section header table */ - be_val shstrndx; /* Sect hdr table index of sect name string table */ -}; -CHECK_SIZE(Header, 0x34); - -struct SectionHeader -{ - be_val name; /* Section name (index into string table) */ - be_val type; /* Section type (SHT_*) */ - be_val flags; /* Section flags (SHF_*) */ - be_val addr; /* Address where section is to be loaded */ - be_val offset; /* File offset of section data, in bytes */ - be_val size; /* Size of section, in bytes */ - be_val link; /* Section type-specific header table index link */ - be_val info; /* Section type-specific extra information */ - be_val addralign; /* Section address alignment */ - be_val entsize; /* Size of records contained within the section */ -}; -CHECK_SIZE(SectionHeader, 0x28); - -struct Symbol -{ - be_val name; /* Symbol name (index into string table) */ - be_val value; /* Value or address associated with the symbol */ - be_val size; /* Size of the symbol */ - be_val info; /* Symbol's type and binding attributes */ - be_val other; /* Must be zero; reserved */ - be_val shndx; /* Which section (header table index) it's defined in (SHN_*) */ -}; -CHECK_SIZE(Symbol, 0x10); - -struct Rela -{ - be_val offset; - be_val info; - be_val addend; -}; -CHECK_SIZE(Rela, 0x0C); - -struct RplImport -{ - be_val count; - be_val signature; - char name[1]; -}; - -struct RplExport -{ - struct Export - { - be_val value; - be_val name; - }; - - be_val count; - be_val signature; - Export exports[1]; -}; - -struct RplCrc -{ - be_val crc; -}; -CHECK_SIZE(RplCrc, 0x04); - -struct RplFileInfo -{ - be_val version; - be_val textSize; - be_val textAlign; - be_val dataSize; - be_val dataAlign; - be_val loadSize; - be_val loadAlign; - be_val tempSize; - be_val trampAdjust; - be_val sdaBase; - be_val sda2Base; - be_val stackSize; - be_val filename; - be_val flags; - be_val heapSize; - be_val tagOffset; - be_val minVersion; - be_val compressionLevel; - be_val trampAddition; - be_val fileInfoPad; - be_val cafeSdkVersion; - be_val cafeSdkRevision; - be_val tlsModuleIndex; - be_val tlsAlignShift; - be_val runtimeFileInfoSize; -}; -CHECK_SIZE(RplFileInfo, 0x60); - -} /* namespace elf */ - -#pragma pack(pop) diff --git a/os/wiiu/wut/elf2rpl/elf2rpl.vcxproj b/os/wiiu/wut/elf2rpl/elf2rpl.vcxproj deleted file mode 100644 index 4f34130c08..0000000000 --- a/os/wiiu/wut/elf2rpl/elf2rpl.vcxproj +++ /dev/null @@ -1,172 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {F6442B08-9323-4D98-ABA6-8856467B148B} - Win32Proj - elf2rpl - 8.1 - - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)\bin\ - $(IncludePath) - - - true - $(SolutionDir)\bin\ - $(IncludePath) - - - false - $(SolutionDir)\bin\ - $(IncludePath) - - - false - $(SolutionDir)\bin\ - $(IncludePath) - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)\common;%(AdditionalIncludeDirectories) - - - Console - true - UseLinkTimeCodeGeneration - - - - - - - Level3 - Disabled - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)\common;%(AdditionalIncludeDirectories) - - - Console - true - UseLinkTimeCodeGeneration - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)\common;%(AdditionalIncludeDirectories) - - - Console - true - true - No - UseLinkTimeCodeGeneration - - - - - Level3 - - - MaxSpeed - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)\common;%(AdditionalIncludeDirectories) - - - Console - true - true - No - UseLinkTimeCodeGeneration - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/os/wiiu/wut/elf2rpl/elf2rpl.vcxproj.filters b/os/wiiu/wut/elf2rpl/elf2rpl.vcxproj.filters deleted file mode 100644 index 67b65b4a97..0000000000 --- a/os/wiiu/wut/elf2rpl/elf2rpl.vcxproj.filters +++ /dev/null @@ -1,36 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/os/wiiu/wut/elf2rpl/main.c b/os/wiiu/wut/elf2rpl/main.c new file mode 100644 index 0000000000..e72d520399 --- /dev/null +++ b/os/wiiu/wut/elf2rpl/main.c @@ -0,0 +1,2254 @@ +/* elf2rpl: turns the Wii U ELF that Makefile.wiiu links (-pie, with + * os/wiiu/link_rpl.ld) into an RPL the Cafe OS loader takes. + * + * The input is read once into memory; its section contents are used + * where they lie, patched in place for the .rela.dyn relative + * relocations, and the output is laid out in one buffer and written + * with one call. Every multi-byte field of both files is big-endian + * and goes through retro_endianness.h; section CRCs and import + * signatures through encoding_crc32(). + * + * Usage: elf2rpl [-v ...] [--] + * Each -v raises the detail printed while converting. */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define LOAD_ADDRESS 0x01000000u +#define CODE_ADDRESS 0x02000000u +#define DATA_ADDRESS 0x10000000u +#define WIIU_LOAD_ADDRESS 0xC0000000u + +#define EM_PPC 20 +#define ELFCLASS32 1 +#define ELFDATA2MSB 2 +#define EV_CURRENT 1 +#define EABI_CAFE 0xCAFE +#define ELF_MAGIC 0x7F454C46u + +#define SHT_NULL 0u +#define SHT_PROGBITS 1u +#define SHT_SYMTAB 2u +#define SHT_STRTAB 3u +#define SHT_RELA 4u +#define SHT_NOBITS 8u +#define SHT_RPL_IMPORTS 0x80000002u +#define SHT_RPL_CRCS 0x80000003u +#define SHT_RPL_FILEINFO 0x80000004u + +#define SHF_WRITE 0x1u +#define SHF_ALLOC 0x2u +#define SHF_EXECINSTR 0x4u + +#define SHN_UNDEF 0 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 + +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 +#define R_PPC_RELATIVE 22 + +#define RPL_IS_RPX 0x2u + +#define EHDR_SIZE 0x34u +#define SHDR_SIZE 0x28u +#define SYM_SIZE 0x10u +#define RELA_SIZE 0x0Cu +#define RPLLIBS_SIZE 0x0Cu +#define FILEINFO_SIZE 0x60u + +#define NO_INDEX (-1) + +#define VERBOSE_LOADED_SECTIONS 1 +#define VERBOSE_LOADED_SYMBOLS 3 +#define VERBOSE_LOADED_IMPORTS 2 +#define VERBOSE_LOADED_RELOCATIONS 4 +#define VERBOSE_LOADED_DYN_RELOC 4 +#define VERBOSE_PRUNED_SYMBOLS 2 +#define VERBOSE_CONVERT_RELOCATIONS 4 +#define VERBOSE_DUP_SYMBOLS 3 +#define VERBOSE_OUTPUT_SECTIONS 1 +#define VERBOSE_OUTPUT_SYMBOLS 2 + +#define ALIGN_UP(v, a) (((uint32_t)(v) + ((uint32_t)(a) - 1u)) & ~((uint32_t)(a) - 1u)) + +static int verbose = 0; + +/* ---- big-endian fields ---- */ + +static INLINE uint32_t rd32(const uint8_t *p) +{ + return retro_get_unaligned_32be((void*)p); +} + +static INLINE uint16_t rd16(const uint8_t *p) +{ + return retro_get_unaligned_16be((void*)p); +} + +static INLINE void wr32(uint8_t *p, uint32_t v) +{ + retro_set_unaligned_32be(p, v); +} + +static INLINE void wr16(uint8_t *p, uint16_t v) +{ + retro_set_unaligned_16be(p, v); +} + +/* ---- log output ---- + * The messages print numbers in a base that, once switched to hex, + * stays hex until a message switches it back, so the log reads the + * same as it always has. */ + +static int log_hex = 0; + +static void log_s(const char *s) +{ + fputs(s, stdout); +} + +static void log_n(const char *s, size_t len) +{ + fwrite(s, 1, len, stdout); +} + +static void log_u(uint32_t v) +{ + printf(log_hex ? "%lx" : "%lu", (unsigned long)v); +} + +static void log_i(int32_t v) +{ + if (log_hex) + printf("%lx", (unsigned long)(uint32_t)v); + else + printf("%ld", (long)v); +} + +/* ---- string storage for names the conversion makes ---- */ + +typedef struct name_block +{ + struct name_block *next; + size_t used; + size_t cap; + char text[1]; +} name_block_t; + +static name_block_t *name_blocks = NULL; + +static char *name_join(const char *a, size_t alen, const char *b, size_t blen) +{ + char *s; + size_t need = alen + blen + 1; + + if (!name_blocks || name_blocks->cap - name_blocks->used < need) + { + size_t cap = need > 65536 ? need : 65536; + name_block_t *blk = (name_block_t*)malloc(sizeof(*blk) + cap); + if (!blk) + return NULL; + blk->next = name_blocks; + blk->used = 0; + blk->cap = cap; + name_blocks = blk; + } + s = name_blocks->text + name_blocks->used; + memcpy(s, a, alen); + memcpy(s + alen, b, blen); + s[alen + blen] = '\0'; + name_blocks->used += need; + return s; +} + +static void names_free(void) +{ + while (name_blocks) + { + name_block_t *next = name_blocks->next; + free(name_blocks); + name_blocks = next; + } +} + +/* ---- growable arrays ---- */ + +static bool grow(void **p, size_t *cap, size_t need, size_t elem) +{ + size_t n; + void *q; + if (need <= *cap) + return true; + n = *cap ? *cap : 16; + while (n < need) + n *= 2; + if (!(q = realloc(*p, n * elem))) + return false; + *p = q; + *cap = n; + return true; +} + +/* ---- the model ---- */ + +typedef struct +{ + uint32_t name; + uint32_t type; + uint32_t flags; + uint32_t addr; + uint32_t offset; + uint32_t size; + uint32_t link; + uint32_t info; + uint32_t addralign; + uint32_t entsize; +} shdr_t; + +typedef struct +{ + shdr_t h; + uint8_t *data; /* in the input image; NULL when it has none */ + uint32_t data_size; +} in_section_t; + +typedef struct +{ + const char *name; + uint8_t *data; /* SHT_PROGBITS: in the input image */ + uint32_t data_size; + uint32_t address; + uint32_t type; + uint32_t flags; + uint32_t size; /* contents for SHT_PROGBITS, extent for SHT_NOBITS */ + int out; /* output section */ + int in_index; +} data_section_t; + +typedef struct +{ + const char *name; + size_t name_len; + uint32_t address; + uint32_t size; + uint32_t out_name_pos; + uint32_t ref_count; + int out_index; + int in_section; /* data section */ + int out_section; /* set on section symbols */ + uint8_t type; + uint8_t binding; +} symbol_t; + +typedef struct +{ + uint32_t target; + uint32_t type; + uint32_t addend; + int symbol; + int symbol_section; /* data section */ + int target_section; /* output section */ +} relocation_t; + +typedef struct +{ + uint32_t stub_addr; + uint32_t tramp_addr; + int stub_symbol; + int tramp_symbol; +} rpl_import_t; + +typedef struct +{ + const char *name; + size_t name_len; + size_t first; /* imports are made library by library */ + size_t count; +} rpl_library_t; + +typedef struct +{ + const char *name; + size_t name_len; + shdr_t h; + uint8_t *data; + size_t data_size; + size_t data_cap; + uint32_t index; + uint32_t signature; /* SHT_RPL_IMPORTS */ + int owned; /* data is ours to free */ + int reloc_section; + int section_symbol; +} out_section_t; + +/* Address to index, the first symbol or import at an address unless + * the rules in symbol_index_add() say otherwise. */ +typedef struct +{ + uint32_t *keys; + int *vals; + size_t cap; /* power of two; a slot is free when vals is NO_INDEX */ + size_t count; +} addr_map_t; + +typedef struct +{ + uint8_t *image; + size_t image_size; + uint32_t entry_point; + + in_section_t *in; + size_t in_count; + + data_section_t *data; + size_t data_count, data_cap; + + symbol_t *syms; + size_t sym_count, sym_cap; + + /* The symbol order: the first four, then the section symbols the + * output makes, then the rest in the order they were read. */ + int *order; + size_t order_count, order_cap; + int *sec_syms; + size_t sec_sym_count, sec_sym_cap; + + relocation_t *relocs; + size_t reloc_count, reloc_cap; + + rpl_import_t *imports; + size_t import_count, import_cap; + rpl_library_t *libs; + size_t lib_count, lib_cap; + + addr_map_t syms_by_addr; + addr_map_t imports_by_addr; + + out_section_t *out; + size_t out_count, out_cap; +} elf2rpl_t; + +/* ---- address maps ---- */ + +static INLINE size_t addr_hash(uint32_t key, size_t cap) +{ + return (size_t)((key * 0x9E3779B1u) >> 7) & (cap - 1); +} + +static bool addr_map_init(addr_map_t *m, size_t expect) +{ + size_t i, cap = 64; + while (cap < expect * 2) + cap *= 2; + m->keys = (uint32_t*)malloc(cap * sizeof(*m->keys)); + m->vals = (int*)malloc(cap * sizeof(*m->vals)); + m->cap = cap; + m->count = 0; + if (!m->keys || !m->vals) + return false; + for (i = 0; i < cap; i++) + m->vals[i] = NO_INDEX; + return true; +} + +static void addr_map_free(addr_map_t *m) +{ + free(m->keys); + free(m->vals); +} + +/* The slot for key: holding it, or the free one it would go in. */ +static size_t addr_map_slot(const addr_map_t *m, uint32_t key) +{ + size_t i = addr_hash(key, m->cap); + while (m->vals[i] != NO_INDEX && m->keys[i] != key) + i = (i + 1) & (m->cap - 1); + return i; +} + +static bool addr_map_grow(addr_map_t *m) +{ + addr_map_t bigger; + size_t i; + if (!addr_map_init(&bigger, m->cap)) + { + addr_map_free(&bigger); + return false; + } + for (i = 0; i < m->cap; i++) + { + if (m->vals[i] != NO_INDEX) + { + size_t s = addr_map_slot(&bigger, m->keys[i]); + bigger.keys[s] = m->keys[i]; + bigger.vals[s] = m->vals[i]; + bigger.count++; + } + } + addr_map_free(m); + *m = bigger; + return true; +} + +static int addr_map_find(const addr_map_t *m, uint32_t key) +{ + return m->vals[addr_map_slot(m, key)]; +} + +/* Stores key -> val unless key is there; returns the slot either way, + * and whether it was already there. */ +static bool addr_map_put(addr_map_t *m, uint32_t key, int val, + size_t *slot, bool *existed) +{ + size_t s; + if ((m->count + 1) * 2 > m->cap && !addr_map_grow(m)) + return false; + s = addr_map_slot(m, key); + *existed = m->vals[s] != NO_INDEX; + if (!*existed) + { + m->keys[s] = key; + m->vals[s] = val; + m->count++; + } + *slot = s; + return true; +} + +/* ---- symbols ---- */ + +static int symbol_new(elf2rpl_t *f, uint32_t address, const char *name, + size_t name_len, uint32_t size, uint8_t type, uint8_t binding) +{ + symbol_t *s; + if (!grow((void**)&f->syms, &f->sym_cap, f->sym_count + 1, sizeof(*f->syms))) + return NO_INDEX; + s = &f->syms[f->sym_count]; + s->name = name; + s->name_len = name_len; + s->address = address; + s->size = size; + s->out_name_pos = 0; + s->ref_count = 0; + s->out_index = NO_INDEX; + s->in_section = NO_INDEX; + s->out_section = NO_INDEX; + s->type = type; + s->binding = binding; + return (int)f->sym_count++; +} + +static void log_dup_sizes(const symbol_t *old, const symbol_t *sym) +{ + log_u(old->size); + log_s(" / "); + log_u(sym->size); + log_s(" "); + log_u(old->type); + log_s(" / "); + log_u(sym->type); + log_s(" "); + log_n(old->name, old->name_len); + log_s(" / "); + log_n(sym->name, sym->name_len); + log_s("\n"); +} + +/* One symbol per address: a typed symbol takes over from an untyped + * one, a section symbol from anything but a section at a non-zero + * address, a sized one from an unsized one at a non-zero address; + * otherwise the first stays. */ +static bool symbol_index_add(elf2rpl_t *f, int idx) +{ + size_t slot; + bool existed; + const symbol_t *sym = &f->syms[idx]; + const symbol_t *old; + + if (!addr_map_put(&f->syms_by_addr, sym->address, idx, &slot, &existed)) + return false; + if (!existed) + return true; + + old = &f->syms[f->syms_by_addr.vals[slot]]; + + if ( (old->type == STT_NOTYPE && sym->type != STT_NOTYPE) + || ( old->type != STT_SECTION + && sym->type == STT_SECTION + && sym->address != 0)) + { + if (verbose >= VERBOSE_DUP_SYMBOLS) + { + log_s("Duplicate symbol - changed type "); + log_hex = 1; + log_s("0x"); + log_u(sym->address); + log_s(" "); + log_n(old->name, old->name_len); + log_s(" / "); + log_n(sym->name, sym->name_len); + log_s("\n"); + } + f->syms_by_addr.vals[slot] = idx; + } + else if ((old->type != STT_SECTION || sym->type == STT_SECTION) + && sym->type != STT_NOTYPE && old->size == 0 + && sym->size != 0 && sym->size != 0xFFFFFFFFu && sym->address != 0) + { + if (verbose >= VERBOSE_DUP_SYMBOLS) + { + log_s("Duplicate symbol - changed size "); + log_hex = 1; + log_s("0x"); + log_u(sym->address); + log_s(" "); + log_dup_sizes(old, sym); + } + f->syms_by_addr.vals[slot] = idx; + } + else if ( (sym->name_len != old->name_len + || memcmp(sym->name, old->name, sym->name_len)) + && sym->address != 0) + { + if (verbose >= VERBOSE_DUP_SYMBOLS) + { + log_s("Duplicate symbol "); + log_hex = 1; + log_s("0x"); + log_u(sym->address); + log_s(" "); + log_dup_sizes(old, sym); + } + } + return true; +} + +static bool symbol_add(elf2rpl_t *f, int idx) +{ + if (!grow((void**)&f->order, &f->order_cap, f->order_count + 1, sizeof(*f->order))) + return false; + f->order[f->order_count++] = idx; + return symbol_index_add(f, idx); +} + +static bool symbol_add_section(elf2rpl_t *f, int idx) +{ + if (!symbol_index_add(f, idx)) + return false; + if (!grow((void**)&f->sec_syms, &f->sec_sym_cap, f->sec_sym_count + 1, + sizeof(*f->sec_syms))) + return false; + f->sec_syms[f->sec_sym_count++] = idx; + return true; +} + +static int symbol_find(const elf2rpl_t *f, uint32_t address) +{ + return addr_map_find(&f->syms_by_addr, address); +} + +static int import_find(const elf2rpl_t *f, uint32_t address) +{ + return addr_map_find(&f->imports_by_addr, address); +} + +/* ---- data sections ---- */ + +static INLINE bool data_section_contains(const data_section_t *s, uint32_t address) +{ + if (!(s->flags & SHF_ALLOC) || s->size == 0 || s->size == 0xFFFFFFFFu) + return false; + return address >= s->address && address <= s->address + s->size; +} + +static int data_section_find(const elf2rpl_t *f, uint32_t address) +{ + size_t i; + if (address == 0) + return f->data_count ? 0 : NO_INDEX; + for (i = 0; i < f->data_count; i++) + if (data_section_contains(&f->data[i], address)) + return (int)i; + return NO_INDEX; +} + +/* ---- input ---- */ + +static uint8_t *loader_data(elf2rpl_t *f, uint32_t address) +{ + size_t i; + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + uint32_t start = s->h.addr; + uint32_t end = start + s->data_size; + if (start <= address && end > address) + return s->data + (address - start); + } + return NULL; +} + +static const char *in_section_name(const elf2rpl_t *f, const in_section_t *strtab, + uint32_t off) +{ + (void)f; + if (!strtab->data || off >= strtab->data_size) + return ""; + return (const char*)strtab->data + off; +} + +static const uint8_t *in_symbol(const in_section_t *symtab, uint32_t index) +{ + if (!symtab->data || (uint64_t)index * SYM_SIZE + SYM_SIZE > symtab->data_size) + return NULL; + return symtab->data + (size_t)index * SYM_SIZE; +} + +static bool read_image(elf2rpl_t *f, const char *path) +{ + FILE *in = fopen(path, "rb"); + long len; + + if (!in) + { + printf("Could not open %s for reading\n", path); + return false; + } + if (fseek(in, 0, SEEK_END) != 0 || (len = ftell(in)) < 0 + || fseek(in, 0, SEEK_SET) != 0) + { + fclose(in); + printf("Could not open %s for reading\n", path); + return false; + } + f->image_size = (size_t)len; + /* One byte past the image, so a name at its very end still ends. */ + if (!(f->image = (uint8_t*)malloc(f->image_size + 1))) + { + fclose(in); + return false; + } + if (fread(f->image, 1, f->image_size, in) != f->image_size) + { + fclose(in); + printf("Could not open %s for reading\n", path); + return false; + } + f->image[f->image_size] = 0; + fclose(in); + return true; +} + +static bool read_elf(elf2rpl_t *f, const char *path) +{ + const uint8_t *eh; + const in_section_t *shstrtab; + uint32_t shoff; + unsigned shnum, shstrndx; + size_t i, sym_total = 0; + int *data_index; + int sym; + + if (!read_image(f, path)) + return false; + + eh = f->image; + if (f->image_size < EHDR_SIZE || rd32(eh) != ELF_MAGIC) + { + log_s("Invalid ELF magic header\n"); + return false; + } + if (eh[4] != ELFCLASS32) + { + log_s("Unexpected ELF file class\n"); + return false; + } + if (eh[5] != ELFDATA2MSB) + { + log_s("Unexpected ELF encoding\n"); + return false; + } + if (rd16(eh + 18) != EM_PPC) + { + log_s("Unexpected ELF machine type\n"); + return false; + } + if (eh[6] != EV_CURRENT) + { + log_s("Unexpected ELF version\n"); + return false; + } + + f->entry_point = rd32(eh + 24); + shoff = rd32(eh + 32); + shnum = rd16(eh + 48); + shstrndx = rd16(eh + 50); + + if ((uint64_t)shoff + (uint64_t)shnum * SHDR_SIZE > f->image_size + || shstrndx >= shnum) + { + log_s("Unexpected ELF section header table\n"); + return false; + } + + /* Section headers and where their contents lie */ + f->in_count = shnum; + if (!(f->in = (in_section_t*)calloc(shnum ? shnum : 1, sizeof(*f->in)))) + return false; + for (i = 0; i < shnum; i++) + { + const uint8_t *sh = f->image + shoff + i * SHDR_SIZE; + in_section_t *s = &f->in[i]; + s->h.name = rd32(sh); + s->h.type = rd32(sh + 4); + s->h.flags = rd32(sh + 8); + s->h.addr = rd32(sh + 12); + s->h.offset = rd32(sh + 16); + s->h.size = rd32(sh + 20); + s->h.link = rd32(sh + 24); + s->h.info = rd32(sh + 28); + s->h.addralign = rd32(sh + 32); + s->h.entsize = rd32(sh + 36); + + if (!s->h.size || s->h.type == SHT_NOBITS) + continue; + if ((uint64_t)s->h.offset + s->h.size > f->image_size) + { + log_s("Unexpected ELF section contents\n"); + return false; + } + s->data = f->image + s->h.offset; + s->data_size = s->h.size; + } + + shstrtab = &f->in[shstrndx]; + + /* Process any loader relocations */ + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + const in_section_t *symtab; + size_t r, count; + + if (s->h.type != SHT_RELA + || strcmp(in_section_name(f, shstrtab, s->h.name), ".rela.dyn")) + continue; + if (s->h.link >= f->in_count) + { + log_s("Unexpected relocation offset in .rela.dyn section\n"); + return false; + } + + symtab = &f->in[s->h.link]; + count = s->data_size / RELA_SIZE; + + for (r = 0; r < count; r++) + { + const uint8_t *rela = s->data + r * RELA_SIZE; + uint32_t off = rd32(rela); + uint32_t info = rd32(rela + 4); + uint32_t index = info >> 8; + const uint8_t *esym = in_symbol(symtab, index); + uint32_t addr; + uint8_t *ptr; + + if (!esym) + { + log_s("Unexpected relocation symbol index in .rela.dyn section\n"); + return false; + } + addr = rd32(esym + 4) + rd32(rela + 8); + + if (!(ptr = loader_data(f, off))) + { + log_s("Unexpected relocation offset in .rela.dyn section\n"); + return false; + } + + switch (info & 0xFF) + { + case R_PPC_RELATIVE: + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + { + log_s("Dyn relocation 0x"); + log_hex = 1; + log_u(off); + log_s(" 0x"); + log_u(rd32(ptr)); + log_s(" -> 0x"); + log_u(addr); + log_s("\n"); + } + wr32(ptr, addr); + break; + case R_PPC_NONE: + /* ignore padding */ + break; + default: + log_s("Unexpected relocation type in .rela.dyn section\n"); + return false; + } + } + } + + /* Read text/data sections */ + if (!(data_index = (int*)malloc((f->in_count ? f->in_count : 1) * sizeof(int)))) + return false; + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + const char *name; + data_section_t *d; + + data_index[i] = NO_INDEX; + + if (s->h.addr >= LOAD_ADDRESS && s->h.addr < CODE_ADDRESS) + continue; /* Skip any load sections */ + + name = in_section_name(f, shstrtab, s->h.name); + + if (s->h.type == SHT_PROGBITS || s->h.type == SHT_NOBITS) + { + if (!grow((void**)&f->data, &f->data_cap, f->data_count + 1, + sizeof(*f->data))) + { + free(data_index); + return false; + } + d = &f->data[f->data_count]; + d->name = name; + d->address = s->h.addr; + d->type = s->h.type; + d->flags = s->h.flags; + d->size = s->h.size; + d->out = NO_INDEX; + d->in_index = (int)i; + d->data = s->h.type == SHT_PROGBITS ? s->data : NULL; + d->data_size = s->h.type == SHT_PROGBITS ? s->data_size : 0; + data_index[i] = (int)f->data_count++; + } + + if (verbose >= VERBOSE_LOADED_SECTIONS) + { + if (s->h.type == SHT_PROGBITS) + log_s("Data section #"); + else if (s->h.type == SHT_NOBITS) + log_s("BSS section #"); + else + log_s("Section #"); + log_i((int32_t)i); + if (s->h.type != SHT_PROGBITS && s->h.type != SHT_NOBITS) + { + log_s(" type 0x"); + log_u(s->h.type); + } + log_s(" "); + log_s(name); + log_hex = 1; + log_s(": 0x"); + log_u(s->h.addr); + log_s(" - 0x"); + log_u(s->h.addr + s->h.size); + log_s("\n"); + } + } + + for (i = 0; i < f->in_count; i++) + if (f->in[i].h.type == SHT_SYMTAB) + sym_total += f->in[i].data_size / SYM_SIZE; + if ( !addr_map_init(&f->syms_by_addr, sym_total + 1024) + || !addr_map_init(&f->imports_by_addr, 1024)) + { + free(data_index); + return false; + } + + /* Default symbols: null, $TEXT, $DATA, $UNDEF */ + if ( (sym = symbol_new(f, 0, "", 0, 0, STT_NOTYPE, STB_LOCAL)) == NO_INDEX + || !symbol_add(f, sym) + || (sym = symbol_new(f, CODE_ADDRESS, "$TEXT", 5, 0, STT_SECTION, STB_LOCAL)) == NO_INDEX + || !symbol_add(f, sym) + || (sym = symbol_new(f, DATA_ADDRESS, "$DATA", 5, 0, STT_SECTION, STB_LOCAL)) == NO_INDEX + || !symbol_add(f, sym) + || (sym = symbol_new(f, 0, "$UNDEF", 6, 0, STT_OBJECT, STB_GLOBAL)) == NO_INDEX + || !symbol_add(f, sym)) + { + free(data_index); + return false; + } + + /* Read symbols */ + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + const in_section_t *strtab; + size_t n, count; + + if (s->h.type != SHT_SYMTAB) + continue; + + if (strcmp(in_section_name(f, shstrtab, s->h.name), ".symtab")) + { + log_s("Unexpected symbol section "); + log_s(in_section_name(f, shstrtab, s->h.name)); + log_s("\n"); + free(data_index); + return false; + } + if (s->h.link >= f->in_count) + { + free(data_index); + return false; + } + + strtab = &f->in[s->h.link]; + count = s->data_size / SYM_SIZE; + + for (n = 0; n < count; n++) + { + const uint8_t *es = s->data + n * SYM_SIZE; + uint32_t value = rd32(es + 4); + uint8_t info = es[12]; + uint8_t type = info & 0xF; + uint16_t shndx = rd16(es + 14); + const char *name; + symbol_t *ns; + + /* Skip any load symbols */ + if (value >= LOAD_ADDRESS && value < CODE_ADDRESS) + continue; + /* Skip null symbol */ + if (type == STT_NOTYPE && value == 0) + continue; + /* Skip file, section symbols */ + if (type == STT_FILE || type == STT_SECTION) + continue; + + name = in_section_name(f, strtab, rd32(es)); + if ((sym = symbol_new(f, value, name, strlen(name), rd32(es + 8), + type, (info >> 4) & 0xF)) == NO_INDEX) + { + free(data_index); + return false; + } + ns = &f->syms[sym]; + if (shndx < f->in_count && data_index[shndx] != NO_INDEX + && f->data[data_index[shndx]].size != 0) + ns->in_section = data_index[shndx]; + + if (verbose >= VERBOSE_LOADED_SYMBOLS) + { + log_s("Symbol "); + log_n(ns->name, ns->name_len); + log_s(" 0x"); + log_hex = 1; + log_u(ns->address); + log_s(" 0x"); + log_u(ns->size); + if (ns->in_section != NO_INDEX) + { + log_s(" ("); + log_s(f->data[ns->in_section].name); + log_s(")"); + } + log_s("\n"); + } + + if (!symbol_add(f, sym)) + { + free(data_index); + return false; + } + } + } + free(data_index); + + /* Read RPL imports */ + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + size_t n, count; + + if (strcmp(in_section_name(f, shstrtab, s->h.name), ".lib.rplLibs")) + continue; + + count = s->data_size / RPLLIBS_SIZE; + for (n = 0; n < count; n++) + { + const uint8_t *def = s->data + n * RPLLIBS_SIZE; + uint32_t stub_start = rd32(def + 4); + uint32_t stub_end = rd32(def + 8); + const char *lib_name = (const char*)loader_data(f, rd32(def)); + rpl_library_t *lib; + uint32_t stub_addr; + + if (!lib_name) + { + log_s("Unexpected import library name\n"); + return false; + } + if (!grow((void**)&f->libs, &f->lib_cap, f->lib_count + 1, sizeof(*f->libs))) + return false; + lib = &f->libs[f->lib_count++]; + lib->name = lib_name; + lib->name_len = strlen(lib_name); + lib->first = f->import_count; + lib->count = 0; + + if (verbose >= VERBOSE_LOADED_SECTIONS) + { + log_s("Import library "); + log_s(lib_name); + log_s("\n"); + } + + for (stub_addr = stub_start; stub_addr < stub_end; stub_addr += 4) + { + const uint8_t *stub = loader_data(f, stub_addr); + uint32_t tramp_addr; + int tramp, stub_sym; + symbol_t *ts; + rpl_import_t *imp; + size_t slot; + bool existed; + + if (!stub) + { + log_s("Unexpected import stub address\n"); + return false; + } + tramp_addr = rd32(stub); + + /* Get the tramp symbol */ + if ((tramp = symbol_find(f, tramp_addr)) == NO_INDEX) + { + log_s("No symbol found for import @ 0x"); + log_hex = 1; + log_u(tramp_addr); + log_s(" in "); + log_s(lib_name); + log_s("\n"); + return false; + } + ts = &f->syms[tramp]; + + if (verbose >= VERBOSE_LOADED_IMPORTS) + { + log_s("Import symbol "); + log_n(ts->name, ts->name_len); + log_s(" 0x"); + log_hex = 1; + log_u(stub_addr); + log_s(" -> 0x"); + log_u(tramp_addr); + log_s("\n"); + } + + /* A new symbol to use for the import, under the tramp's + * name; the tramp is renamed. */ + if ((stub_sym = symbol_new(f, 0, ts->name, ts->name_len, 0, + STT_FUNC, STB_GLOBAL)) == NO_INDEX) + return false; + ts = &f->syms[tramp]; + if (!(ts->name = name_join(ts->name, ts->name_len, "_tramp", 6))) + return false; + ts->name_len += 6; + + f->syms[stub_sym].ref_count++; + if (!symbol_add(f, stub_sym)) + return false; + + if (!grow((void**)&f->imports, &f->import_cap, f->import_count + 1, + sizeof(*f->imports))) + return false; + imp = &f->imports[f->import_count]; + imp->stub_addr = stub_addr; + imp->tramp_addr = tramp_addr; + imp->stub_symbol = stub_sym; + imp->tramp_symbol = tramp; + + if (!addr_map_put(&f->imports_by_addr, stub_addr, + (int)f->import_count, &slot, &existed)) + return false; + if (existed) + { + log_s("Duplicate import stub address "); + log_n(f->syms[stub_sym].name, f->syms[stub_sym].name_len); + log_s("\n"); + } + if (!addr_map_put(&f->imports_by_addr, tramp_addr, + (int)f->import_count, &slot, &existed)) + return false; + if (existed) + { + log_s("Duplicate import tramp address "); + log_n(f->syms[tramp].name, f->syms[tramp].name_len); + log_s("\n"); + } + f->import_count++; + lib->count++; + } + } + } + + /* Read relocations */ + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + const in_section_t *symtab; + const char *name; + size_t r, count; + + if (s->h.type != SHT_RELA) + continue; + name = in_section_name(f, shstrtab, s->h.name); + if (!strcmp(name, ".rela.dyn")) + continue; /* Skip dyn relocations */ + if (s->h.link >= f->in_count) + return false; + + symtab = &f->in[s->h.link]; + count = s->data_size / RELA_SIZE; + + if (!grow((void**)&f->relocs, &f->reloc_cap, f->reloc_count + count, + sizeof(*f->relocs))) + return false; + + for (r = 0; r < count; r++) + { + const uint8_t *rela = s->data + r * RELA_SIZE; + uint32_t off = rd32(rela); + uint32_t info = rd32(rela + 4); + uint32_t addend = rd32(rela + 8); + uint32_t type = info & 0xFF; + uint32_t index = info >> 8; + const uint8_t *esym = in_symbol(symtab, index); + relocation_t *rel; + int target_sym = NO_INDEX; + int target_sec = NO_INDEX; + int imp; + + if (!esym) + { + log_s("Unexpected relocation symbol index in "); + log_s(name); + log_s("\n"); + return false; + } + + /* A symbol still undefined after the link is a weak reference + * nothing defined. The link already wrote its final value - + * zero, or a branch to itself - which does not depend on where + * the module loads, so there is nothing for the loader to do. + * Handed to it, the relocation names an undefined symbol and a + * branch to address zero no 24-bit displacement reaches. */ + if (index != 0 && rd16(esym + 14) == SHN_UNDEF) + { + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + { + log_s("Relocation at 0x"); + log_hex = 1; + log_u(off); + log_s(" against an undefined weak symbol dropped\n"); + } + continue; + } + + if ((esym[12] & 0xF) == STT_SECTION && rd32(esym + 4) == CODE_ADDRESS + && (off < CODE_ADDRESS || off >= DATA_ADDRESS)) + { + log_s("Unexpected symbol referenced in relocation section "); + log_s(name); + log_s("\n"); + return false; + } + + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + { + log_s("Relocation #"); + log_hex = 0; + log_u((uint32_t)f->reloc_count); + log_s(" sym index "); + log_u(index); + log_s(" offset 0x"); + log_hex = 1; + log_u(off); + log_s(" addend 0x"); + log_u(addend); + } + + if ((imp = import_find(f, addend)) != NO_INDEX) + { + target_sym = f->imports[imp].stub_symbol; + addend = 0; + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + log_s(" -> import "); + } + else if ((target_sym = symbol_find(f, addend)) != NO_INDEX) + { + addend = 0; + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + log_s(" -> symbol "); + } + else if ((target_sec = data_section_find(f, addend)) != NO_INDEX) + { + addend -= f->data[target_sec].address; + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + log_s(" -> section "); + } + else + { + uint8_t *ptr; + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + log_s(" -> not found\n"); + + /* If we can't find a proper symbol, write the addend in and + * hope for the best. Stored in host order, as it always has + * been. */ + if ((ptr = loader_data(f, off)) != NULL) + memcpy(ptr, &addend, sizeof(addend)); + + log_s("Unexpected addend "); + log_hex = 1; + log_u(addend); + log_s(" referenced in relocation section "); + log_s(name); + log_s(", continuing.\n"); + continue; + } + + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + { + if (target_sym != NO_INDEX) + log_n(f->syms[target_sym].name, f->syms[target_sym].name_len); + else + log_s(f->data[target_sec].name); + if (addend != 0) + { + log_s(" + "); + log_u(addend); + } + log_s("\n"); + } + + if (target_sym != NO_INDEX) + f->syms[target_sym].ref_count++; + + rel = &f->relocs[f->reloc_count++]; + rel->target = off; + rel->type = type; + rel->addend = addend; + rel->symbol = target_sym; + rel->symbol_section = target_sec; + rel->target_section = NO_INDEX; + } + } + + /* Read dyn relocations */ + for (i = 0; i < f->in_count; i++) + { + const in_section_t *s = &f->in[i]; + const in_section_t *symtab; + size_t r, count; + + if (s->h.type != SHT_RELA + || strcmp(in_section_name(f, shstrtab, s->h.name), ".rela.dyn")) + continue; + + symtab = &f->in[s->h.link]; + count = s->data_size / RELA_SIZE; + + if (!grow((void**)&f->relocs, &f->reloc_cap, f->reloc_count + count, + sizeof(*f->relocs))) + return false; + + for (r = 0; r < count; r++) + { + const uint8_t *rela = s->data + r * RELA_SIZE; + uint32_t off = rd32(rela); + uint32_t info = rd32(rela + 4); + int32_t raddend = (int32_t)rd32(rela + 8); + uint32_t type = info & 0xFF; + uint32_t index = info >> 8; + const uint8_t *esym = in_symbol(symtab, index); + uint32_t addr, addend = 0; + relocation_t *rel; + int target_sym = NO_INDEX; + int target_sec = NO_INDEX; + + if (type == R_PPC_NONE) + continue; /* ignore padding */ + if (!esym) + { + log_s("Unexpected relocation symbol index in .rela.dyn section\n"); + return false; + } + addr = rd32(esym + 4) + (uint32_t)raddend; + + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + { + log_s("Dyn relocation #"); + log_hex = 0; + log_u((uint32_t)f->reloc_count); + log_s(" offset 0x"); + log_hex = 1; + log_u(off); + log_s(" addend 0x"); + log_i(raddend); + log_s(" addr 0x"); + log_u(addr); + } + + /* Index 0 is what R_PPC_RELATIVE carries */ + if (index == 0) + { + int imp; + addend = (uint32_t)raddend; + + if ((imp = import_find(f, addend)) != NO_INDEX) + { + target_sym = f->imports[imp].stub_symbol; + addend = 0; + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + log_s(" -> import "); + } + else if ((target_sym = symbol_find(f, addend)) != NO_INDEX) + { + addend -= f->syms[target_sym].address; + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + log_s(" -> symbol "); + } + else if ((target_sec = data_section_find(f, addend)) != NO_INDEX) + { + addend -= f->data[target_sec].address; + if (verbose >= VERBOSE_LOADED_RELOCATIONS) + log_s(" -> section "); + } + else + { + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + log_s(" -> bad\n"); + log_s("Unexpected relocation symbol address 0x"); + log_hex = 1; + log_u(addr); + log_s(" in .rela.dyn section\n"); + return false; + } + } + else + { + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + { + log_s(" -> index "); + log_hex = 0; + log_u(index); + log_s("\n"); + } + log_s("Unexpected relocation symbol index "); + log_hex = 0; + log_u(index); + log_s(" in .rela.dyn section\n"); + return false; + } + + if (verbose >= VERBOSE_LOADED_DYN_RELOC) + { + if (target_sym != NO_INDEX) + log_n(f->syms[target_sym].name, f->syms[target_sym].name_len); + else + log_s(f->data[target_sec].name); + if (addend != 0) + { + log_s(" + "); + log_u(addend); + } + log_s("\n"); + } + + if (type != R_PPC_RELATIVE) + { + log_s("Unexpected relocation type in .rela.dyn section\n"); + return false; + } + + /* Scrap any compiler/linker garbage */ + if (off >= CODE_ADDRESS && off < WIIU_LOAD_ADDRESS) + { + if (target_sym != NO_INDEX) + f->syms[target_sym].ref_count++; + rel = &f->relocs[f->reloc_count++]; + rel->target = off; + rel->type = R_PPC_ADDR32; + rel->addend = addend; + rel->symbol = target_sym; + rel->symbol_section = target_sec; + rel->target_section = NO_INDEX; + } + } + } + + return true; +} + +/* ---- output sections ---- */ + +static INLINE bool out_section_contains(const out_section_t *s, uint32_t address) +{ + if (!(s->h.flags & SHF_ALLOC) || s->h.size == 0 || s->h.size == 0xFFFFFFFFu) + return false; + return address >= s->h.addr && address <= s->h.addr + s->h.size; +} + +static int out_section_at(const elf2rpl_t *f, uint32_t address, int hint) +{ + size_t i; + if (address == 0) + return 0; + if (hint != NO_INDEX && out_section_contains(&f->out[hint], address)) + return hint; + for (i = 0; i < f->out_count; i++) + if (out_section_contains(&f->out[i], address)) + return (int)i; + return NO_INDEX; +} + +static int out_section_named(const elf2rpl_t *f, const char *name, size_t len) +{ + size_t i; + for (i = 0; i < f->out_count; i++) + if (f->out[i].name_len == len && !memcmp(f->out[i].name, name, len)) + return (int)i; + return NO_INDEX; +} + +static int out_section_new(elf2rpl_t *f, const char *name, size_t name_len) +{ + out_section_t *s; + if (!grow((void**)&f->out, &f->out_cap, f->out_count + 1, sizeof(*f->out))) + return NO_INDEX; + s = &f->out[f->out_count]; + memset(s, 0, sizeof(*s)); + s->name = name; + s->name_len = name_len; + s->index = 0xFFFFFFFFu; + s->reloc_section = NO_INDEX; + s->section_symbol = NO_INDEX; + return (int)f->out_count++; +} + +static bool out_append(out_section_t *s, const void *p, size_t n) +{ + if (!grow((void**)&s->data, &s->data_cap, s->data_size + n, 1)) + return false; + memcpy(s->data + s->data_size, p, n); + s->data_size += n; + s->owned = 1; + return true; +} + +static void log_out_section(const out_section_t *s) +{ + log_s("Output section #"); + log_hex = 0; + log_u(s->index); + log_hex = 1; + log_s(" "); + log_u(s->h.type); + log_s(" "); + log_u(s->h.flags); + log_s(" "); + log_n(s->name, s->name_len); + log_s(" (0x"); + log_u(s->h.addr); + log_s(" - 0x"); + log_u(s->h.addr + s->h.size); + log_s(")\n"); +} + +/* Numbers the section the next in order. With a symbol, a section of + * any size gets a section symbol among the first ones. */ +static bool out_section_add(elf2rpl_t *f, int idx, bool with_symbol) +{ + out_section_t *s = &f->out[idx]; + int sym = NO_INDEX; + + if (with_symbol && s->h.size != 0) + { + if ((sym = symbol_new(f, s->h.addr, s->name, s->name_len, 0xFFFFFFFFu, + STT_SECTION, STB_LOCAL)) == NO_INDEX) + return false; + s = &f->out[idx]; + f->syms[sym].out_section = idx; + s->section_symbol = sym; + } + s->index = (uint32_t)idx; + + if (verbose >= VERBOSE_OUTPUT_SECTIONS) + log_out_section(s); + + if (with_symbol || s->name_len) + { + int named = out_section_named(f, s->name, s->name_len); + if (named != NO_INDEX && named != idx) + { + log_s("Duplicate output section name "); + log_n(s->name, s->name_len); + log_s("\n"); + } + } + + if (sym == NO_INDEX) + return true; + + if (verbose >= VERBOSE_LOADED_SYMBOLS) + { + log_s("Section symbol "); + log_n(f->syms[sym].name, f->syms[sym].name_len); + log_s(" 0x"); + log_hex = 1; + log_u(f->syms[sym].address); + log_s(" 0x"); + log_u(f->syms[sym].size); + log_s("\n"); + } + + return symbol_add_section(f, sym); +} + +static void shdr_set(shdr_t *h, uint32_t type, uint32_t flags, uint32_t addr, + uint32_t size, uint32_t link, uint32_t info, uint32_t addralign, + uint32_t entsize) +{ + h->name = 0xFFFFFFFFu; + h->type = type; + h->flags = flags; + h->addr = addr; + h->offset = 0xFFFFFFFFu; + h->size = size; + h->link = link; + h->info = info; + h->addralign = addralign; + h->entsize = entsize; +} + +/* Gives each section a file offset, after the CRC and file info, + * data sections first, then those loaded at 0xC0000000, then the rest; + * returns where the next would go. */ +static uint32_t place(out_section_t *s, uint32_t at) +{ + if (s->h.type != SHT_NOBITS) + s->h.size = (uint32_t)s->data_size; + if (s->data_size) + { + s->h.offset = at; + return ALIGN_UP(at + (uint32_t)s->data_size, 64); + } + s->h.offset = 0; + return at; +} + +static bool write_rpl(elf2rpl_t *f, const char *path) +{ + size_t i, n, final_count; + int *final_order; + int idx, target = NO_INDEX, sym_hint = NO_INDEX; + int sym_text, sym_data, symtab, strtab, shstrtab, crcs, fileinfo; + uint32_t load_address, predict_str, predict_sym, predict_shstr; + uint32_t text_size = 0, data_size = 0, load_size = 0, temp_size = 0; + uint32_t shoff, data_offset, file_size, heap_size = 0x8000; + uint8_t *out, *p; + uint8_t zeros[16]; + FILE *fp; + + /* NULL section */ + if ((idx = out_section_new(f, "", 0)) == NO_INDEX) + return false; + f->out[idx].h.offset = 0; + if (!out_section_add(f, idx, false)) + return false; + + /* text/data sections */ + for (i = 0; i < f->data_count; i++) + { + data_section_t *d = &f->data[i]; + out_section_t *s; + + if ((idx = out_section_new(f, d->name, strlen(d->name))) == NO_INDEX) + return false; + s = &f->out[idx]; + shdr_set(&s->h, d->type, d->flags, d->address, + d->type == SHT_NOBITS ? d->size : d->data_size, 0, 0, 256, 0); + if (d->address == DATA_ADDRESS) + { + s->h.addralign = 4096; + s->h.flags |= SHF_WRITE; /* .rodata needs to be writable? */ + } + s->data = d->data; + s->data_size = d->data_size; + d->out = idx; + if (!out_section_add(f, idx, true)) + return false; + } + + /* Relocation sections, one per section relocated */ + for (i = 0; i < f->reloc_count; i++) + { + relocation_t *rel = &f->relocs[i]; + + if ((target = out_section_at(f, rel->target, target)) == NO_INDEX) + { + log_s("Error could not find section for relocation\n"); + return false; + } + rel->target_section = target; + + if (f->out[target].reloc_section == NO_INDEX) + { + const out_section_t *t = &f->out[target]; + char *name = name_join(".rela", 5, t->name, t->name_len); + if (!name) + return false; + if ((idx = out_section_new(f, name, strlen(name))) == NO_INDEX) + return false; + shdr_set(&f->out[idx].h, SHT_RELA, 0, 0, 0xFFFFFFFFu, 0xFFFFFFFFu, + f->out[target].index, 4, RELA_SIZE); + f->out[target].reloc_section = idx; + if (!out_section_add(f, idx, true)) + return false; + } + } + + /* Sizes of the symbol and string tables, so the RPL imports go + * after them */ + predict_str = 1; + predict_sym = 1; + predict_shstr = 1; + for (i = 0; i < 4 && i < f->order_count; i++) + { + predict_str += (uint32_t)f->syms[f->order[i]].name_len + 1; + predict_sym += SYM_SIZE; + } + for (i = 0; i < f->sec_sym_count; i++) + { + predict_str += (uint32_t)f->syms[f->sec_syms[i]].name_len + 1; + predict_sym += SYM_SIZE; + } + for (i = 4; i < f->order_count; i++) + { + predict_str += (uint32_t)f->syms[f->order[i]].name_len + 1; + predict_sym += SYM_SIZE; + } + for (i = 0; i < f->out_count; i++) + predict_shstr += (uint32_t)f->out[i].name_len + 1; + predict_str = ALIGN_UP(predict_str, 0x10); + predict_sym = ALIGN_UP(predict_sym, 0x10); + predict_shstr = ALIGN_UP(predict_shstr, 0x10); + load_address = WIIU_LOAD_ADDRESS + predict_str + predict_sym + predict_shstr; + + /* RPL import sections, .fimport_* */ + for (i = 0; i < f->lib_count; i++) + { + const rpl_library_t *lib = &f->libs[i]; + uint32_t name_size = ALIGN_UP(8 + lib->name_len, 8); + uint32_t stub_size = 8 + 8 * (uint32_t)lib->count; + uint32_t size = name_size > stub_size ? name_size : stub_size; + char *name = name_join(".fimport_", 9, lib->name, lib->name_len); + out_section_t *s; + + if (!name || (idx = out_section_new(f, name, 9 + lib->name_len)) == NO_INDEX) + return false; + s = &f->out[idx]; + shdr_set(&s->h, SHT_RPL_IMPORTS, SHF_ALLOC | SHF_EXECINSTR, load_address, + size, 0, 0, 4, 0); + + /* A name whose length is a multiple of eight ends one byte past + * the section; the terminator is kept out of it. */ + if (!(s->data = (uint8_t*)calloc(size + 1, 1))) + return false; + s->data_size = size; + s->data_cap = size + 1; + s->owned = 1; + wr32(s->data, (uint32_t)lib->count); + wr32(s->data + 4, 0); + memcpy(s->data + 8, lib->name, lib->name_len); + s->signature = 0; + + /* Addresses of the import symbols */ + for (n = 0; n < lib->count; n++) + f->syms[f->imports[lib->first + n].stub_symbol].address = + load_address + 8 + (uint32_t)n * 8; + + load_address = ALIGN_UP(load_address + size, 4); + + if (!out_section_add(f, idx, true)) + return false; + } + + /* Prune out unneeded symbols - but keep those used for relocations! + * The order: the first four, the section symbols, the rest. */ + if (!(final_order = (int*)malloc((f->order_count + f->sec_sym_count + 1) + * sizeof(int)))) + return false; + final_count = 0; + for (i = 0; i < f->order_count + f->sec_sym_count; i++) + { + int s; + const symbol_t *sym; + if (i < 4 && i < f->order_count) + s = f->order[i]; + else if (i < 4 + f->sec_sym_count && f->order_count >= 4) + s = f->sec_syms[i - 4]; + else if (f->order_count < 4) + s = f->sec_syms[i - f->order_count]; + else + s = f->order[i - f->sec_sym_count]; + + sym = &f->syms[s]; + if (sym->name_len && sym->type == STT_NOTYPE && sym->size == 0 + && sym->ref_count == 0) + { + if (verbose >= VERBOSE_PRUNED_SYMBOLS) + { + log_s("Pruning symbol "); + log_n(sym->name, sym->name_len); + log_s("\n"); + } + continue; + } + f->syms[s].out_index = (int)final_count; + final_order[final_count++] = s; + } + + /* From here on the symbol list does not change */ + sym_text = symbol_find(f, CODE_ADDRESS); + sym_data = symbol_find(f, DATA_ADDRESS); + + /* Convert relocations */ + for (i = 0; i < f->reloc_count; i++) + { + relocation_t *rel = &f->relocs[i]; + out_section_t *rs; + int sym, out_idx; + uint8_t rela[RELA_SIZE]; + + if (rel->target_section == NO_INDEX + || f->out[rel->target_section].reloc_section == NO_INDEX) + { + log_s("Error could not find section for relocation\n"); + free(final_order); + return false; + } + rs = &f->out[f->out[rel->target_section].reloc_section]; + + /* The symbol this relocation points to */ + if (rel->symbol == NO_INDEX && rel->symbol_section != NO_INDEX + && f->data[rel->symbol_section].out != NO_INDEX) + rel->symbol = f->out[f->data[rel->symbol_section].out].section_symbol; + + sym = rel->symbol; + if (sym == NO_INDEX) + { + log_s("Could not find matching symbol for relocation\n"); + free(final_order); + return false; + } + out_idx = f->syms[sym].out_index; + + if (verbose >= VERBOSE_CONVERT_RELOCATIONS || out_idx == NO_INDEX) + { + log_s("Converting relocation #"); + log_hex = 0; + log_u((uint32_t)i); + log_hex = 1; + log_s(" @ 0x"); + log_u(rel->target); + } + if (verbose >= VERBOSE_CONVERT_RELOCATIONS && out_idx != NO_INDEX) + { + log_s("-> symbol "); + log_hex = 0; + log_i(out_idx); + log_s(" "); + log_n(f->syms[sym].name, f->syms[sym].name_len); + log_s("\n"); + } + + /* A symbol not in the table but within DATA or TEXT: those + * symbols plus an addend */ + if (out_idx == NO_INDEX) + { + uint32_t a = f->syms[sym].address; + if (a >= CODE_ADDRESS && a < DATA_ADDRESS) + { + out_idx = 1; + rel->addend = a - CODE_ADDRESS; + rel->symbol = sym_text; + log_s("-> no symbol, using TEXT\n"); + } + else if (a >= DATA_ADDRESS && a < WIIU_LOAD_ADDRESS) + { + out_idx = 2; + rel->addend = a - DATA_ADDRESS; + rel->symbol = sym_data; + log_s("-> no symbol, using DATA\n"); + } + else + { + log_s("-> invalid\n"); + log_s("Could not find matching symbol for relocation\n"); + free(final_order); + return false; + } + } + + wr32(rela, rel->target); + wr32(rela + 4, (rel->type == R_PPC_RELATIVE ? R_PPC_ADDR32 : rel->type) + | ((uint32_t)out_idx << 8)); + wr32(rela + 8, rel->addend); + if (!out_append(rs, rela, RELA_SIZE)) + { + free(final_order); + return false; + } + } + + /* Symbol and string sections */ + if ( (symtab = out_section_new(f, ".symtab", 7)) == NO_INDEX + || !out_section_add(f, symtab, false) + || (strtab = out_section_new(f, ".strtab", 7)) == NO_INDEX + || !out_section_add(f, strtab, false) + || (shstrtab = out_section_new(f, ".shstrtab", 9)) == NO_INDEX + || !out_section_add(f, shstrtab, false)) + { + free(final_order); + return false; + } + + /* Relocation sections link to the symbol table */ + for (i = 0; i < f->out_count; i++) + { + out_section_t *s = &f->out[i]; + if (s->h.type == SHT_RELA) + s->h.link = (uint32_t)symtab; + if (s->h.type != SHT_NOBITS) + s->h.size = (uint32_t)s->data_size; + if (s->section_symbol != NO_INDEX) + { + f->syms[s->section_symbol].address = s->h.addr; + f->syms[s->section_symbol].size = s->h.size; + } + } + + /* .strtab: every symbol's name, in order */ + { + out_section_t *s = &f->out[strtab]; + size_t total = 1; + for (i = 0; i < final_count; i++) + if (f->syms[final_order[i]].name_len) + total += f->syms[final_order[i]].name_len + 1; + if (!(s->data = (uint8_t*)malloc(total))) + { + free(final_order); + return false; + } + s->owned = 1; + s->data_cap = total; + s->data[0] = 0; + s->data_size = 1; + for (i = 0; i < final_count; i++) + { + symbol_t *sym = &f->syms[final_order[i]]; + if (!sym->name_len) + { + sym->out_name_pos = 0; + continue; + } + sym->out_name_pos = (uint32_t)s->data_size; + memcpy(s->data + s->data_size, sym->name, sym->name_len); + s->data[s->data_size + sym->name_len] = 0; + s->data_size += sym->name_len + 1; + } + shdr_set(&s->h, SHT_STRTAB, SHF_ALLOC, 0, 0xFFFFFFFFu, 0, 0, 1, 0); + s->h.name = 0; + } + + /* .symtab */ + { + out_section_t *s = &f->out[symtab]; + if (!(s->data = (uint8_t*)malloc(final_count * SYM_SIZE + 1))) + { + free(final_order); + return false; + } + s->owned = 1; + s->data_cap = final_count * SYM_SIZE + 1; + shdr_set(&s->h, SHT_SYMTAB, SHF_ALLOC, 0, 0xFFFFFFFFu, (uint32_t)strtab, + 0, 4, SYM_SIZE); + s->h.name = 0; + + for (i = 0; i < final_count; i++) + { + const symbol_t *sym = &f->syms[final_order[i]]; + uint8_t *es = s->data + i * SYM_SIZE; + out_section_t *ss; + + if (sym->out_section != NO_INDEX) + sym_hint = sym->out_section; + else if (sym->in_section != NO_INDEX && f->data[sym->in_section].out != NO_INDEX) + sym_hint = f->data[sym->in_section].out; + else if (sym->type == STT_SECTION && sym->address == 0) + sym_hint = out_section_named(f, sym->name, sym->name_len); + else + sym_hint = out_section_at(f, sym->address, sym_hint); + + if (sym_hint == NO_INDEX) + { + log_s("Could not find section for symbol "); + log_n(sym->name, sym->name_len); + log_s(" 0x"); + log_hex = 1; + log_u(sym->address); + log_s("\n"); + free(final_order); + return false; + } + ss = &f->out[sym_hint]; + + wr32(es, sym->out_name_pos); + wr32(es + 4, sym->address); + wr32(es + 8, sym->size); + es[12] = (uint8_t)(sym->type | (sym->binding << 4)); + es[13] = 0; + wr16(es + 14, (uint16_t)ss->index); + + if (verbose >= VERBOSE_OUTPUT_SYMBOLS) + { + printf("%08lx %08lx %3u %-12.*s ", (unsigned long)sym->address, + (unsigned long)sym->size, (unsigned)(uint16_t)ss->index, + (int)ss->name_len, ss->name); + log_n(sym->name, sym->name_len); + log_s("\n"); + log_hex = 0; + } + + /* Compound the symbol into the import section's signature */ + if (ss->h.type == SHT_RPL_IMPORTS && sym->type != STT_SECTION) + ss->signature = encoding_crc32(ss->signature, + f->out[strtab].data + sym->out_name_pos, sym->name_len + 1); + } + s->data_size = final_count * SYM_SIZE; + } + free(final_order); + + /* Finish the SHT_RPL_IMPORTS signatures */ + memset(zeros, 0, sizeof(zeros)); + for (i = 0; i < f->out_count; i++) + { + out_section_t *s = &f->out[i]; + if (s->h.type != SHT_RPL_IMPORTS) + continue; + s->signature = encoding_crc32(s->signature, zeros, 0xE); + wr32(s->data + 4, s->signature); + } + + /* .shstrtab: every section's name */ + { + out_section_t *s = &f->out[shstrtab]; + size_t total = 1; + for (i = 0; i < f->out_count; i++) + if (f->out[i].name_len) + total += f->out[i].name_len + 1; + if (!(s->data = (uint8_t*)malloc(total))) + return false; + s->owned = 1; + s->data_cap = total; + s->data[0] = 0; + s->data_size = 1; + shdr_set(&s->h, SHT_STRTAB, SHF_ALLOC, 0, 0xFFFFFFFFu, 0, 0, 1, 0); + s->h.name = 0; + for (i = 0; i < f->out_count; i++) + { + out_section_t *o = &f->out[i]; + if (!o->name_len) + { + o->h.name = 0; + continue; + } + o->h.name = (uint32_t)s->data_size; + memcpy(s->data + s->data_size, o->name, o->name_len); + s->data[s->data_size + o->name_len] = 0; + s->data_size += o->name_len + 1; + } + } + + /* Addresses of symtab, strtab, shstrtab */ + f->out[symtab].h.addr = WIIU_LOAD_ADDRESS; + f->out[symtab].h.size = (uint32_t)f->out[symtab].data_size; + f->out[strtab].h.addr = ALIGN_UP(f->out[symtab].h.addr + predict_sym, 16); + f->out[strtab].h.size = (uint32_t)f->out[strtab].data_size; + f->out[shstrtab].h.addr = ALIGN_UP(f->out[strtab].h.addr + predict_str, 16); + f->out[shstrtab].h.size = (uint32_t)f->out[shstrtab].data_size; + + /* File info: text, data and load extents, and what the rest needs */ + for (i = 0; i < f->out_count; i++) + { + const out_section_t *s = &f->out[i]; + uint32_t size = s->h.type == SHT_NOBITS ? s->h.size : (uint32_t)s->data_size; + uint32_t end = s->h.addr + s->h.size; + + if (s->h.addr >= CODE_ADDRESS && s->h.addr < DATA_ADDRESS) + { + if (end - CODE_ADDRESS > text_size) + text_size = end - CODE_ADDRESS; + } + else if (s->h.addr >= DATA_ADDRESS && s->h.addr < WIIU_LOAD_ADDRESS) + { + if (end - DATA_ADDRESS > data_size) + data_size = end - DATA_ADDRESS; + } + else if (s->h.addr >= WIIU_LOAD_ADDRESS) + { + if (end - WIIU_LOAD_ADDRESS > load_size) + load_size = end - WIIU_LOAD_ADDRESS; + } + else if (s->h.addr == 0 && s->h.type != SHT_RPL_CRCS + && s->h.type != SHT_RPL_FILEINFO) + temp_size += size + 128; + } + + if ( (crcs = out_section_new(f, "", 0)) == NO_INDEX + || (fileinfo = out_section_new(f, "", 0)) == NO_INDEX) + return false; + shdr_set(&f->out[crcs].h, SHT_RPL_CRCS, 0, 0, 0xFFFFFFFFu, 0, 0, 4, 4); + f->out[crcs].h.name = 0; + shdr_set(&f->out[fileinfo].h, SHT_RPL_FILEINFO, 0, 0, 0xFFFFFFFFu, 0, 0, 4, 0); + f->out[fileinfo].h.name = 0; + { + out_section_t *s = &f->out[fileinfo]; + if (!(s->data = (uint8_t*)calloc(FILEINFO_SIZE, 1))) + return false; + s->owned = 1; + s->data_size = FILEINFO_SIZE; + s->data_cap = FILEINFO_SIZE; + p = s->data; + wr32(p + 0x00, 0xCAFE0402u); /* version */ + wr32(p + 0x04, text_size); + wr32(p + 0x08, 32); /* textAlign */ + wr32(p + 0x0C, data_size); + wr32(p + 0x10, 4096); /* dataAlign */ + wr32(p + 0x14, load_size); + wr32(p + 0x18, 4); /* loadAlign */ + wr32(p + 0x1C, temp_size); + wr32(p + 0x20, 0); /* trampAdjust */ + /* These were calculated based on observation, however some games + * differ. */ + wr32(p + 0x24, ALIGN_UP(DATA_ADDRESS + data_size + heap_size, 64)); + wr32(p + 0x28, ALIGN_UP(DATA_ADDRESS + heap_size, 64)); + wr32(p + 0x2C, 0x10000); /* stackSize */ + wr32(p + 0x30, 0); /* filename */ + wr32(p + 0x34, RPL_IS_RPX); /* flags */ + wr32(p + 0x38, heap_size); + wr32(p + 0x3C, 0); /* tagOffset */ + wr32(p + 0x40, 0x5078); /* minVersion */ + wr32(p + 0x44, 0xFFFFFFFFu); /* compressionLevel -1 */ + wr32(p + 0x48, 0); /* trampAddition */ + wr32(p + 0x4C, 0); /* fileInfoPad */ + wr32(p + 0x50, 0x51BA); /* cafeSdkVersion */ + wr32(p + 0x54, 0xCCD1); /* cafeSdkRevision */ + wr16(p + 0x58, 0); /* tlsModuleIndex */ + wr16(p + 0x5A, 0); /* tlsAlignShift */ + wr32(p + 0x5C, 0); /* runtimeFileInfoSize */ + } + + if (!out_section_add(f, crcs, false) || !out_section_add(f, fileinfo, false)) + return false; + + /* SHT_RPL_CRCS: a CRC of every section's contents, its own empty */ + { + out_section_t *s = &f->out[crcs]; + size_t count = f->out_count; + if (!(s->data = (uint8_t*)malloc(count * 4))) + return false; + s->owned = 1; + s->data_cap = count * 4; + for (i = 0; i < count; i++) + { + const out_section_t *o = &f->out[i]; + uint32_t crc = (i != (size_t)crcs && o->data_size) + ? encoding_crc32(0, o->data, o->data_size) : 0; + wr32(s->data + i * 4, crc); + } + s->data_size = count * 4; + } + + /* Section sizes and offsets */ + shoff = ALIGN_UP(EHDR_SIZE, 64); + data_offset = ALIGN_UP(shoff + (uint32_t)f->out_count * SHDR_SIZE, 64); + + for (i = 0; i < f->out_count; i++) + if (f->out[i].h.type == SHT_RPL_CRCS || f->out[i].h.type == SHT_RPL_FILEINFO) + data_offset = place(&f->out[i], data_offset); + + for (i = 0; i < f->out_count; i++) + { + out_section_t *s = &f->out[i]; + if (s->h.offset != 0xFFFFFFFFu + || s->h.addr < DATA_ADDRESS || s->h.addr >= WIIU_LOAD_ADDRESS) + continue; + if (s->h.type != SHT_NOBITS) + s->h.size = (uint32_t)s->data_size; + s->h.offset = data_offset; + data_offset = ALIGN_UP(data_offset + (uint32_t)s->data_size, 64); + } + + for (i = 0; i < f->out_count; i++) + { + out_section_t *s = &f->out[i]; + if (s->h.offset != 0xFFFFFFFFu || s->h.addr < WIIU_LOAD_ADDRESS) + continue; + data_offset = place(s, data_offset); + } + + for (i = 0; i < f->out_count; i++) + if (f->out[i].h.offset == 0xFFFFFFFFu) + data_offset = place(&f->out[i], data_offset); + + /* The file, laid out whole */ + file_size = shoff + (uint32_t)f->out_count * SHDR_SIZE; + for (i = 0; i < f->out_count; i++) + { + const out_section_t *s = &f->out[i]; + if (s->data_size && s->h.offset + (uint32_t)s->data_size > file_size) + file_size = s->h.offset + (uint32_t)s->data_size; + } + if (!(out = (uint8_t*)calloc(file_size, 1))) + return false; + + wr32(out, ELF_MAGIC); + out[4] = ELFCLASS32; + out[5] = ELFDATA2MSB; + out[6] = EV_CURRENT; + wr16(out + 7, EABI_CAFE); + wr16(out + 16, 0xFE01); /* type */ + wr16(out + 18, EM_PPC); + wr32(out + 20, 1); /* version */ + wr32(out + 24, f->entry_point); + wr32(out + 28, 0); /* phoff */ + wr32(out + 32, shoff); + wr32(out + 36, 0); /* flags */ + wr16(out + 40, EHDR_SIZE); + wr16(out + 42, 0); /* phentsize */ + wr16(out + 44, 0); /* phnum */ + wr16(out + 46, SHDR_SIZE); + wr16(out + 48, (uint16_t)f->out_count); + wr16(out + 50, (uint16_t)shstrtab); + + for (i = 0; i < f->out_count; i++) + { + const out_section_t *s = &f->out[i]; + p = out + shoff + i * SHDR_SIZE; + wr32(p, s->h.name); + wr32(p + 4, s->h.type); + wr32(p + 8, s->h.flags); + wr32(p + 12, s->h.addr); + wr32(p + 16, s->h.offset); + wr32(p + 20, s->h.size); + wr32(p + 24, s->h.link); + wr32(p + 28, s->h.info); + wr32(p + 32, s->h.addralign); + wr32(p + 36, s->h.entsize); + if (s->data_size) + memcpy(out + s->h.offset, s->data, s->data_size); + } + + if (!(fp = fopen(path, "wb"))) + { + printf("Could not open %s for writing\n", path); + free(out); + return false; + } + if (fwrite(out, 1, file_size, fp) != file_size) + { + fclose(fp); + free(out); + return false; + } + fclose(fp); + free(out); + return true; +} + +static void elf2rpl_free(elf2rpl_t *f) +{ + size_t i; + for (i = 0; i < f->out_count; i++) + if (f->out[i].owned) + free(f->out[i].data); + free(f->out); + free(f->relocs); + free(f->imports); + free(f->libs); + free(f->order); + free(f->sec_syms); + free(f->syms); + free(f->data); + free(f->in); + free(f->image); + if (f->syms_by_addr.vals) + addr_map_free(&f->syms_by_addr); + if (f->imports_by_addr.vals) + addr_map_free(&f->imports_by_addr); + names_free(); +} + +int main(int argc, char **argv) +{ + int arg_start = 1; + int ret = -1; + elf2rpl_t f; + + while (arg_start < argc && argv[arg_start][0] == '-') + { + if (!strcmp(argv[arg_start], "--")) + { + ++arg_start; + break; + } + if (strcmp(argv[arg_start], "-v")) + { + printf("Unknown option %s\n", argv[arg_start]); + return -1; + } + ++verbose; + ++arg_start; + } + + if (argc < arg_start + 2) + { + printf("Usage: %s \n", argv[0]); + return -1; + } + + memset(&f, 0, sizeof(f)); + if (read_elf(&f, argv[arg_start]) && write_rpl(&f, argv[arg_start + 1])) + ret = 0; + fflush(stdout); + elf2rpl_free(&f); + return ret; +} diff --git a/os/wiiu/wut/elf2rpl/main.cpp b/os/wiiu/wut/elf2rpl/main.cpp deleted file mode 100644 index 4dad40ac7d..0000000000 --- a/os/wiiu/wut/elf2rpl/main.cpp +++ /dev/null @@ -1,1794 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "crc32.h" -#include "elf.h" - -#pragma pack(push, 1) - -struct RplLibsDef -{ - be_val name; - be_val stubStart; - be_val stubEnd; -}; - -#pragma pack(pop) - -static const uint32_t LoadAddress = 0x01000000u; -static const uint32_t CodeAddress = 0x02000000u; -static const uint32_t DataAddress = 0x10000000u; -static const uint32_t WiiuLoadAddress = 0xC0000000u; - -int verbose = 0; -constexpr int verbose_loaded_sections = 1; -constexpr int verbose_loaded_symbols = 3; -constexpr int verbose_loaded_imports = 2; -constexpr int verbose_loaded_relocations = 4; -constexpr int verbose_loaded_dyn_reloc = 4; -constexpr int verbose_pruned_symbols = 2; -constexpr int verbose_convert_relocations = 4; -constexpr int verbose_dup_symbols = 3; -constexpr int verbose_output_sections = 1; -constexpr int verbose_output_symbols = 2; - -struct OutputSection; -using OutputSectionPtr = std::shared_ptr; - -struct ElfFile -{ - struct DataSection - { - std::string name; - uint32_t address = 0; - elf::SectionType type = elf::SHT_NULL; - elf::SectionFlags flags = elf::SectionFlags(0); - OutputSectionPtr outSection; - int inIndex = -1; - - /* Data used if type == SHT_PROGBITS */ - std::vector data; - - /* Size used if type == SHT_NOBITS */ - uint32_t size = 0; - - DataSection(uint32_t _address, std::string _name, - elf::SectionType _type, elf::SectionFlags _flags) - : name(_name), address(_address), type(_type), flags(_flags) - {} - - static std::shared_ptr - New(uint32_t _address, std::string _name, - elf::SectionType _type, elf::SectionFlags _flags) - { - return std::make_shared(_address, _name, _type, _flags); - } - - bool contains(uint32_t _address, uint32_t _size = 0) - { - - if (!(flags & elf::SHF_ALLOC) || size == 0 || size == uint32_t(-1)) - return false; - - auto start = address; - auto end = start + size; - - if (_address >= start && _address + _size <= end) - return true; - - return false; - } - }; - - using DataSectionPtr = std::shared_ptr; - using DataSectionList = std::vector; - - struct Symbol - { - std::string name; - uint32_t address = 0; - uint32_t size = 0; - elf::SymbolType type = elf::STT_NOTYPE; - elf::SymbolBinding binding = elf::STB_LOCAL; - uint32_t outNamePos = 0; - uint32_t refCount = 0; - int inIndex = -1; - int outIndex = -1; - DataSectionPtr inSection; - OutputSectionPtr outSection; - - Symbol(uint32_t _address, const std::string& _name, - uint32_t _size, elf::SymbolType _type, elf::SymbolBinding _binding) - : name(_name), address(_address), size(_size), type(_type), binding(_binding) - {} - - static std::shared_ptr - New(uint32_t _address, const std::string& _name, - uint32_t _size, elf::SymbolType _type, elf::SymbolBinding _binding) - { - return std::make_shared(_address, _name, _size, _type, _binding); - } - }; - - using SymbolPtr = std::shared_ptr; - using SymbolList = std::vector; - using SymbolByAddrMap = std::map; - - struct Relocation - { - uint32_t target = 0; - elf::RelocationType type = elf::R_PPC_NONE; - - SymbolPtr symbol; - DataSectionPtr symbolSection; - uint32_t addend = 0; - OutputSectionPtr targetSection; - }; - - using RelocationPtr = std::shared_ptr; - using RelocationList = std::vector; - - struct RplImport - { - SymbolPtr trampSymbol; - SymbolPtr stubSymbol; - uint32_t stubAddr = 0; - uint32_t trampAddr = 0; - - RplImport(uint32_t _stubAddr, uint32_t _trampAddr, - SymbolPtr _stubSymbol, SymbolPtr _trampSymbol) - : trampSymbol(_trampSymbol), stubSymbol(_stubSymbol), - stubAddr(_stubAddr), trampAddr(_trampAddr) - {} - - static std::shared_ptr - New(uint32_t _stubAddr, uint32_t _trampAddr, - SymbolPtr _stubSymbol, SymbolPtr _trampSymbol) - { - return std::make_shared(_stubAddr, _trampAddr, _stubSymbol, _trampSymbol); - } - }; - - using RplImportPtr = std::shared_ptr; - using RplImportList = std::vector; - using RplImportByAddrMap = std::map; - - struct RplImportLibrary - { - std::string name; - RplImportList imports; - - RplImportLibrary(const std::string& _name) - : name(_name) - {} - - static std::shared_ptr - New(const std::string& _name) - { - return std::make_shared(_name); - } - - void - addImport(const RplImportPtr& import) - { - imports.emplace_back(import); - } - }; - - using RplImportLibraryPtr = std::shared_ptr; - using RplImportLibraryList = std::vector; - - uint32_t entryPoint = 0; - -private: - DataSectionList dataSections; - SymbolList symbols; - RelocationList relocations; - RplImportLibraryList rplImports; - - SymbolByAddrMap symbolsByAddr; - RplImportByAddrMap importsByAddr; - -public: - void - addDataSection(const DataSectionPtr& section) - { - dataSections.emplace_back(section); - } - - void - addSymbolToIndex(const SymbolPtr& symbol) - { - auto iter = symbolsByAddr.find(symbol->address); - if (iter != symbolsByAddr.end()) - { - if ((iter->second->type == elf::STT_NOTYPE - && symbol->type != elf::STT_NOTYPE) - || (iter->second->type != elf::STT_SECTION - && symbol->type == elf::STT_SECTION - && symbol->address != 0)) - { - if (verbose >= verbose_dup_symbols) - { - std::cout << "Duplicate symbol - changed type " - << std::hex << "0x" << symbol->address << " " - << iter->second->name << " / " << symbol->name - << std::endl; - } - iter->second = symbol; - } - else if ((iter->second->type != elf::STT_SECTION || symbol->type == elf::STT_SECTION) - && symbol->type != elf::STT_NOTYPE && iter->second->size == 0 - && symbol->size != 0 && symbol->size != uint32_t(-1) && symbol->address != 0) - { - if (verbose >= verbose_dup_symbols) - { - std::cout << "Duplicate symbol - changed size " - << std::hex << "0x" << symbol->address << " " - << iter->second->size << " / " << symbol->size << " " - << iter->second->type << " / " << symbol->type << " " - << iter->second->name << " / " << symbol->name - << std::endl; - } - iter->second = symbol; - } - else - { - if (symbol->name != iter->second->name && symbol->address != 0) - { - if (verbose >= verbose_dup_symbols) - { - std::cout << "Duplicate symbol " - << std::hex << "0x" << symbol->address << " " - << iter->second->size << " / " << symbol->size << " " - << iter->second->type << " / " << symbol->type << " " - << iter->second->name << " / " << symbol->name - << std::endl; - } - } - } - } - else - { - symbolsByAddr.emplace(symbol->address, symbol); - } - } - - void - addSymbol(const SymbolPtr& symbol) - { - symbols.emplace_back(symbol); - addSymbolToIndex(symbol); - } - - SymbolList::iterator - insertSymbol(SymbolList::iterator iter, const SymbolPtr& symbol) - { - addSymbolToIndex(symbol); - return symbols.insert(iter, symbol); - } - - void - addImport(const RplImportLibraryPtr& lib, - const RplImportPtr& import) - { - lib->addImport(import); - auto result = importsByAddr.emplace(import->stubAddr, import); - if (!result.second) - { - std::cout << "Duplicate import stub address " - << import->stubSymbol->name << std::endl; - } - result = importsByAddr.emplace(import->trampAddr, import); - if (!result.second) - { - std::cout << "Duplicate import tramp address " - << import->trampSymbol->name << std::endl; - } - } - - void - addImportLibrary(const RplImportLibraryPtr& lib) - { - rplImports.emplace_back(lib); - } - - void - addRelocation(const RelocationPtr& relocation) - { - relocations.emplace_back(relocation); - } - - SymbolPtr - findSymbol(uint32_t address) - { - auto iter = symbolsByAddr.find(address); - if (iter != symbolsByAddr.end()) - return iter->second; - return nullptr; - } - - RplImportPtr - findImport(uint32_t address) - { - auto iter = importsByAddr.find(address); - if (iter != importsByAddr.end()) - return iter->second; - return nullptr; - } - - DataSectionPtr - findSection(uint32_t address, - const DataSectionPtr& hint = nullptr) - { - if (address == 0) - return dataSections[0]; - - if (hint && hint->contains(address)) - return hint; - - for (auto §ion : dataSections) { - if (section->contains(address)) - return section; - } - - return nullptr; - } - - DataSectionList& - getDataSections() - { - return dataSections; - } - - SymbolList& - getSymbols() - { - return symbols; - } - - RelocationList& - getRelocations() - { - return relocations; - } - - RplImportLibraryList& - getImportLibs() - { - return rplImports; - } - - void - pruneSymbols() - { - SymbolList new_symbols; - new_symbols.resize(symbols.size()); - unsigned d = 0; - - /* Prune out unneeded symbols - but keep those used for relocations! */ - for (unsigned s = 0u; s < symbols.size(); ++s) - { - if (!symbols[s]->name.empty() && symbols[s]->type == elf::STT_NOTYPE - && symbols[s]->size == 0 && symbols[s]->refCount == 0) - { - if (verbose >= verbose_pruned_symbols) - { - std::cout << "Pruning symbol " << symbols[s]->name << std::endl; - } - } - else - { - std::swap(new_symbols[d], symbols[s]); - ++d; - } - } - - new_symbols.resize(d); - std::swap(new_symbols, symbols); - } - - void - indexSymbols() - { - for (unsigned i = 0; i < symbols.size(); ++i) - { - symbols[i]->outIndex = i; - } - } -}; - -struct InputSection -{ - elf::SectionHeader header; - std::vector data; -}; - -template -static Type * -getLoaderDataPtr(std::vector &inSections, uint32_t address) -{ - for (auto §ion : inSections) - { - auto start = section.header.addr; - auto end = start + section.data.size(); - - if (start <= address && end > address) - { - auto offset = address - start; - return reinterpret_cast(section.data.data() + offset); - } - } - - return nullptr; -} - -static elf::Symbol * -getSectionSymbol(InputSection §ion, size_t index) -{ - auto symbols = reinterpret_cast(section.data.data()); - return &symbols[index]; -}; - -static bool -read(ElfFile &file, const std::string &filename) -{ - std::ifstream in { filename, std::ifstream::binary }; - std::vector inSections; - ElfFile::DataSectionList dataSectionIndex; - - if (!in.is_open()) { - std::cout << "Could not open " << filename << " for reading" << std::endl; - return false; - } - - /* Read header */ - elf::Header header; - in.read(reinterpret_cast(&header), sizeof(elf::Header)); - - if (header.magic != elf::HeaderMagic) { - std::cout << "Invalid ELF magic header" << std::endl; - return false; - } - - if (header.fileClass != elf::ELFCLASS32) { - std::cout << "Unexpected ELF file class" << std::endl; - return false; - } - - if (header.encoding != elf::ELFDATA2MSB) { - std::cout << "Unexpected ELF encoding" << std::endl; - return false; - } - - if (header.machine != elf::EM_PPC) { - std::cout << "Unexpected ELF machine type" << std::endl; - return false; - } - - if (header.elfVersion != elf::EV_CURRENT) { - std::cout << "Unexpected ELF version" << std::endl; - return false; - } - - file.entryPoint = header.entry; - - /* Read section headers and data */ - in.seekg(static_cast(header.shoff)); - inSections.resize(header.shnum); - dataSectionIndex.resize(header.shnum); - - for (auto §ion : inSections) { - in.read(reinterpret_cast(§ion.header), sizeof(elf::SectionHeader)); - - if (!section.header.size || section.header.type == elf::SHT_NOBITS) { - continue; - } - - auto pos = in.tellg(); - in.seekg(static_cast(section.header.offset)); - section.data.resize(section.header.size); - in.read(section.data.data(), section.data.size()); - in.seekg(pos); - } - - auto shStrTab = inSections[header.shstrndx].data.data(); - - /* Process any loader relocations */ - for (auto §ion : inSections) - { - if (section.header.type != elf::SHT_RELA) - continue; - - auto name = std::string { shStrTab + section.header.name }; - - if (name.compare(".rela.dyn") != 0) - continue; - - auto symSection = inSections[section.header.link]; - auto relas = reinterpret_cast(section.data.data()); - auto count = section.data.size() / sizeof(elf::Rela); - - for (unsigned i = 0u; i < count; ++i) - { - auto &rela = relas[i]; - auto index = rela.info >> 8; - auto symbol = getSectionSymbol(symSection, index); - auto addr = symbol->value + rela.addend; - - auto type = rela.info & 0xff; - auto ptr = getLoaderDataPtr(inSections, rela.offset); - - if (!ptr) - { - std::cout << "Unexpected relocation offset in .rela.dyn section" << std::endl; - return false; - } - - switch (type) { - case elf::R_PPC_RELATIVE: - if (verbose >= verbose_loaded_dyn_reloc) - { - std::cout << "Dyn relocation 0x" << std::hex << rela.offset << " 0x" << byte_swap(*ptr) << " -> 0x" << addr << std::endl; - } - *ptr = byte_swap(addr); - break; - case elf::R_PPC_NONE: - /* ignore padding */ - break; - default: - std::cout << "Unexpected relocation type in .rela.dyn section" << std::endl; - return false; - } - } - } - - int index = -1; - /* Read text/data sections */ - for (auto §ion : inSections) { - - ++index; - - if (section.header.addr >= LoadAddress && section.header.addr < CodeAddress) { - /* Skip any load sections */ - continue; - } - - auto name = std::string { shStrTab + section.header.name }; - auto address = section.header.addr.value(); - auto type = elf::SectionType(section.header.type.value()); - auto flags = elf::SectionFlags(section.header.flags.value()); - auto size = section.header.size.value(); - - if (type == elf::SHT_PROGBITS) { - auto data = ElfFile::DataSection::New(address, name, type, flags); - data->data = section.data; - data->size = size; - data->inIndex = index; - dataSectionIndex[index] = data; - file.addDataSection(data); - - if (verbose >= verbose_loaded_sections) - { - std::cout << "Data section #" << index << ' ' << name - << std::hex << ": 0x" << address << " - 0x" << address + size << std::endl; - } - } else if (type == elf::SHT_NOBITS) { - auto bss = ElfFile::DataSection::New(address, name, type, flags); - bss->size = size; - bss->inIndex = index; - dataSectionIndex[index] = bss; - file.addDataSection(bss); - - if (verbose >= verbose_loaded_sections) - { - std::cout << "BSS section #" << index << ' ' << name - << std::hex << ": 0x" << address << " - 0x" << address + size << std::endl; - } - } else { - if (verbose >= verbose_loaded_sections) - { - std::cout << "Section #" << index << " type 0x" << type << ' ' << name - << std::hex << ": 0x" << address << " - 0x" << address + size << std::endl; - } - } - } - - /* Default symbols */ - auto symNull = ElfFile::Symbol::New(0, "", 0, elf::STT_NOTYPE, elf::STB_LOCAL); - file.addSymbol(symNull); - - auto symText = ElfFile::Symbol::New(CodeAddress, "$TEXT", 0, elf::STT_SECTION, elf::STB_LOCAL); - file.addSymbol(symText); - - auto symData = ElfFile::Symbol::New(DataAddress, "$DATA", 0, elf::STT_SECTION, elf::STB_LOCAL); - file.addSymbol(symData); - - auto symUndef = ElfFile::Symbol::New(0, "$UNDEF", 0, elf::STT_OBJECT, elf::STB_GLOBAL); - file.addSymbol(symUndef); - - /* Read symbols */ - for (auto §ion : inSections) - { - if (section.header.type != elf::SHT_SYMTAB) - continue; - - auto name = std::string { shStrTab + section.header.name }; - - if (name.compare(".symtab") != 0) - { - std::cout << "Unexpected symbol section " << name << std::endl; - return false; - } - - auto strTab = inSections[section.header.link].data.data(); - auto symTab = reinterpret_cast(section.data.data()); - auto count = section.data.size() / sizeof(elf::Symbol); - - for (unsigned i = 0u; i < count; ++i) - { - auto &sym = symTab[i]; - - /* Skip any load symbols */ - if (sym.value >= LoadAddress && sym.value < CodeAddress) - continue; - - auto type = elf::SymbolType(sym.info & 0xF); - auto binding = elf::SymbolBinding((sym.info >> 4) & 0xF); - - if (type == elf::STT_NOTYPE && sym.value == 0) { - /* Skip null symbol */ - continue; - } - - if (type == elf::STT_FILE || type == elf::STT_SECTION) { - /* Skip file, section symbols */ - continue; - } - - auto name = strTab + sym.name; - auto symbol = ElfFile::Symbol::New(sym.value, name, sym.size, type, binding); - auto inSection = dataSectionIndex[sym.shndx]; - if (inSection && inSection->size != 0) - symbol->inSection = dataSectionIndex[sym.shndx]; - - if (verbose >= verbose_loaded_symbols) - { - std::cout << "Symbol " << symbol->name << " 0x" << std::hex << symbol->address - << " 0x" << symbol->size; - if (symbol->inSection) - { - std::cout << " (" << symbol->inSection->name << ')'; - } - std::cout << '\n'; - } - - file.addSymbol(symbol); - } - } - - /* Read RPL imports */ - for (auto §ion : inSections) { - auto name = std::string { shStrTab + section.header.name }; - - if (name.compare(".lib.rplLibs") != 0) - continue; - - auto rplTab = reinterpret_cast(section.data.data()); - auto count = section.data.size() / sizeof(RplLibsDef); - - for (unsigned i = 0u; i < count; ++i) - { - auto &rpl = rplTab[i]; - auto libName = getLoaderDataPtr(inSections, rpl.name); - auto lib = ElfFile::RplImportLibrary::New(libName); - - if (verbose >= verbose_loaded_sections) - { - std::cout << "Import library " << libName << std::endl; - } - - for (auto stubAddr = rpl.stubStart; stubAddr < rpl.stubEnd; stubAddr += 4) { - auto trampAddr = byte_swap(*getLoaderDataPtr(inSections, stubAddr)); - - /* Get the tramp symbol */ - auto trampSymbol = file.findSymbol(trampAddr); - - if (!trampSymbol) - { - std::cout << "No symbol found for import @ 0x" << std::hex << trampAddr << " in " << libName << std::endl; - } - - if (verbose >= verbose_loaded_imports) - { - std::cout << "Import symbol " << trampSymbol->name << " 0x" << std::hex << stubAddr << " -> 0x" << trampAddr << std::endl; - } - - /* Create a new symbol to use for the import */ - auto stubSymbol = ElfFile::Symbol::New(0, trampSymbol->name, 0, elf::STT_FUNC, elf::STB_GLOBAL); - auto import = ElfFile::RplImport::New(stubAddr, trampAddr, stubSymbol, trampSymbol); - - /* Rename tramp symbol */ - /* These are not needed anymore, right */ - /* trampSymbol->refCount++; */ - trampSymbol->name += "_tramp"; - - stubSymbol->refCount++; - file.addSymbol(stubSymbol); - file.addImport(lib, import); - } - - file.addImportLibrary(lib); - } - } - - ElfFile::DataSectionPtr prevSection; - - /* Read relocations */ - for (auto §ion : inSections) { - if (section.header.type != elf::SHT_RELA) - continue; - - auto name = std::string { shStrTab + section.header.name }; - - if (name.compare(".rela.dyn") == 0) { - /* Skip dyn relocations */ - continue; - } - - auto symTab = reinterpret_cast(inSections[section.header.link].data.data()); - auto relTab = reinterpret_cast(section.data.data()); - auto count = section.data.size() / sizeof(elf::Rela); - - for (unsigned i = 0u; i < count; ++i) - { - auto relocation = std::make_shared(); - auto &rela = relTab[i]; - - auto type = rela.info & 0xff; - auto index = rela.info >> 8; - auto &sym = symTab[index]; - auto symType = sym.info & 0xf; - - /* A symbol still undefined after the link is a weak reference - * nothing defined. The link already wrote its final value - - * zero, or a branch to itself - which does not depend on where - * the module loads, so there is nothing for the loader to do. - * Handed to it, the relocation names an undefined symbol and a - * branch to address zero no 24-bit displacement reaches. */ - if (index != 0 && sym.shndx == elf::SHN_UNDEF) - { - if (verbose >= verbose_loaded_relocations) - std::cout << "Relocation at 0x" << std::hex << rela.offset - << " against an undefined weak symbol dropped" << std::endl; - continue; - } - - if (symType == elf::STT_SECTION && sym.value == CodeAddress) { - if (rela.offset < CodeAddress || rela.offset >= DataAddress) { - std::cout << "Unexpected symbol referenced in relocation section " << name << std::endl; - return false; - } - } - - auto addend = static_cast(rela.addend); - - if (verbose >= verbose_loaded_relocations) - { - std::cout << "Relocation #" << std::dec << file.getRelocations().size() - << " sym index " << index << ' ' - << " offset 0x" << std::hex << rela.offset - << " addend 0x" << addend; - } - - if (auto import = file.findImport(addend)) { - relocation->symbol = import->stubSymbol; - relocation->addend = 0; - if (verbose >= verbose_loaded_relocations) - { - std::cout << " -> import "; - } - } else if (auto symbol = file.findSymbol(addend)) { - relocation->symbol = symbol; - relocation->addend = 0; - if (verbose >= verbose_loaded_relocations) - { - std::cout << " -> symbol "; - } - } else if ((prevSection = file.findSection(addend)) != nullptr) { - relocation->symbolSection = prevSection; - relocation->addend = addend - prevSection->address; - if (verbose >= verbose_loaded_relocations) - { - std::cout << " -> section "; - } - } else { - if (verbose >= verbose_loaded_relocations) - { - std::cout << " -> not found" << std::endl; - } - - /* If we can't find a proper symbol, write the addend in and hope for the best */ - auto ptr = getLoaderDataPtr(inSections, rela.offset); - *ptr = addend; - - std::cout << "Unexpected addend " << std::hex << addend << " referenced in relocation section " << name << ", continuing." << std::endl; - continue; - } - - if (verbose >= verbose_loaded_relocations) - { - if (relocation->symbol) - std::cout << relocation->symbol->name; - else if (relocation->symbolSection) - std::cout << relocation->symbolSection->name; - if (relocation->addend != 0) - std::cout << " + " << relocation->addend; - std::cout << std::endl; - } - - if (relocation->symbol) - relocation->symbol->refCount++; - relocation->target = rela.offset; - relocation->type = static_cast(type); - - file.addRelocation(relocation); - } - } - - /* Read dyn relocations */ - for (auto §ion : inSections) - { - if (section.header.type != elf::SHT_RELA) - continue; - - auto name = std::string { shStrTab + section.header.name }; - - if (name.compare(".rela.dyn") != 0) - continue; - - auto symSection = inSections[section.header.link]; - auto relas = reinterpret_cast(section.data.data()); - auto count = section.data.size() / sizeof(elf::Rela); - - for (unsigned i = 0u; i < count; ++i) { - auto relocation = std::make_shared(); - auto &rela = relas[i]; - - auto type = rela.info & 0xff; - auto index = rela.info >> 8; - auto symbol = getSectionSymbol(symSection, index); - auto addr = symbol->value + rela.addend; - - if (type == elf::R_PPC_NONE) - { - /* ignore padding */ - continue; - } - - if (verbose >= verbose_loaded_dyn_reloc) - { - std::cout << "Dyn relocation #" << std::dec << file.getRelocations().size() - << " offset 0x" << std::hex << rela.offset - << " addend 0x" << rela.addend - << " addr 0x" << addr; - } - - // Why index must be == 0? Cause it's required for R_PPC_RELATIVE - if (index == 0) - { - auto addend = static_cast(rela.addend); - - if (auto import = file.findImport(addend)) { - relocation->symbol = import->stubSymbol; - relocation->addend = 0; - if (verbose >= verbose_loaded_dyn_reloc) - { - std::cout << " -> import "; - } - } else if (auto symbol = file.findSymbol(addend)) { - relocation->symbol = symbol; - relocation->addend = addend - symbol->address; - if (verbose >= verbose_loaded_dyn_reloc) - { - std::cout << " -> symbol "; - } - } else if ((prevSection = file.findSection(addend)) != nullptr) { - relocation->symbolSection = prevSection; - relocation->addend = addend - prevSection->address; - if (verbose >= verbose_loaded_relocations) - { - std::cout << " -> section "; - } - } else { - if (verbose >= verbose_loaded_dyn_reloc) - { - std::cout << " -> bad" << std::endl; - } - std::cout << "Unexpected relocation symbol address 0x" << std::hex << addr - << " in .rela.dyn section" << std::endl; - return false; - } - } - else - { - if (verbose >= verbose_loaded_dyn_reloc) - { - std::cout << " -> index " << std::dec << index << std::endl; - } - - std::cout << "Unexpected relocation symbol index " << std::dec << index - << " in .rela.dyn section" << std::endl; - return false; - } - - if (verbose >= verbose_loaded_dyn_reloc) - { - if (relocation->symbol) - std::cout << relocation->symbol->name; - else if (relocation->symbolSection) - std::cout << relocation->symbolSection->name; - if (relocation->addend != 0) - std::cout << " + " << relocation->addend; - std::cout << std::endl; - } - - switch (type) { - case elf::R_PPC_RELATIVE: - type = elf::R_PPC_ADDR32; - break; - default: - std::cout << "Unexpected relocation type in .rela.dyn section" << std::endl; - return false; - } - - relocation->target = rela.offset; - relocation->type = static_cast(type); - - /* Scrap any compiler/linker garbage */ - if(relocation->target >= CodeAddress && relocation->target < WiiuLoadAddress) - { - if (relocation->symbol) - relocation->symbol->refCount++; - file.addRelocation(relocation); - } - - } - } - - return true; -} - -struct OutputSection -{ - std::string name; - elf::SectionHeader header; - std::vector data; - std::shared_ptr relocationSection; - std::shared_ptr sectionSymbol; - uint32_t index = uint32_t(-1); - - bool contains(uint32_t address, uint32_t size = 0) - { - - if (!(header.flags & elf::SHF_ALLOC) || header.size == 0 || header.size == uint32_t(-1)) - return false; - - auto start = header.addr; - auto end = start + header.size; - - if (address >= start && address + size <= end) - return true; - return false; - } -}; - -struct OutputSections -{ - std::vector> sections; - std::map> sections_by_name; - - void addSection(std::shared_ptr section) - { - section->index = sections.size(); - sections.push_back(section); - if (verbose >= verbose_output_sections) - { - auto& header = section->header; - std::cout << "Output section #" << std::dec << section->index - << std::hex << ' ' << section->header.type << ' ' << section->header.flags - << ' ' << section->name - << " (0x" << header.addr << " - 0x" << header.addr + header.size << ')' - << std::endl; - } - if (!section->name.empty()) - { - if (sections_by_name.count(section->name)) - { - std::cout << "Duplicate output section name " << section->name - << std::endl; - } - else - { - sections_by_name[section->name] = section; - } - } - }; - - - template - SymbolIterator addSection(ElfFile &file, SymbolIterator symbolIterator, std::shared_ptr section) - { - ElfFile::SymbolPtr sectionSymbol; - if (section->header.size != 0) - { - sectionSymbol = ElfFile::Symbol::New(section->header.addr, section->name, -1, elf::STT_SECTION, elf::STB_LOCAL); - sectionSymbol->outSection = section; - section->sectionSymbol = sectionSymbol; - } - section->index = sections.size(); - sections.push_back(section); - - if (verbose >= verbose_output_sections) - { - auto& header = section->header; - std::cout << "Output section #" << std::dec << section->index - << std::hex << ' ' << section->header.type << ' ' << section->header.flags - << ' ' << section->name - << " (0x" << header.addr << " - 0x" << header.addr + header.size << ')' - << std::endl; - } - - if (sections_by_name.count(section->name)) - { - std::cout << "Duplicate output section name " << section->name - << std::endl; - } - else - { - sections_by_name[section->name] = section; - } - - if (!sectionSymbol) - return symbolIterator; - - if (verbose >= verbose_loaded_symbols) - { - std::cout << "Section symbol " << sectionSymbol->name << " 0x" << std::hex << sectionSymbol->address - << " 0x" << sectionSymbol->size << '\n'; - } - - return file.insertSymbol(symbolIterator, sectionSymbol) + 1; - }; - - uint32_t - getSectionIndex(uint32_t address) - { - if (address == 0) - return 0; - - for (unsigned i = 0u; i < sections.size(); ++i) - { - if (sections[i]->contains(address)) - return i; - } - - return -1; - } - - uint32_t - getSectionIndex(const std::string &name) - { - auto iter = sections_by_name.find(name); - if (iter == sections_by_name.end()) - return -1; - return iter->second->index; - } - - std::shared_ptr - getSection(uint32_t address, - const std::shared_ptr& hint = nullptr) - { - if (address == 0) - return sections[0]; - - if (hint && hint->contains(address)) - return hint; - - for (auto §ion : sections) { - if (section->contains(address)) - return section; - } - - return nullptr; - } - - std::shared_ptr - getSection(const std::string &name) - { - auto iter = sections_by_name.find(name); - if (iter == sections_by_name.end()) - return nullptr; - return iter->second; - } - - const std::vector>& - getSections() - { - return sections; - } - -}; - -static bool -write(ElfFile &file, const std::string &filename) -{ - OutputSections outSections; - auto sectionSymbolItr = file.getSymbols().begin() + 4; - - /* Create NULL section */ - auto nullSection = std::make_shared(); - memset(&nullSection->header, 0, sizeof(elf::SectionHeader)); - outSections.addSection(nullSection); - - /* Create text/data sections */ - for (auto §ion : file.getDataSections()) - { - auto out = std::make_shared(); - out->header.name = -1; - out->header.type = section->type; - out->header.flags = section->flags; - out->header.addr = section->address; - out->header.offset = -1; - - if (section->type == elf::SHT_NOBITS) - out->header.size = section->size; - else - out->header.size = section->data.size(); - - out->header.link = 0; - out->header.info = 0; - - if (section->address == DataAddress) - { - out->header.addralign = 4096; - out->header.flags |= elf::SHF_WRITE; /* .rodata needs to be writable? */ - } - else - out->header.addralign = 256; - - out->header.entsize = 0; - - /* Add section */ - out->name = section->name; - out->data = section->data; - section->outSection = out; - sectionSymbolItr = outSections.addSection(file, sectionSymbolItr, out); - } - - std::shared_ptr targetSection; - - /* Create relocation sections */ - for (auto &relocation : file.getRelocations()) { - targetSection = outSections.getSection(relocation->target, targetSection); - - if (!targetSection) - { - std::cout << "Error could not find section for relocation" << std::endl; - return false; - } - - relocation->targetSection = targetSection; - - if (!targetSection->relocationSection) - { - /* Create new relocation section */ - auto out = std::make_shared(); - out->header.name = -1; - out->header.type = elf::SHT_RELA; - out->header.flags = 0; - out->header.addr = 0; - out->header.offset = -1; - out->header.size = -1; - out->header.link = -1; - out->header.info = targetSection->index; - out->header.addralign = 4; - out->header.entsize = sizeof(elf::Rela); - - /* Add section */ - out->name = ".rela" + targetSection->name; - sectionSymbolItr = outSections.addSection(file, sectionSymbolItr, out); - targetSection->relocationSection = out; - } - } - - /* Calculate sizes of symbol/string tables so RPL imports are placed after them */ - auto loadAddress = 0xC0000000; - auto predictStrTabSize = 1; - auto predictSymTabSize = 1; - auto predictShstrTabSize = 1; - - for (auto &symbol : file.getSymbols()) - { - predictStrTabSize += symbol->name.size() + 1; - predictSymTabSize += sizeof(elf::Symbol); - } - - for (auto §ion : outSections.getSections()) - predictShstrTabSize += section->name.size() + 1; - - predictStrTabSize = align_up(predictStrTabSize, 0x10); - predictSymTabSize = align_up(predictSymTabSize, 0x10); - predictShstrTabSize = align_up(predictShstrTabSize, 0x10); - loadAddress += predictStrTabSize + predictSymTabSize + predictShstrTabSize; - - /* Create RPL import sections, .fimport_*, .dimport_* */ - for (auto &lib : file.getImportLibs()) - { - auto out = std::make_shared(); - out->header.name = -1; - out->header.type = elf::SHT_RPL_IMPORTS; - out->header.flags = elf::SHF_ALLOC | elf::SHF_EXECINSTR; - out->header.addr = loadAddress; - out->header.offset = -1; - out->header.link = 0; - out->header.info = 0; - out->header.addralign = 4; - out->header.entsize = 0; - out->name = ".fimport_" + lib->name; - - /* Calculate size */ - auto nameSize = align_up(8 + lib->name.size(), 8); - auto stubSize = 8 + 8 * lib->imports.size(); - out->header.size = std::max(nameSize, stubSize); - out->data.resize(out->header.size); - - /* Setup data */ - auto imports = reinterpret_cast(out->data.data()); - imports->count = lib->imports.size(); - imports->signature = crc32(0, Z_NULL, 0); - memcpy(imports->name, lib->name.data(), lib->name.size()); - imports->name[lib->name.size()] = 0; - - /* Update address of import symbols */ - for (unsigned i = 0u; i < lib->imports.size(); ++i) - lib->imports[i]->stubSymbol->address = loadAddress + 8 + i * 8; - - loadAddress = align_up(loadAddress + out->header.size, 4); - - /* Add section */ - sectionSymbolItr = outSections.addSection(file, sectionSymbolItr, out); - } - - /* Prune out unneeded symbols - but keep those used for relocations! */ - file.pruneSymbols(); - file.indexSymbols(); - - /* NOTICE: FROM NOW ON DO NOT MODIFY mSymbols */ - - auto symText = file.findSymbol(CodeAddress); - auto symData = file.findSymbol(DataAddress); - - int number = -1; - /* Convert relocations */ - for (auto &relocation : file.getRelocations()) { - ++number; - auto targetSection = relocation->targetSection; - - if (!targetSection || !targetSection->relocationSection) { - std::cout << "Error could not find section for relocation" << std::endl; - return false; - } - - /* Get address of relocation->target */ - auto relocationSection = targetSection->relocationSection; - - /* Find symbol this relocation points to */ - if (!relocation->symbol && relocation->symbolSection && relocation->symbolSection->outSection) - relocation->symbol = relocation->symbolSection->outSection->sectionSymbol; - - auto symbol = relocation->symbol; - - auto idx = symbol->outIndex; - bool symbol_found = idx != -1; - - if ((verbose >= verbose_convert_relocations) || !symbol_found) - { - std::cout << "Converting relocation #" << std::dec << number - << std::hex << " @ 0x" << relocation->target; - } - if ((verbose >= verbose_convert_relocations) && symbol_found) - { - std::cout << "-> symbol " << std::dec << idx - << ' ' << symbol->name << std::endl; - } - - /* If the symbol doesn't exist but it is within DATA or TEXT, use those symbols + an addend */ - if (!symbol_found) { - if (relocation->symbol->address >= CodeAddress && relocation->symbol->address < DataAddress) { - idx = 1; - relocation->addend = relocation->symbol->address - CodeAddress; - relocation->symbol = symText; - std::cout << "-> no symbol, using TEXT" << std::endl; - } else if (relocation->symbol->address >= DataAddress && relocation->symbol->address < WiiuLoadAddress) { - idx = 2; - relocation->addend = relocation->symbol->address - DataAddress; - relocation->symbol = symData; - std::cout << "-> no symbol, using DATA" << std::endl; - } else { - std::cout << "-> invalid" << std::endl; - std::cout << "Could not find matching symbol for relocation" << std::endl; - return false; - } - } - - /* Create relocation */ - elf::Rela rela; - rela.info = relocation->type | idx << 8; - - if(relocation->type == elf::R_PPC_RELATIVE) - rela.info = elf::R_PPC_ADDR32 | idx << 8; - - rela.addend = relocation->addend; - rela.offset = relocation->target; - - /* Append to relocation section data */ - char *relaData = reinterpret_cast(&rela); - relocationSection->data.insert(relocationSection->data.end(), relaData, relaData + sizeof(elf::Rela)); - } - - /* String + Symbol sections */ - auto symTabSection = std::make_shared(); - auto strTabSection = std::make_shared(); - auto shStrTabSection = std::make_shared(); - - symTabSection->name = ".symtab"; - strTabSection->name = ".strtab"; - shStrTabSection->name = ".shstrtab"; - - outSections.addSection(symTabSection); - auto symTabIndex = symTabSection->index; - - outSections.addSection(strTabSection); - auto strTabIndex = strTabSection->index; - - outSections.addSection(shStrTabSection); - auto shStrTabIndex = shStrTabSection->index; - - /* Update relocation sections to link to symtab */ - for (auto §ion : outSections.getSections()) - { - if (section->header.type == elf::SHT_RELA) - section->header.link = symTabIndex; - - if (section->header.type != elf::SHT_NOBITS) - section->header.size = section->data.size(); - - if (section->sectionSymbol) - { - section->sectionSymbol->address = section->header.addr; - section->sectionSymbol->size = section->header.size; - } - } - - /* Create .strtab */ - strTabSection->header.name = 0; - strTabSection->header.type = elf::SHT_STRTAB; - strTabSection->header.flags = elf::SHF_ALLOC; - strTabSection->header.addr = 0; - strTabSection->header.offset = -1; - strTabSection->header.size = -1; - strTabSection->header.link = 0; - strTabSection->header.info = 0; - strTabSection->header.addralign = 1; - strTabSection->header.entsize = 0; - - /* Add all symbol names to data, update symbol->outNamePos */ - strTabSection->data.push_back(0); - - for (auto &symbol : file.getSymbols()) - { - if (symbol->name.empty()) - symbol->outNamePos = 0; - else - { - symbol->outNamePos = static_cast(strTabSection->data.size()); - std::copy(symbol->name.begin(), symbol->name.end(), std::back_inserter(strTabSection->data)); - strTabSection->data.push_back(0); - } - } - - /* Create .symtab */ - symTabSection->header.name = 0; - symTabSection->header.type = elf::SHT_SYMTAB; - symTabSection->header.flags = elf::SHF_ALLOC; - symTabSection->header.addr = 0; - symTabSection->header.offset = -1; - symTabSection->header.size = -1; - symTabSection->header.link = strTabIndex; - symTabSection->header.info = 0; - symTabSection->header.addralign = 4; - symTabSection->header.entsize = sizeof(elf::Symbol); - - std::shared_ptr symSection; - - for (auto &symbol : file.getSymbols()) - { - elf::Symbol sym; - if (symbol->outSection) - symSection = symbol->outSection; - else if (symbol->inSection && symbol->inSection->outSection) - symSection = symbol->inSection->outSection; - else if (symbol->type == elf::STT_SECTION && symbol->address == 0) - symSection = outSections.getSection(symbol->name); - else - symSection = outSections.getSection(symbol->address, symSection); - - if (symSection == nullptr) - { - std::cout << "Could not find section for symbol " << symbol->name - << " 0x" << std::hex << symbol->address << std::endl; - return false; - } - - sym.name = symbol->outNamePos; - sym.value = symbol->address; - sym.size = symbol->size; - sym.info = symbol->type | (symbol->binding << 4); - sym.other = 0; - sym.shndx = symSection->index; - - if (verbose >= verbose_output_symbols) - { - std::cout << std::right - << std::hex << std::setfill('0') << std::setw(8) << symbol->address - << ' ' << std::setw(8) << symbol->size << std::dec - << ' ' << std::setfill(' ') << std::setw(3) << sym.shndx - << ' ' << std::setw(12) << std::left << symSection->name - << ' ' << symbol->name << std::endl; - } - - /* Compound symbol crc into section crc */ - if (symSection->header.type == elf::SHT_RPL_IMPORTS && symbol->type != elf::STT_SECTION) - { - auto rplImport = reinterpret_cast(symSection->data.data()); - rplImport->signature = crc32(rplImport->signature, reinterpret_cast(strTabSection->data.data() + sym.name),strlen(strTabSection->data.data() + sym.name)+1); - } - - /* Append to symtab data */ - char *symData = reinterpret_cast(&sym); - symTabSection->data.insert(symTabSection->data.end(), symData, symData + sizeof(elf::Symbol)); - } - - /* Finish SHT_RPL_IMPORTS signatures */ - Bytef *zero_buffer = reinterpret_cast(calloc(0x10, 1)); - for (auto §ion : outSections.getSections()) - { - if(section->header.type == elf::SHT_RPL_IMPORTS) - { - auto rplImport = reinterpret_cast(section->data.data()); - rplImport->signature = crc32(rplImport->signature, zero_buffer, 0xE); - } - } - free(zero_buffer); - - /* Create .shstrtab */ - shStrTabSection->header.name = 0; - shStrTabSection->header.type = elf::SHT_STRTAB; - shStrTabSection->header.flags = elf::SHF_ALLOC; - shStrTabSection->header.addr = 0; - shStrTabSection->header.offset = -1; - shStrTabSection->header.size = -1; - shStrTabSection->header.link = 0; - shStrTabSection->header.info = 0; - shStrTabSection->header.addralign = 1; - shStrTabSection->header.entsize = 0; - - /* Add all section header names to data, update section->header.name */ - shStrTabSection->data.push_back(0); - - for (auto §ion : outSections.getSections()) - { - if (section->name.empty()) - section->header.name = 0; - else - { - section->header.name = shStrTabSection->data.size(); - std::copy(section->name.begin(), section->name.end(), std::back_inserter(shStrTabSection->data)); - shStrTabSection->data.push_back(0); - } - } - - loadAddress = 0xC0000000; - - /* Update symtab, strtab, shstrtab section addresses */ - symTabSection->header.addr = loadAddress; - symTabSection->header.size = symTabSection->data.size(); - - loadAddress = align_up(symTabSection->header.addr + predictSymTabSize, 16); - strTabSection->header.addr = loadAddress; - strTabSection->header.size = strTabSection->data.size(); - - loadAddress = align_up(strTabSection->header.addr + predictStrTabSize, 16); - shStrTabSection->header.addr = loadAddress; - shStrTabSection->header.size = shStrTabSection->data.size(); - - /* Create SHT_RPL_FILEINFO section */ - auto fileInfoSection = std::make_shared(); - fileInfoSection->header.name = 0; - fileInfoSection->header.type = elf::SHT_RPL_FILEINFO; - fileInfoSection->header.flags = 0; - fileInfoSection->header.addr = 0; - fileInfoSection->header.offset = -1; - fileInfoSection->header.size = -1; - fileInfoSection->header.link = 0; - fileInfoSection->header.info = 0; - fileInfoSection->header.addralign = 4; - fileInfoSection->header.entsize = 0; - - elf::RplFileInfo fileInfo; - fileInfo.version = 0xCAFE0402; - fileInfo.textSize = 0; - fileInfo.textAlign = 32; - fileInfo.dataSize = 0; - fileInfo.dataAlign = 4096; - fileInfo.loadSize = 0; - fileInfo.loadAlign = 4; - fileInfo.tempSize = 0; - fileInfo.trampAdjust = 0; - fileInfo.trampAddition = 0; - fileInfo.sdaBase = 0; - fileInfo.sda2Base = 0; - fileInfo.stackSize = 0x10000; - fileInfo.heapSize = 0x8000; - fileInfo.filename = 0; - fileInfo.flags = elf::RPL_IS_RPX; - fileInfo.minVersion = 0x5078; - fileInfo.compressionLevel = -1; - fileInfo.fileInfoPad = 0; - fileInfo.cafeSdkVersion = 0x51BA; - fileInfo.cafeSdkRevision = 0xCCD1; - fileInfo.tlsAlignShift = 0; - fileInfo.tlsModuleIndex = 0; - fileInfo.runtimeFileInfoSize = 0; - fileInfo.tagOffset = 0; - - /* Count file info textSize, dataSize, loadSize */ - for (auto §ion : outSections.getSections()) - { - auto size = section->data.size(); - - if (section->header.type == elf::SHT_NOBITS) - size = section->header.size; - - if (section->header.addr >= CodeAddress && section->header.addr < DataAddress) - { - auto val = section->header.addr.value() + section->header.size.value() - CodeAddress; - if(val > fileInfo.textSize) - fileInfo.textSize = val; - } - else if (section->header.addr >= DataAddress && section->header.addr < WiiuLoadAddress) - { - auto val = section->header.addr.value() + section->header.size.value() - DataAddress; - if(val > fileInfo.dataSize) - fileInfo.dataSize = val; - } - else if (section->header.addr >= WiiuLoadAddress) - { - auto val = section->header.addr.value() + section->header.size.value() - WiiuLoadAddress; - if(val > fileInfo.loadSize) - fileInfo.loadSize = val; - } - else if (section->header.addr == 0 && section->header.type != elf::SHT_RPL_CRCS && section->header.type != elf::SHT_RPL_FILEINFO) - fileInfo.tempSize += (size + 128); - } - - /* TODO: These were calculated based on observation, however some games differ. */ - fileInfo.sdaBase = align_up(DataAddress + fileInfo.dataSize + fileInfo.heapSize, 64); - fileInfo.sda2Base = align_up(DataAddress + fileInfo.heapSize, 64); - - char *fileInfoData = reinterpret_cast(&fileInfo); - fileInfoSection->data.insert(fileInfoSection->data.end(), fileInfoData, fileInfoData + sizeof(elf::RplFileInfo)); - - /* Create SHT_RPL_CRCS section */ - auto crcSection = std::make_shared(); - crcSection->header.name = 0; - crcSection->header.type = elf::SHT_RPL_CRCS; - crcSection->header.flags = 0; - crcSection->header.addr = 0; - crcSection->header.offset = -1; - crcSection->header.size = -1; - crcSection->header.link = 0; - crcSection->header.info = 0; - crcSection->header.addralign = 4; - crcSection->header.entsize = 4; - - outSections.addSection(crcSection); - outSections.addSection(fileInfoSection); - - std::vector sectionCRCs; - - for (auto §ion : outSections.getSections()) - { - auto crc = 0u; - - if (!section->data.empty()) - { - crc = crc32(0, Z_NULL, 0); - crc = crc32(crc, reinterpret_cast(section->data.data()), section->data.size()); - } - - sectionCRCs.push_back(byte_swap(crc)); - } - - char *crcData = reinterpret_cast(sectionCRCs.data()); - crcSection->data.insert(crcSection->data.end(), crcData, crcData + sizeof(uint32_t) * sectionCRCs.size()); - - /* Update section sizes and offsets */ - auto shoff = align_up(sizeof(elf::Header), 64); - auto dataOffset = align_up(shoff + outSections.getSections().size() * sizeof(elf::SectionHeader), 64); - - /* Add CRC and FileInfo sections first */ - for (auto §ion : outSections.getSections()) - { - if (section->header.type != elf::SHT_RPL_CRCS && section->header.type != elf::SHT_RPL_FILEINFO) - continue; - - if (section->header.type != elf::SHT_NOBITS) - section->header.size = section->data.size(); - - if (!section->data.empty()) - { - section->header.offset = dataOffset; - dataOffset = align_up(section->header.offset + section->data.size(), 64); - } - else - section->header.offset = 0; - } - - /* Add data sections next */ - for (auto §ion : outSections.getSections()) - { - if(section->header.offset != -1) - continue; - - if (section->header.addr < DataAddress || section->header.addr >= WiiuLoadAddress) - continue; - - if (section->header.type != elf::SHT_NOBITS) - section->header.size = section->data.size(); - - section->header.offset = dataOffset; - dataOffset = align_up(section->header.offset + section->data.size(), 64); - } - - /* Add load sections next */ - for (auto §ion : outSections.getSections()) - { - if(section->header.offset != -1) - continue; - - if (section->header.addr < WiiuLoadAddress) - continue; - - if (section->header.type != elf::SHT_NOBITS) - section->header.size = section->data.size(); - - if (!section->data.empty()) { - section->header.offset = dataOffset; - dataOffset = align_up(section->header.offset + section->data.size(), 64); - } else { - section->header.offset = 0; - } - } - - /* Everything else */ - for (auto §ion : outSections.getSections()) { - if(section->header.offset != -1) - continue; - - if (section->header.type != elf::SHT_NOBITS) - section->header.size = section->data.size(); - - if (!section->data.empty()) - { - section->header.offset = dataOffset; - dataOffset = align_up(section->header.offset + section->data.size(), 64); - } - else - section->header.offset = 0; - } - - /* Write to file */ - std::ofstream out { filename, std::ofstream::binary }; - std::vector padding; - - if (!out.is_open()) - { - std::cout << "Could not open " << filename << " for writing" << std::endl; - return false; - } - - elf::Header header; - header.magic = elf::HeaderMagic; - header.fileClass = 1; - header.encoding = elf::ELFDATA2MSB; - header.elfVersion = elf::EV_CURRENT; - header.abi = elf::EABI_CAFE; - memset(&header.pad, 0, 7); - header.type = 0xFE01; - header.machine = elf::EM_PPC; - header.version = 1; - header.entry = file.entryPoint; - header.phoff = 0; - header.phentsize = 0; - header.phnum = 0; - header.shoff = shoff; - header.shnum = outSections.getSections().size(); - header.shentsize = sizeof(elf::SectionHeader); - header.flags = 0; - header.ehsize = sizeof(elf::Header); - header.shstrndx = shStrTabIndex; - out.write(reinterpret_cast(&header), sizeof(elf::Header)); - - /* Write section headers */ - out.seekp(header.shoff.value()); - - for (auto §ion : outSections.getSections()) - out.write(reinterpret_cast(§ion->header), sizeof(elf::SectionHeader)); - - /* Write section data */ - for (auto §ion : outSections.getSections()) - { - if (!section->data.empty()) - { - out.seekp(section->header.offset.value()); - out.write(section->data.data(), section->data.size()); - } - } - - return true; -} - -int main(int argc, char **argv) -{ - int arg_start = 1; - - while (argc >= arg_start + 1 && argv[arg_start][0] == '-') - { - if (argv[arg_start] == std::string("--")) - { - ++arg_start; - break; - } - else if (argv[arg_start] == std::string("-v")) - { - ++verbose; - ++arg_start; - } - } - - if (argc < arg_start + 1) - { - std::cout << "Usage: " << argv[0] << " " << std::endl; - return -1; - } - - ElfFile elf; - auto src = argv[arg_start]; - auto dst = argv[arg_start + 1]; - - if (!read(elf, src)) - return -1; - - if (!write(elf, dst)) - return -1; - - return 0; -} diff --git a/os/wiiu/wut/elf2rpl/utils.h b/os/wiiu/wut/elf2rpl/utils.h deleted file mode 100644 index 5df4e14b85..0000000000 --- a/os/wiiu/wut/elf2rpl/utils.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once -#include -#include - -#if defined(WIN32) || defined(_WIN32) || defined(_MSC_VER) -#define PLATFORM_WINDOWS -#elif __APPLE__ -#define PLATFORM_APPLE -#define PLATFORM_POSIX -#elif __linux__ -#define PLATFORM_LINUX -#define PLATFORM_POSIX -#endif - -#ifdef PLATFORM_LINUX -#include -#endif - -/* reinterpret_cast for value types */ -template -inline DstType -bit_cast(const SrcType& src) -{ - static_assert(sizeof(SrcType) == sizeof(DstType), "bit_cast must be between same sized types"); - static_assert(std::is_trivially_copyable::value, "SrcType is not trivially copyable."); - static_assert(std::is_trivially_copyable::value, "DstType is not trivially copyable."); - - DstType dst; - std::memcpy(&dst, &src, sizeof(SrcType)); - return dst; -} - -/* Utility class to swap endian for types of size 1, 2, 4, 8 */ -/* other type sizes are not supported */ -template -struct byte_swap_t; - -template -struct byte_swap_t -{ - static Type swap(Type src) - { - return src; - } -}; - -template -struct byte_swap_t -{ - static Type swap(Type src) - { -#ifdef PLATFORM_WINDOWS - return bit_cast(_byteswap_ushort(bit_cast(src))); -#elif defined(PLATFORM_APPLE) - /* Apple has no 16-bit byteswap intrinsic */ - const uint16_t data = bit_cast(src); - return bit_cast((uint16_t)((data >> 8) | (data << 8))); -#elif defined(PLATFORM_LINUX) - return bit_cast(bswap_16(bit_cast(src))); -#endif - } -}; - -template -struct byte_swap_t -{ - static Type swap(Type src) - { -#ifdef PLATFORM_WINDOWS - return bit_cast(_byteswap_ulong(bit_cast(src))); -#elif defined(PLATFORM_APPLE) - return bit_cast(__builtin_bswap32(bit_cast(src))); -#elif defined(PLATFORM_LINUX) - return bit_cast(bswap_32(bit_cast(src))); -#endif - } -}; - -template -struct byte_swap_t -{ - static Type swap(Type src) - { -#ifdef PLATFORM_WINDOWS - return bit_cast(_byteswap_uint64(bit_cast(src))); -#elif defined(PLATFORM_APPLE) - return bit_cast(__builtin_bswap64(bit_cast(src))); -#elif defined(PLATFORM_LINUX) - return bit_cast(bswap_64(bit_cast(src))); -#endif - } -}; - -/* Swaps endian of src */ -template -inline Type -byte_swap(Type src) -{ - return byte_swap_t::swap(src); -} - -/* Alignment helpers */ -template -constexpr inline Type -align_up(Type value, size_t alignment) -{ - return static_cast((static_cast(value) + (alignment - 1)) & ~(alignment - 1)); -} - -template -constexpr inline Type -align_down(Type value, size_t alignment) -{ - return static_cast(static_cast(value) & ~(alignment - 1)); -} - -#define CHECK_SIZE(Type, Size) \ - static_assert(sizeof(Type) == Size, \ - #Type " must be " #Size " bytes") diff --git a/samples/frontend/wiiu_elf2rpl/.gitignore b/samples/frontend/wiiu_elf2rpl/.gitignore index f558bb25e1..ee328d0112 100644 --- a/samples/frontend/wiiu_elf2rpl/.gitignore +++ b/samples/frontend/wiiu_elf2rpl/.gitignore @@ -1,3 +1,4 @@ /wiiu_elf2rpl_tool /wiiu_elf2rpl_in.tmp /wiiu_elf2rpl_out.tmp +/wiiu_elf2rpl_trunc.tmp diff --git a/samples/frontend/wiiu_elf2rpl/Makefile b/samples/frontend/wiiu_elf2rpl/Makefile index 56d1b60a9b..47e66bd5d4 100644 --- a/samples/frontend/wiiu_elf2rpl/Makefile +++ b/samples/frontend/wiiu_elf2rpl/Makefile @@ -1,28 +1,29 @@ # os/wiiu/wut/elf2rpl, the host tool that turns the linked Wii U ELF # into an RPL, run on a linked ELF the test writes itself in the shape -# a -pie link gives it. No PowerPC toolchain is needed. Builds for Linux -# and, with CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ -# EXE=.exe RUNNER=wine, for Windows. +# a -pie link gives it, then on truncated copies of it. No PowerPC +# toolchain is needed. Builds for Linux and, with +# CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine, for Windows. TARGET := wiiu_elf2rpl_test$(EXE) TOOL := wiiu_elf2rpl_tool$(EXE) CORE_DIR := ../../.. ELF2RPL := $(CORE_DIR)/os/wiiu/wut/elf2rpl +LIBRETRO_COMM_DIR := $(CORE_DIR)/libretro-common + +# The tool and the libretro-common units it links, as its own Makefile +# lists them +TOOL_SOURCES := $(ELF2RPL)/main.c \ + $(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \ + $(LIBRETRO_COMM_DIR)/features/features_cpu.c \ + $(LIBRETRO_COMM_DIR)/time/rtime.c -CXX ?= g++ CFLAGS += -std=c89 -Wall -g1 -O1 -DELF2RPL='"$(if $(EXE),,./)$(TOOL)"' -TFLAGS := -O2 -std=c++14 -I$(ELF2RPL) -Wno-class-memaccess +TFLAGS := -std=gnu89 -Wall -g1 -O2 -I$(LIBRETRO_COMM_DIR)/include ifneq ($(SANITIZER),) CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS) LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS) - TFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(TFLAGS) - # The tool reads and writes ELF headers through packed big-endian - # fields, and keeps its section graph to exit, as a one-shot tool - # may; its other undefined behaviour and memory errors still show. - ifneq ($(findstring undefined,$(SANITIZER)),) - TFLAGS += -fno-sanitize=alignment - endif - CFLAGS += -DELF2RPL_ENV='"ASAN_OPTIONS=detect_leaks=0 "' + TFLAGS := -fsanitize=$(SANITIZER) -fno-sanitize-recover=all \ + -fno-omit-frame-pointer $(TFLAGS) endif ifeq ($(EXE),.exe) @@ -31,8 +32,8 @@ endif all: $(TARGET) $(TOOL) -$(TOOL): $(ELF2RPL)/main.cpp - $(CXX) $(TFLAGS) -o $@ $< +$(TOOL): $(TOOL_SOURCES) + $(CC) $(TFLAGS) -o $@ $(TOOL_SOURCES) $(TARGET): wiiu_elf2rpl_test.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) @@ -43,6 +44,7 @@ check: all clean: rm -f wiiu_elf2rpl_test wiiu_elf2rpl_test.exe \ wiiu_elf2rpl_tool wiiu_elf2rpl_tool.exe \ - wiiu_elf2rpl_in.tmp wiiu_elf2rpl_out.tmp + wiiu_elf2rpl_in.tmp wiiu_elf2rpl_out.tmp \ + wiiu_elf2rpl_trunc.tmp .PHONY: all check clean diff --git a/samples/frontend/wiiu_elf2rpl/wiiu_elf2rpl_test.c b/samples/frontend/wiiu_elf2rpl/wiiu_elf2rpl_test.c index 59571453f3..70cac748a9 100644 --- a/samples/frontend/wiiu_elf2rpl/wiiu_elf2rpl_test.c +++ b/samples/frontend/wiiu_elf2rpl/wiiu_elf2rpl_test.c @@ -9,21 +9,26 @@ * symbol - the Cafe OS loader cannot resolve one, and a 24-bit branch * to address zero is out of reach from the code area - while every * relocation against a resolved target survives, and the instruction - * the link wrote at the weak branch is left as it was. */ + * the link wrote at the weak branch is left as it was. + * + * Every section's entry in SHT_RPL_CRCS must be the zlib CRC-32 of its + * contents, which the loader checks; and a truncated ELF, or an option + * the tool does not know, must end in its error, not a crash. */ #include #include #include +#ifndef _WIN32 +#include +#endif #ifndef ELF2RPL #define ELF2RPL "./wiiu_elf2rpl_tool" #endif -#ifndef ELF2RPL_ENV -#define ELF2RPL_ENV "" -#endif -#define IN_PATH "wiiu_elf2rpl_in.tmp" -#define OUT_PATH "wiiu_elf2rpl_out.tmp" +#define IN_PATH "wiiu_elf2rpl_in.tmp" +#define OUT_PATH "wiiu_elf2rpl_out.tmp" +#define TRUNC_PATH "wiiu_elf2rpl_trunc.tmp" #define CODE 0x02000000u #define DATA 0x10000000u @@ -233,13 +238,75 @@ static unsigned char *read_all(const char *path, long *len) return buf; } +/* zlib's CRC-32, a bit at a time, to check the tool's against */ +static unsigned long crc32_ref(const unsigned char *p, unsigned long n) +{ + unsigned long crc = 0xFFFFFFFFul; + while (n--) + { + int k; + crc ^= *p++; + for (k = 0; k < 8; k++) + crc = (crc >> 1) ^ (0xEDB88320ul & (0ul - (crc & 1ul))); + } + return crc ^ 0xFFFFFFFFul; +} + +/* The tool gave up with its own error (exit -1), rather than crashing + * or being stopped by a sanitizer. */ +static int failed_cleanly(int rc) +{ +#ifdef _WIN32 + return rc == -1 || rc == 255; +#else + return rc != -1 && WIFEXITED(rc) && WEXITSTATUS(rc) == 255; +#endif +} + +static int bad_input_rejected(const unsigned char *elf, long elf_len) +{ + static const long cuts[] = { 0, 4, 20, 51, 52, 60, 200, 400, 1000, -1 }; + char cmd[256]; + int ok = 1, c; + + for (c = 0; cuts[c] >= 0; c++) + { + long cut = cuts[c] < elf_len ? cuts[c] : elf_len - 1; + FILE *f = fopen(TRUNC_PATH, "wb"); + int rc; + if (!f) + return 0; + if (cut && fwrite(elf, 1, (size_t)cut, f) != (size_t)cut) + ok = 0; + fclose(f); + sprintf(cmd, "%s %s %s", ELF2RPL, TRUNC_PATH, OUT_PATH); + rc = system(cmd); + if (!failed_cleanly(rc)) + { + fprintf(stderr, "ELF cut to %ld bytes: elf2rpl returned %d\n", cut, rc); + ok = 0; + } + } + remove(TRUNC_PATH); + remove(OUT_PATH); + + sprintf(cmd, "%s -x %s %s", ELF2RPL, IN_PATH, OUT_PATH); + if (!failed_cleanly(system(cmd))) + { + fprintf(stderr, "elf2rpl took an unknown option\n"); + ok = 0; + } + remove(OUT_PATH); + return ok; +} + int main(void) { - unsigned char *rpl; - long len = 0; - unsigned long shoff; + unsigned char *rpl, *elf; + long len = 0, elf_len = 0; + unsigned long shoff, crc_off = 0, crc_count = 0; unsigned shnum, i; - int kept = 0, undef = 0, rel24 = 0, branch_ok = 0; + int kept = 0, undef = 0, rel24 = 0, branch_ok = 0, crc_bad = 0; char cmd[256]; if (!write_input()) @@ -247,7 +314,7 @@ int main(void) fprintf(stderr, "FAIL could not write %s\n", IN_PATH); return 1; } - sprintf(cmd, "%s%s %s %s", ELF2RPL_ENV, ELF2RPL, IN_PATH, OUT_PATH); + sprintf(cmd, "%s %s %s", ELF2RPL, IN_PATH, OUT_PATH); remove(OUT_PATH); CHECK(system(cmd) == 0); if (!(rpl = read_all(OUT_PATH, &len))) @@ -268,6 +335,12 @@ int main(void) unsigned long sh_size = get32(sh + 20); unsigned long sh_link = get32(sh + 24); + if (type == 0x80000003ul) + { + crc_off = sh_off; + crc_count = sh_size / 4; + } + /* The weak branch the link wrote, still as it was. */ if (type == 1 && sh_addr == CODE && sh_size >= 12) branch_ok = get32(rpl + sh_off + 8) == 0x48000000ul; @@ -301,7 +374,32 @@ int main(void) CHECK(kept == 3); /* table@ha, table@l and &main survive */ CHECK(branch_ok); + /* One CRC per section, its own and empty ones zero */ + CHECK(crc_count == shnum); + for (i = 0; i < shnum && crc_count == shnum; i++) + { + const unsigned char *sh = rpl + shoff + (unsigned long)i * 40; + unsigned long type = get32(sh + 4); + unsigned long size = get32(sh + 20); + unsigned long want = (type == 0x80000003ul || type == 8 || !size) + ? 0 : crc32_ref(rpl + get32(sh + 16), size); + if (get32(rpl + crc_off + i * 4) != want) + { + fprintf(stderr, "section %u CRC %08lx, want %08lx\n", i, + get32(rpl + crc_off + i * 4), want); + crc_bad++; + } + } + CHECK(crc_bad == 0); free(rpl); + + CHECK((elf = read_all(IN_PATH, &elf_len)) != NULL); + if (elf) + { + CHECK(bad_input_rejected(elf, elf_len)); + free(elf); + } + remove(IN_PATH); remove(OUT_PATH); if (failures)