Boot: Unify the ELF and DOL code paths

They're essentially the same. To achieve this, this commit unifies
DolReader and ElfReader into a common interface for boot executable
readers, so the only remaining difference between ELF and DOL is
how which volume is inserted.
This commit is contained in:
Léo Lam 2017-05-28 17:12:38 +02:00
parent 22992ae41e
commit 065261dbad
13 changed files with 195 additions and 233 deletions

View File

@ -21,7 +21,8 @@
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Core/Boot/Boot_DOL.h"
#include "Core/Boot/DolReader.h"
#include "Core/Boot/ElfReader.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Debugger/Debugger_SymbolMap.h"
@ -86,11 +87,11 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(const std::stri
return std::make_unique<BootParameters>(Disc{path, std::move(volume)});
}
if (extension == ".elf" || extension == ".dol")
{
return std::make_unique<BootParameters>(
Executable{path, extension == ".elf" ? Executable::Type::ELF : Executable::Type::DOL});
}
if (extension == ".elf")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<ElfReader>(path)});
if (extension == ".dol")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<DolReader>(path)});
if (extension == ".dff")
return std::make_unique<BootParameters>(DFF{path});
@ -371,14 +372,13 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
{
NOTICE_LOG(BOOT, "Booting from executable: %s", executable.path.c_str());
// TODO: needs more cleanup.
if (executable.type == BootParameters::Executable::Type::DOL)
{
CDolLoader dolLoader(executable.path);
if (!dolLoader.IsValid())
return false;
if (!executable.reader->IsValid())
return false;
const DiscIO::Volume* volume = nullptr;
const DiscIO::Volume* volume = nullptr;
// VolumeDirectory only works with DOLs.
if (StringEndsWith(executable.path, ".dol"))
{
if (!config.m_strDVDRoot.empty())
{
NOTICE_LOG(BOOT, "Setting DVDRoot %s", config.m_strDVDRoot.c_str());
@ -390,57 +390,41 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
NOTICE_LOG(BOOT, "Loading default ISO %s", config.m_strDefaultISO.c_str());
volume = SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO));
}
// Poor man's bootup
if (config.bWii)
{
HID4.SBE = 1;
SetupMSR();
SetupBAT(config.bWii);
// Because there is no TMD to get the requested system (IOS) version from,
// we default to IOS58, which is the version used by the Homebrew Channel.
SetupWiiMemory(volume, 0x000000010000003a);
}
else
{
EmulatedBS2_GC(volume, true);
}
Load_FST(config.bWii, volume);
dolLoader.Load();
PC = dolLoader.GetEntryPoint();
if (LoadMapFromFilename())
HLE::PatchFunctions();
}
if (executable.type == BootParameters::Executable::Type::ELF)
else
{
const DiscIO::Volume* volume = SetDefaultDisc();
// Poor man's bootup
if (config.bWii)
{
// Because there is no TMD to get the requested system (IOS) version from,
// we default to IOS58, which is the version used by the Homebrew Channel.
SetupWiiMemory(volume, 0x000000010000003a);
}
else
{
EmulatedBS2_GC(volume, true);
}
Load_FST(config.bWii, volume);
if (!Boot_ELF(executable.path))
return false;
// Note: Boot_ELF calls HLE::PatchFunctions()
UpdateDebugger_MapLoaded();
Dolphin_Debugger::AddAutoBreakpoints();
volume = SetDefaultDisc();
}
if (!executable.reader->LoadIntoMemory())
{
PanicAlertT("Failed to load the executable to memory.");
return false;
}
// Poor man's bootup
if (config.bWii)
{
HID4.SBE = 1;
SetupMSR();
SetupBAT(config.bWii);
// Because there is no TMD to get the requested system (IOS) version from,
// we default to IOS58, which is the version used by the Homebrew Channel.
SetupWiiMemory(volume, 0x000000010000003a);
}
else
{
EmulatedBS2_GC(volume, true);
}
Load_FST(config.bWii, volume);
PC = executable.reader->GetEntryPoint();
if (executable.reader->LoadSymbols() || LoadMapFromFilename())
{
UpdateDebugger_MapLoaded();
HLE::PatchFunctions();
}
return true;
}
@ -495,3 +479,16 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
HLE::PatchFixedFunctions();
return true;
}
BootExecutableReader::BootExecutableReader(const std::string& file_name)
{
m_bytes.resize(File::GetSize(file_name));
File::IOFile file{file_name, "rb"};
file.ReadBytes(m_bytes.data(), m_bytes.size());
}
BootExecutableReader::BootExecutableReader(const std::vector<u8>& bytes) : m_bytes(bytes)
{
}
BootExecutableReader::~BootExecutableReader() = default;

