From 065261dbadab5793d245faed996b4cd26ff58321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 28 May 2017 17:12:38 +0200 Subject: [PATCH] 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. --- Source/Core/Core/Boot/Boot.cpp | 117 +++++++++--------- Source/Core/Core/Boot/Boot.h | 30 +++-- Source/Core/Core/Boot/Boot_ELF.cpp | 89 ------------- .../Core/Boot/{Boot_DOL.cpp => DolReader.cpp} | 41 +++--- .../Core/Boot/{Boot_DOL.h => DolReader.h} | 20 +-- Source/Core/Core/Boot/ElfReader.cpp | 49 +++++++- Source/Core/Core/Boot/ElfReader.h | 42 ++++--- Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/ConfigManager.cpp | 9 +- Source/Core/Core/Core.vcxproj | 7 +- Source/Core/Core/Core.vcxproj.filters | 11 +- Source/Core/Core/IOS/IOS.cpp | 7 +- Source/Core/Core/IOS/MIOS.cpp | 3 +- 13 files changed, 195 insertions(+), 233 deletions(-) delete mode 100644 Source/Core/Core/Boot/Boot_ELF.cpp rename Source/Core/Core/Boot/{Boot_DOL.cpp => DolReader.cpp} (75%) rename Source/Core/Core/Boot/{Boot_DOL.h => DolReader.h} (63%) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 43296eb784..65394fd05f 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -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::GenerateFromFile(const std::stri return std::make_unique(Disc{path, std::move(volume)}); } - if (extension == ".elf" || extension == ".dol") - { - return std::make_unique( - Executable{path, extension == ".elf" ? Executable::Type::ELF : Executable::Type::DOL}); - } + if (extension == ".elf") + return std::make_unique(Executable{path, std::make_unique(path)}); + + if (extension == ".dol") + return std::make_unique(Executable{path, std::make_unique(path)}); if (extension == ".dff") return std::make_unique(DFF{path}); @@ -371,14 +372,13 @@ bool CBoot::BootUp(std::unique_ptr 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 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 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& bytes) : m_bytes(bytes) +{ +} + +BootExecutableReader::~BootExecutableReader() = default; diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 742bb056e5..562c067685 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -5,9 +5,11 @@ #pragma once #include +#include #include #include #include +#include #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 reader; }; struct NAND @@ -72,7 +71,6 @@ class CBoot { public: static bool BootUp(std::unique_ptr 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& 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 m_bytes; +}; diff --git a/Source/Core/Core/Boot/Boot_ELF.cpp b/Source/Core/Core/Boot/Boot_ELF.cpp deleted file mode 100644 index 1cef0937ba..0000000000 --- a/Source/Core/Core/Boot/Boot_ELF.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include - -#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(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(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; -} diff --git a/Source/Core/Core/Boot/Boot_DOL.cpp b/Source/Core/Core/Boot/DolReader.cpp similarity index 75% rename from Source/Core/Core/Boot/Boot_DOL.cpp rename to Source/Core/Core/Boot/DolReader.cpp index b86169744d..7f17696240 100644 --- a/Source/Core/Core/Boot/Boot_DOL.cpp +++ b/Source/Core/Core/Boot/DolReader.cpp @@ -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 #include @@ -12,29 +12,19 @@ #include "Common/Swap.h" #include "Core/HW/Memmap.h" -CDolLoader::CDolLoader(const std::vector& buffer) +DolReader::DolReader(const std::vector& 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 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& buffer) +bool DolReader::Initialize(const std::vector& buffer) { if (buffer.size() < sizeof(SDolHeader)) return false; @@ -97,17 +87,30 @@ bool CDolLoader::Initialize(const std::vector& 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; } diff --git a/Source/Core/Core/Boot/Boot_DOL.h b/Source/Core/Core/Boot/DolReader.h similarity index 63% rename from Source/Core/Core/Boot/Boot_DOL.h rename to Source/Core/Core/Boot/DolReader.h index f415e35705..43b1c0f354 100644 --- a/Source/Core/Core/Boot/Boot_DOL.h +++ b/Source/Core/Core/Boot/DolReader.h @@ -8,20 +8,20 @@ #include #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& 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& 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 { diff --git a/Source/Core/Core/Boot/ElfReader.cpp b/Source/Core/Core/Boot/ElfReader.cpp index 91eeba7487..4f9297b06d 100644 --- a/Source/Core/Core/Boot/ElfReader.cpp +++ b/Source/Core/Core/Boot/ElfReader.cpp @@ -66,7 +66,17 @@ static void byteswapSection(Elf32_Shdr& sec) bswap(sec.sh_type); } -ElfReader::ElfReader(void* ptr) +ElfReader::ElfReader(const std::vector& 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; +} diff --git a/Source/Core/Core/Boot/ElfReader.h b/Source/Core/Core/Boot/ElfReader.h index e3d7c257a8..014d0667c3 100644 --- a/Source/Core/Core/Boot/ElfReader.h +++ b/Source/Core/Core/Boot/ElfReader.h @@ -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& 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; }; diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index e3684b891d..b29f42fef8 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -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 diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 342cb60527..b94f06e820 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -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, diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 6616031f96..7fccbc5fed 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -52,9 +52,8 @@ - - + @@ -308,7 +307,7 @@ - + @@ -583,4 +582,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 27485da41f..a0a74fc073 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -182,15 +182,12 @@ Boot - - Boot - - - Boot - Boot + + Boot + Boot @@ -888,7 +885,7 @@ Boot - + Boot diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index abbc7b2444..56cb56ae10 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -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(content->m_Data->Get()); + const auto dol_loader = std::make_unique(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 diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index 57f9451d43..47a4fd5f6d 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -134,8 +134,7 @@ bool Load() return false; } - std::vector 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.");