Add support for reading Wii U RPX/RPL

== DETAILS
The Wii U uses a slightly customized ELF format which uses
zlib compression on certain section types.

This commit adds the following:
- add `SHT_` constants for the custom ELF sections
- add `SHF_RPX_DEFLATE` flag used to denote a compressed section
- add code in the section `load()` method to detect the `SHF_RPX_DEFLATE` flag
  and decompress the section
This commit is contained in:
Nathan Strong 2022-10-25 08:46:58 -07:00 committed by Serge Lamikhov-Center
parent 1df77b2495
commit 582d929d9d
2 changed files with 69 additions and 6 deletions

View File

@ -529,6 +529,11 @@ constexpr Elf_Word SHT_ARM_DEBUGOVERLAY = 0x70000004;
constexpr Elf_Word SHT_ARM_OVERLAYSECTION = 0x70000005;
constexpr Elf_Word SHT_HIPROC = 0x7FFFFFFF;
constexpr Elf_Word SHT_LOUSER = 0x80000000;
// Used by Nintendo Wii U
constexpr Elf_Word SHT_RPL_EXPORTS = 0x80000001;
constexpr Elf_Word SHT_RPL_IMPORTS = 0x80000002;
constexpr Elf_Word SHT_RPL_CRCS = 0x80000003;
constexpr Elf_Word SHT_RPL_FILEINFO = 0x80000004;
constexpr Elf_Word SHT_HIUSER = 0xFFFFFFFF;
// Section attribute flags
@ -545,6 +550,8 @@ constexpr Elf_Xword SHF_TLS = 0x400;
constexpr Elf_Xword SHF_COMPRESSED = 0x800;
constexpr Elf_Xword SHF_GNU_RETAIN = 0x200000;
constexpr Elf_Xword SHF_GNU_MBIND = 0x01000000;
// flag used in Nintendo RPX/RPL to indicate section data is zlib-compressed
constexpr Elf_Xword SHF_RPX_DEFLATE = 0x08000000;
constexpr Elf_Xword SHF_MASKOS = 0x0FF00000;
constexpr Elf_Xword SHF_MIPS_GPREL = 0x10000000;
constexpr Elf_Xword SHF_ORDERED = 0x40000000;

View File

@ -28,6 +28,8 @@ THE SOFTWARE.
#include <new>
#include <limits>
#include <zlib.h>
namespace ELFIO {
class section
@ -214,13 +216,59 @@ template <class T> class section_impl : public section
if ( ( 0 != size ) && ( nullptr != data ) ) {
stream.seekg(
( *translator )[( *convertor )( header.sh_offset )] );
stream.read( data.get(), size );
if ( static_cast<Elf_Xword>( stream.gcount() ) != size ) {
data = nullptr;
return false;
if(get_flags() & SHF_RPX_DEFLATE) {
Elf_Xword uncompressed_size = 0;
fixup_size(stream, size, uncompressed_size);
// reallocate data to be the correct size
data.reset( new (std::nothrow) char[size_t(uncompressed_size) + 1]);
// create a buffer to hold the compressed bits
auto compressed_data = std::unique_ptr<char>(new char[size_t(size)]);
if( data == nullptr || compressed_data == nullptr) {
std::cerr << "failed to allocate memory buffers for decompression" << std::endl;
return false;
}
set_size(uncompressed_size);
// read rest of data into data buffer
stream.read( compressed_data.get(), size);
z_stream s = { 0 };
int z_result = 0;
s.zalloc = Z_NULL;
s.zfree = Z_NULL;
s.opaque = Z_NULL;
if(Z_OK != (z_result = inflateInit_(&s, ZLIB_VERSION, sizeof(s)))) {
std::cerr << "error initializing zlib: " << z_result << std::endl;
data = nullptr;
return false;
}
s.avail_in = size;
s.next_in = (Bytef *)compressed_data.get();
s.avail_out = uncompressed_size;
s.next_out = (Bytef *)data.get();
z_result = inflate(&s, Z_FINISH);
inflateEnd(&s);
if (z_result != Z_OK && z_result != Z_STREAM_END) {
std::cerr << "error decompressing section: " << z_result << std::endl;
data = nullptr;
return false;
}
} else {
stream.read( data.get(), size );
if ( static_cast<Elf_Xword>( stream.gcount() ) != size ) {
data = nullptr;
return false;
}
}
data.get()[size] =
0; // Ensure data is ended with 0 to avoid oob read
// refresh size because it may have changed if we had to decompress data
size = get_size();
data.get()[size] = 0; // Ensure data is ended with 0 to avoid oob read
data_size = decltype( data_size )( size );
}
else {
@ -250,6 +298,14 @@ template <class T> class section_impl : public section
//------------------------------------------------------------------------------
private:
void fixup_size(std::istream& stream, Elf_Xword& compressed_size, Elf_Xword& uncompressed_size) {
uint32_t tmp = 0;
stream.read((char *)&tmp, 4);
tmp = (*convertor)(tmp);
uncompressed_size = static_cast<Elf_Xword>(tmp);
compressed_size = get_size() - 4;
}
//------------------------------------------------------------------------------
void save_header( std::ostream& stream, std::streampos header_offset ) const
{