View File

@ -5,9 +5,11 @@
#pragma once
#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "Common/CommonTypes.h"
#include "DiscIO/Enums.h"
@ -21,6 +23,8 @@ struct RegionSetting
const std::string code;
};
class BootExecutableReader;
struct BootParameters
{
struct Disc
@ -31,13 +35,8 @@ struct BootParameters
struct Executable
{
enum class Type
{
DOL,
ELF,
};
std::string path;
Type type;
std::unique_ptr<BootExecutableReader> reader;
};
struct NAND
@ -72,7 +71,6 @@ class CBoot
{
public:
static bool BootUp(std::unique_ptr<BootParameters> boot);
static bool IsElfWii(const std::string& filename);
// Tries to find a map file for the current game by looking first in the
// local user directory, then in the shared user directory.
@ -97,7 +95,6 @@ private:
static void UpdateDebugger_MapLoaded();
static bool Boot_ELF(const std::string& filename);
static bool Boot_WiiWAD(const std::string& filename);
static void SetupMSR();
@ -111,3 +108,20 @@ private:
static bool SetupWiiMemory(const DiscIO::Volume* volume, u64 ios_title_id);
};
class BootExecutableReader
{
public:
BootExecutableReader(const std::string& file_name);
BootExecutableReader(const std::vector<u8>& buffer);
virtual ~BootExecutableReader();
virtual u32 GetEntryPoint() const = 0;
virtual bool IsValid() const = 0;
virtual bool IsWii() const = 0;
virtual bool LoadIntoMemory(bool only_in_mem1 = false) const = 0;
virtual bool LoadSymbols() const = 0;
protected:
std::vector<u8> m_bytes;
};

View File

@ -1,89 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <memory>
#include "Common/FileUtil.h"
#include "Common/Swap.h"
#include "Core/Boot/Boot.h"
#include "Core/Boot/ElfReader.h"
#include "Core/HLE/HLE.h"
#include "Core/PowerPC/PowerPC.h"
bool CBoot::IsElfWii(const std::string& filename)
{
/* We already check if filename existed before we called this function, so
there is no need for another check, just read the file right away */
size_t filesize = File::GetSize(filename);
auto elf = std::make_unique<u8[]>(filesize);
{
File::IOFile f(filename, "rb");
f.ReadBytes(elf.get(), filesize);
}
// Use the same method as the DOL loader uses: search for mfspr from HID4,
// which should only be used in Wii ELFs.
//
// Likely to have some false positives/negatives, patches implementing a
// better heuristic are welcome.
// Swap these once, instead of swapping every word in the file.
u32 HID4_pattern = Common::swap32(0x7c13fba6);
u32 HID4_mask = Common::swap32(0xfc1fffff);
ElfReader reader(elf.get());
for (int i = 0; i < reader.GetNumSegments(); ++i)
{
if (reader.IsCodeSegment(i))
{
u32* code = (u32*)reader.GetSegmentPtr(i);
for (u32 j = 0; j < reader.GetSegmentSize(i) / sizeof(u32); ++j)
{
if ((code[j] & HID4_mask) == HID4_pattern)
return true;
}
}
}
return false;
}
bool CBoot::Boot_ELF(const std::string& filename)
{
// Read ELF from file
size_t filesize = File::GetSize(filename);
auto elf = std::make_unique<u8[]>(filesize);
{
File::IOFile f(filename, "rb");
f.ReadBytes(elf.get(), filesize);
}
// Load ELF into GameCube Memory
ElfReader reader(elf.get());
if (!reader.LoadIntoMemory())
return false;
const bool is_wii = IsElfWii(filename);
if (is_wii)
HID4.SBE = 1;
SetupMSR();
SetupBAT(is_wii);
if (!reader.LoadSymbols())
{
if (LoadMapFromFilename())
HLE::PatchFunctions();
}
else
{
HLE::PatchFunctions();
}
PC = reader.GetEntryPoint();
return true;
}

View File

@ -2,7 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/Boot/Boot_DOL.h"
#include "Core/Boot/DolReader.h"
#include <cstring>
#include <string>
@ -12,29 +12,19 @@
#include "Common/Swap.h"
#include "Core/HW/Memmap.h"
CDolLoader::CDolLoader(const std::vector<u8>& buffer)
DolReader::DolReader(const std::vector<u8>& buffer) : BootExecutableReader(buffer)
{
m_is_valid = Initialize(buffer);
}
CDolLoader::CDolLoader(const std::string& filename)
DolReader::DolReader(const std::string& filename) : BootExecutableReader(filename)
{
const u64 size = File::GetSize(filename);
std::vector<u8> temp_buffer(size);
{
File::IOFile pStream(filename, "rb");
pStream.ReadBytes(temp_buffer.data(), temp_buffer.size());
}
m_is_valid = Initialize(temp_buffer);
m_is_valid = Initialize(m_bytes);
}
CDolLoader::~CDolLoader()
{
}
DolReader::~DolReader() = default;
bool CDolLoader::Initialize(const std::vector<u8>& buffer)
bool DolReader::Initialize(const std::vector<u8>& buffer)
{
if (buffer.size() < sizeof(SDolHeader))
return false;
@ -97,17 +87,30 @@ bool CDolLoader::Initialize(const std::vector<u8>& buffer)
return true;
}
void CDolLoader::Load() const
bool DolReader::LoadIntoMemory(bool only_in_mem1) const
{
if (!m_is_valid)
return false;
// load all text (code) sections
for (size_t i = 0; i < m_text_sections.size(); ++i)
if (!m_text_sections[i].empty())
if (!m_text_sections[i].empty() &&
!(only_in_mem1 &&
m_dolheader.textAddress[i] + m_text_sections[i].size() >= Memory::REALRAM_SIZE))
{
Memory::CopyToEmu(m_dolheader.textAddress[i], m_text_sections[i].data(),
m_text_sections[i].size());
}
// load all data sections
for (size_t i = 0; i < m_data_sections.size(); ++i)
if (!m_data_sections[i].empty())
if (!m_data_sections[i].empty() &&
!(only_in_mem1 &&
m_dolheader.dataAddress[i] + m_data_sections[i].size() >= Memory::REALRAM_SIZE))
{
Memory::CopyToEmu(m_dolheader.dataAddress[i], m_data_sections[i].data(),
m_data_sections[i].size());
}
return true;
}

View File

@ -8,20 +8,20 @@
#include <vector>
#include "Common/CommonTypes.h"
#include "Core/Boot/Boot.h"
class CDolLoader
class DolReader final : public BootExecutableReader
{
public:
CDolLoader(const std::string& filename);
CDolLoader(const std::vector<u8>& buffer);
~CDolLoader();
bool IsValid() const { return m_is_valid; }
bool IsWii() const { return m_is_wii; }
u32 GetEntryPoint() const { return m_dolheader.entryPoint; }
// Load into emulated memory
void Load() const;
DolReader(const std::string& filename);
DolReader(const std::vector<u8>& buffer);
~DolReader();
bool IsValid() const override { return m_is_valid; }
bool IsWii() const override { return m_is_wii; }
u32 GetEntryPoint() const override { return m_dolheader.entryPoint; }
bool LoadIntoMemory(bool only_in_mem1 = false) const override;
bool LoadSymbols() const override { return false; }
private:
enum
{

View File

@ -66,7 +66,17 @@ static void byteswapSection(Elf32_Shdr& sec)
bswap(sec.sh_type);
}
ElfReader::ElfReader(void* ptr)
ElfReader::ElfReader(const std::vector<u8>& buffer) : BootExecutableReader(buffer)
{
Initialize(m_bytes.data());
}
ElfReader::ElfReader(const std::string& filename) : BootExecutableReader(filename)
{
Initialize(m_bytes.data());
}
void ElfReader::Initialize(u8* ptr)
{
base = (char*)ptr;
base32 = (u32*)ptr;
@ -86,6 +96,8 @@ ElfReader::ElfReader(void* ptr)
byteswapSection(sections[i]);
}
entryPoint = header->e_entry;
bRelocate = (header->e_type != ET_EXEC);
}
const char* ElfReader::GetSectionName(int section) const
@ -103,13 +115,10 @@ const char* ElfReader::GetSectionName(int section) const
}
// This is just a simple elf loader, good enough to load elfs generated by devkitPPC
bool ElfReader::LoadIntoMemory(bool only_in_mem1)
bool ElfReader::LoadIntoMemory(bool only_in_mem1) const
{
INFO_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx);
// Should we relocate?
bRelocate = (header->e_type != ET_EXEC);
if (bRelocate)
{
PanicAlert("Error: Dolphin doesn't know how to load a relocatable elf.");
@ -160,7 +169,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
return -1;
}
bool ElfReader::LoadSymbols()
bool ElfReader::LoadSymbols() const
{
bool hasSymbols = false;
SectionID sec = GetSectionByName(".symtab");
@ -205,3 +214,31 @@ bool ElfReader::LoadSymbols()
g_symbolDB.Index();
return hasSymbols;
}
bool ElfReader::IsWii() const
{
// Use the same method as the DOL loader uses: search for mfspr from HID4,
// which should only be used in Wii ELFs.
//
// Likely to have some false positives/negatives, patches implementing a
// better heuristic are welcome.
// Swap these once, instead of swapping every word in the file.
u32 HID4_pattern = Common::swap32(0x7c13fba6);
u32 HID4_mask = Common::swap32(0xfc1fffff);
for (int i = 0; i < GetNumSegments(); ++i)
{
if (IsCodeSegment(i))
{
u32* code = (u32*)GetSegmentPtr(i);
for (u32 j = 0; j < GetSegmentSize(i) / sizeof(u32); ++j)
{
if ((code[j] & HID4_mask) == HID4_pattern)
return true;
}
}
}
return false;
}

View File

@ -5,6 +5,7 @@
#pragma once
#include "Common/CommonTypes.h"
#include "Core/Boot/Boot.h"
#include "Core/Boot/ElfTypes.h"
enum KnownElfTypes
@ -17,31 +18,23 @@ enum KnownElfTypes
typedef int SectionID;
class ElfReader
class ElfReader final : public BootExecutableReader
{
private:
char* base;
u32* base32;
Elf32_Ehdr* header;
Elf32_Phdr* segments;
Elf32_Shdr* sections;
u32* sectionAddrs;
bool bRelocate;
u32 entryPoint;
public:
explicit ElfReader(void* ptr);
ElfReader(const std::string& filename);
ElfReader(const std::vector<u8>& buffer);
~ElfReader() {}
u32 Read32(int off) const { return base32[off >> 2]; }
// Quick accessors
ElfType GetType() const { return (ElfType)(header->e_type); }
ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); }
u32 GetEntryPoint() const { return entryPoint; }
u32 GetEntryPoint() const override { return entryPoint; }
u32 GetFlags() const { return (u32)(header->e_flags); }
bool LoadIntoMemory(bool only_in_mem1 = false);
bool LoadSymbols();
bool LoadIntoMemory(bool only_in_mem1 = false) const override;
bool LoadSymbols() const override;
// TODO: actually check for validity.
bool IsValid() const override { return true; }
bool IsWii() const override;
int GetNumSegments() const { return (int)(header->e_phnum); }
int GetNumSections() const { return (int)(header->e_shnum); }
@ -57,11 +50,24 @@ public:
return nullptr;
}
bool IsCodeSegment(int segment) const { return segments[segment].p_flags & PF_X; }
const u8* GetSegmentPtr(int segment) { return GetPtr(segments[segment].p_offset); }
const u8* GetSegmentPtr(int segment) const { return GetPtr(segments[segment].p_offset); }
int GetSegmentSize(int segment) const { return segments[segment].p_filesz; }
u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; }
int GetSectionSize(SectionID section) const { return sections[section].sh_size; }
SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
bool DidRelocate() const { return bRelocate; }
private:
void Initialize(u8* bytes);
char* base;
u32* base32;
Elf32_Ehdr* header;
Elf32_Phdr* segments;
Elf32_Shdr* sections;
u32* sectionAddrs;
bool bRelocate;
u32 entryPoint;
};

View File

@ -21,9 +21,8 @@ set(SRCS
WiiRoot.cpp
Boot/Boot_BS2Emu.cpp
Boot/Boot.cpp
Boot/Boot_DOL.cpp
Boot/Boot_ELF.cpp
Boot/Boot_WiiWAD.cpp
Boot/DolReader.cpp
Boot/ElfReader.cpp
Config/Config.cpp
Config/GraphicsSettings.cpp

View File

@ -23,7 +23,6 @@
#include "Core/Analytics.h"
#include "Core/Boot/Boot.h"
#include "Core/Boot/Boot_DOL.h"
#include "Core/Config/Config.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@ -896,10 +895,10 @@ struct SetGameMetadata
bool operator()(const BootParameters::Executable& executable) const
{
if (executable.type == BootParameters::Executable::Type::DOL)
config->bWii = CDolLoader{executable.path}.IsWii();
if (executable.type == BootParameters::Executable::Type::ELF)
config->bWii = CBoot::IsElfWii(executable.path);
if (!executable.reader->IsValid())
return false;
config->bWii = executable.reader->IsWii();
// TODO: Right now GC homebrew boots in NTSC and Wii homebrew in PAL.
// This is intentional so that Wii homebrew can boot in both 50Hz and 60Hz,

View File

@ -52,9 +52,8 @@
<ClCompile Include="BootManager.cpp" />
<ClCompile Include="Boot\Boot.cpp" />
<ClCompile Include="Boot\Boot_BS2Emu.cpp" />
<ClCompile Include="Boot\Boot_DOL.cpp" />
<ClCompile Include="Boot\Boot_ELF.cpp" />
<ClCompile Include="Boot\Boot_WiiWAD.cpp" />
<ClCompile Include="Boot\DolReader.cpp" />
<ClCompile Include="Boot\ElfReader.cpp" />
<ClCompile Include="Config\Config.cpp" />
<ClCompile Include="Config\GraphicsSettings.cpp" />
@ -308,7 +307,7 @@
<ClInclude Include="ARDecrypt.h" />
<ClInclude Include="BootManager.h" />
<ClInclude Include="Boot\Boot.h" />
<ClInclude Include="Boot\Boot_DOL.h" />
<ClInclude Include="Boot\DolReader.h" />
<ClInclude Include="Boot\ElfReader.h" />
<ClInclude Include="Boot\ElfTypes.h" />
<ClInclude Include="Config\Config.h" />
@ -583,4 +582,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -182,15 +182,12 @@
<ClCompile Include="Boot\Boot_BS2Emu.cpp">
<Filter>Boot</Filter>
</ClCompile>
<ClCompile Include="Boot\Boot_DOL.cpp">
<Filter>Boot</Filter>
</ClCompile>
<ClCompile Include="Boot\Boot_ELF.cpp">
<Filter>Boot</Filter>
</ClCompile>
<ClCompile Include="Boot\Boot_WiiWAD.cpp">
<Filter>Boot</Filter>
</ClCompile>
<ClCompile Include="Boot\DolReader.cpp">
<Filter>Boot</Filter>
</ClCompile>
<ClCompile Include="Boot\ElfReader.cpp">
<Filter>Boot</Filter>
</ClCompile>
@ -888,7 +885,7 @@
<ClInclude Include="Boot\Boot.h">
<Filter>Boot</Filter>
</ClInclude>
<ClInclude Include="Boot\Boot_DOL.h">
<ClInclude Include="Boot\DolReader.h">
<Filter>Boot</Filter>
</ClInclude>
<ClInclude Include="Boot\ElfReader.h">

View File

@ -18,7 +18,7 @@
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/Boot/Boot_DOL.h"
#include "Core/Boot/DolReader.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
@ -282,14 +282,15 @@ bool Kernel::BootstrapPPC(const DiscIO::NANDContentLoader& content_loader)
if (!content)
return false;
const auto dol_loader = std::make_unique<CDolLoader>(content->m_Data->Get());
const auto dol_loader = std::make_unique<DolReader>(content->m_Data->Get());
if (!dol_loader->IsValid())
return false;
if (!SetupMemory(m_title_id, MemorySetupType::Full))
return false;
dol_loader->Load();
if (!dol_loader->LoadIntoMemory())
return false;
// NAND titles start with address translation off at 0x3400 (via the PPC bootstub)
// The state of other CPU registers (like the BAT registers) doesn't matter much

View File

@ -134,8 +134,7 @@ bool Load()
return false;
}
std::vector<u8> elf_bytes = mios.GetElf();
ElfReader elf{elf_bytes.data()};
ElfReader elf{mios.GetElf()};
if (!elf.LoadIntoMemory(true))
{
PanicAlertT("Failed to load MIOS ELF into memory.");