Skip to content

Instantly share code, notes, and snippets.

@nwert
Created November 7, 2018 08:57
Show Gist options
  • Save nwert/e44ec01c8ae9f40eb537f999226837c6 to your computer and use it in GitHub Desktop.
Save nwert/e44ec01c8ae9f40eb537f999226837c6 to your computer and use it in GitHub Desktop.
#include "RPLLoader.h"
#include "../common/elf/ELFLoaderBase.h"
#include "RPL.h"
#include "RPL_inlines.h"
#include "Section.h"
#include "miniz_tinfl.h"
class RPLLoaderImpl final
: public ELFLoaderImplBase<Elf32>
{
u32 FakeReloc(u32 val)
{
if(val & 0xC0000000)
val = (val & ~0xC0000000) | 0x03000000;
return val;
}
protected:
std::vector<u8> SectionGetData(Elf_Shdr& shdr) final
{
if (!(shdr.sh_flags & SHF_COMPRESSED))
return ELFLoaderImplBase::SectionGetData(shdr);
u32 size_hdr = *(u32 *)(data.GetData() + shdr.sh_offset);
size_hdr = _ES32(size_hdr);
std::vector<u8> res;
res.resize(size_hdr);
size_t size_out = tinfl_decompress_mem_to_mem(res.data(), size_hdr,
data.GetData() + shdr.sh_offset + 4, shdr.sh_size - 4, TINFL_FLAG_PARSE_ZLIB_HEADER);
if (size_out == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED)
{
MWARNING("Failed to decompress section @ %08X.", shdr.sh_addr);
res.clear();
}
else
shdr.sh_size = size_out;
return res;
}
bool HandleSection(Elf_Shdr& shdr) final
{
shdr.sh_addr = FakeReloc(shdr.sh_addr);
return (ELFLoaderImplBase::HandleSection(shdr) &&
shdr.sh_type != SHT_NULL &&
shdr.sh_type != SHT_STRTAB &&
shdr.sh_type != SHT_SYMTAB/* &&
shdr.sh_type != SHT_RPL_EXPORT &&
shdr.sh_type != SHT_RPL_IMPORT &&
shdr.sh_type != SHT_RPL_CRCS &&
shdr.sh_type != SHT_RPL_CAFE*/
);
}
bool HandleSymbol(std::string& name, Elf_Sym& sym) final
{
sym.st_value = FakeReloc(sym.st_value);
return ELFLoaderImplBase::HandleSymbol(name, sym);
}
bool ParseRelocations()
{
if (!ehdr.e_shnum)
return false;
for (auto[_, shdr] : sections)
{
if (shdr.sh_type != SHT_RELA)
continue;
auto sym_buf = SectionGetData(sections[shdr.sh_link].second);
auto target_sec = binary.FindSectionByName(sections[shdr.sh_info].first);
auto relas = SectionGetData(shdr);
Elf_Rela rela, *rela_it = (Elf_Rela *)relas.data();
for (u32 j = 0; j < relas.size() / sizeof(Elf_Rela); j++, ++rela_it)
{
ELF_COPY_EST(Rela, &rela, rela_it);
int sym_idx = rela.r_info >> 8;
int rel_type = rela.r_info & 0xFF;
if (rel_type == R_PPC_NONE)
continue;
Elf_Sym sym;
ELF_COPY_EST(Sym, &sym, &((Elf_Sym *)sym_buf.data())[sym_idx]);
u32 value = FakeReloc(sym.st_value + rela.r_addend);
rela.r_offset = FakeReloc(rela.r_offset);
u8 *target = target_sec->GetData().data() + rela.r_offset - target_sec->GetAddr();
switch (rel_type)
{
case R_PPC_ADDR32:
*(u32 *)target = _ES32(value);
break;
case R_PPC_ADDR16_LO:
value &= 0xFFFF;
*(u16 *)target = _ES16(value);
break;
case R_PPC_ADDR16_HI:
value >>= 16;
*(u16 *)target = _ES16(value);
break;
case R_PPC_ADDR16_HA:
value = (value + 0x8000) >> 16;
*(u16 *)target = _ES16(value);
break;
case R_PPC_REL24:
{
u32 tmp = *(u32 *)target;
tmp = _ES32(tmp) & ~0x03FFFFFC;
tmp |= (value - rela.r_offset) & 0x03FFFFFC;
*(u32 *)target = _ES32(tmp);
}
break;
case R_PPC_DTPREL32:
*(u32 *)target = _ES32(value);
break;
case R_PPC_GHS_REL16_HI:
{
u16 tmp = (value - rela.r_offset) >> 16;
*(u16 *)target = _ES16(tmp);
}
break;
case R_PPC_GHS_REL16_LO:
{
u16 tmp = (value - rela.r_offset) & 0xFFFF;
*(u16 *)target = _ES16(tmp);
}
break;
}
}
}
return true;
}
public:
RPLLoaderImpl(Project *_project, DataBuffer& _data)
: ELFLoaderImplBase(_project, _data)
{}
bool Load()
{
if (!ParseHeader())
return false;
FindStrTab();
if (!ParseSections())
{
MWARNING("Failed to parse section headers.");
return false;
}
ParseSymbols();
ParseRelocations();
return true;
}
};
bool RPLLoader::CanLoad(const std::string& _UNUSED(fname), DataBuffer data, std::string& name_out)
{
if (data.GetSize() < sizeof(Elf32_Ehdr))
return false;
Elf32_Ehdr ehdr;
memcpy(&ehdr, data.GetData(), sizeof(Elf32_Ehdr));
if (!(memcmp(ehdr.e_ident, ElfMagic, 4) == 0 &&
ehdr.e_ident[EI_CLASS] == ELFCLASS32 &&
ehdr.e_ident[EI_DATA] == ELFDATA2MSB &&
ehdr.e_ident[EI_OSABI] == ELFOSABI_CAFE &&
ehdr.e_ident[EI_ABIVERSION] == ELFABIVERSION_CAFE))
return false;
const bool bigendian = true;
const u8 bits = 32;
ELF_EST(Ehdr, &ehdr);
if (!(ehdr.e_type == ET_CAFE_RPL &&
ehdr.e_machine == EM_PPC))
return false;
name_out = "RPL/RPX (Wii U)";
return true;
}
bool RPLLoader::Load(Project *project, const std::string& _UNUSED(fname), DataBuffer data)
{
return RPLLoaderImpl(project, data).Load();
}
#include "PluginAPI.h"
LDR_DEFINE(RPLLoader)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment