mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-26 15:40:41 +00:00
System: Support loading ELF files
Some checks failed
Create rolling release / Windows x64 Build (push) Has been cancelled
Create rolling release / Windows x64 SSE2 Build (push) Has been cancelled
Create rolling release / Windows ARM64 Build (push) Has been cancelled
Create rolling release / Linux x64 AppImage (push) Has been cancelled
Create rolling release / Linux x64 SSE2 AppImage (push) Has been cancelled
Create rolling release / Linux Flatpak Build (push) Has been cancelled
Create rolling release / MacOS Universal Build (push) Has been cancelled
Create rolling release / Create Release (push) Has been cancelled
Some checks failed
Create rolling release / Windows x64 Build (push) Has been cancelled
Create rolling release / Windows x64 SSE2 Build (push) Has been cancelled
Create rolling release / Windows ARM64 Build (push) Has been cancelled
Create rolling release / Linux x64 AppImage (push) Has been cancelled
Create rolling release / Linux x64 SSE2 AppImage (push) Has been cancelled
Create rolling release / Linux Flatpak Build (push) Has been cancelled
Create rolling release / MacOS Universal Build (push) Has been cancelled
Create rolling release / Create Release (push) Has been cancelled
This commit is contained in:
parent
65f3dcbe9b
commit
ead9e56c4d
@ -22,6 +22,7 @@
|
|||||||
X(DInputSource) \
|
X(DInputSource) \
|
||||||
X(DMA) \
|
X(DMA) \
|
||||||
X(DynamicLibrary) \
|
X(DynamicLibrary) \
|
||||||
|
X(FileLoader) \
|
||||||
X(FileSystem) \
|
X(FileSystem) \
|
||||||
X(FullscreenUI) \
|
X(FullscreenUI) \
|
||||||
X(GDBProtocol) \
|
X(GDBProtocol) \
|
||||||
@ -55,7 +56,6 @@
|
|||||||
X(Multitap) \
|
X(Multitap) \
|
||||||
X(NeGconRumble) \
|
X(NeGconRumble) \
|
||||||
X(PCDrv) \
|
X(PCDrv) \
|
||||||
X(PSFLoader) \
|
|
||||||
X(Pad) \
|
X(Pad) \
|
||||||
X(PerfMon) \
|
X(PerfMon) \
|
||||||
X(PlatformMisc) \
|
X(PlatformMisc) \
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "timing_event.h"
|
#include "timing_event.h"
|
||||||
|
|
||||||
#include "util/cd_image.h"
|
#include "util/cd_image.h"
|
||||||
|
#include "util/elf_file.h"
|
||||||
#include "util/state_wrapper.h"
|
#include "util/state_wrapper.h"
|
||||||
|
|
||||||
#include "common/align.h"
|
#include "common/align.h"
|
||||||
@ -170,6 +171,7 @@ static void SetRAMPageWritable(u32 page_index, bool writable);
|
|||||||
static void KernelInitializedHook();
|
static void KernelInitializedHook();
|
||||||
static bool SideloadEXE(const std::string& path, Error* error);
|
static bool SideloadEXE(const std::string& path, Error* error);
|
||||||
static bool InjectCPE(std::span<const u8> buffer, bool set_pc, Error* error);
|
static bool InjectCPE(std::span<const u8> buffer, bool set_pc, Error* error);
|
||||||
|
static bool InjectELF(const ELFFile& elf, bool set_pc, Error* error);
|
||||||
|
|
||||||
static void SetHandlers();
|
static void SetHandlers();
|
||||||
static void UpdateMappedRAMSize();
|
static void UpdateMappedRAMSize();
|
||||||
@ -972,15 +974,12 @@ bool Bus::InjectExecutable(std::span<const u8> buffer, bool set_pc, Error* error
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.memfill_size > 0)
|
if (header.memfill_size > 0 &&
|
||||||
|
!CPU::SafeZeroMemoryBytes(header.memfill_start & ~UINT32_C(3), Common::AlignDownPow2(header.memfill_size, 4)))
|
||||||
{
|
{
|
||||||
const u32 words_to_write = header.memfill_size / 4;
|
Error::SetStringFmt(error, "Failed to zero {} bytes of memory at address 0x{:08X}.", header.memfill_start,
|
||||||
u32 address = header.memfill_start & ~UINT32_C(3);
|
header.memfill_size);
|
||||||
for (u32 i = 0; i < words_to_write; i++)
|
return false;
|
||||||
{
|
|
||||||
CPU::SafeWriteMemoryWord(address, 0);
|
|
||||||
address += sizeof(u32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 data_load_size =
|
const u32 data_load_size =
|
||||||
@ -1145,6 +1144,34 @@ bool Bus::InjectCPE(std::span<const u8> buffer, bool set_pc, Error* error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bus::InjectELF(const ELFFile& elf, bool set_pc, Error* error)
|
||||||
|
{
|
||||||
|
const bool okay = elf.LoadExecutableSections(
|
||||||
|
[](std::span<const u8> data, u32 dest_addr, u32 dest_size, Error* error) {
|
||||||
|
if (!data.empty() && !CPU::SafeWriteMemoryBytes(dest_addr, data))
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Failed to load {} bytes to 0x{:08X}", data.size(), dest_addr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 zero_addr = dest_addr + static_cast<u32>(data.size());
|
||||||
|
const u32 zero_bytes = dest_size - static_cast<u32>(data.size());
|
||||||
|
if (zero_bytes > 0 && !CPU::SafeZeroMemoryBytes(zero_addr, zero_bytes))
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Failed to zero {} bytes at 0x{:08X}", zero_bytes, zero_addr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
error);
|
||||||
|
|
||||||
|
if (okay && set_pc)
|
||||||
|
CPU::SetPC(elf.GetEntryPoint());
|
||||||
|
|
||||||
|
return okay;
|
||||||
|
}
|
||||||
|
|
||||||
void Bus::KernelInitializedHook()
|
void Bus::KernelInitializedHook()
|
||||||
{
|
{
|
||||||
if (s_kernel_initialize_hook_run)
|
if (s_kernel_initialize_hook_run)
|
||||||
@ -1179,8 +1206,7 @@ void Bus::KernelInitializedHook()
|
|||||||
|
|
||||||
bool Bus::SideloadEXE(const std::string& path, Error* error)
|
bool Bus::SideloadEXE(const std::string& path, Error* error)
|
||||||
{
|
{
|
||||||
const std::optional<DynamicHeapArray<u8>> exe_data =
|
std::optional<DynamicHeapArray<u8>> exe_data = FileSystem::ReadBinaryFile(path.c_str(), error);
|
||||||
FileSystem::ReadBinaryFile(System::GetExeOverride().c_str(), error);
|
|
||||||
if (!exe_data.has_value())
|
if (!exe_data.has_value())
|
||||||
{
|
{
|
||||||
Error::AddPrefixFmt(error, "Failed to read {}: ", Path::GetFileName(path));
|
Error::AddPrefixFmt(error, "Failed to read {}: ", Path::GetFileName(path));
|
||||||
@ -1195,6 +1221,14 @@ bool Bus::SideloadEXE(const std::string& path, Error* error)
|
|||||||
{
|
{
|
||||||
okay = InjectCPE(exe_data->cspan(), true, error);
|
okay = InjectCPE(exe_data->cspan(), true, error);
|
||||||
}
|
}
|
||||||
|
else if (StringUtil::EndsWithNoCase(filename, ".elf"))
|
||||||
|
{
|
||||||
|
ELFFile elf;
|
||||||
|
if (!elf.Open(std::move(exe_data.value()), error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
okay = InjectELF(elf, true, error);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// look for a libps.exe next to the exe, if it exists, load it
|
// look for a libps.exe next to the exe, if it exists, load it
|
||||||
|
@ -3212,6 +3212,47 @@ bool CPU::SafeWriteMemoryBytes(VirtualMemoryAddress addr, const std::span<const
|
|||||||
return SafeWriteMemoryBytes(addr, data.data(), static_cast<u32>(data.size()));
|
return SafeWriteMemoryBytes(addr, data.data(), static_cast<u32>(data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CPU::SafeZeroMemoryBytes(VirtualMemoryAddress addr, u32 length)
|
||||||
|
{
|
||||||
|
using namespace Bus;
|
||||||
|
|
||||||
|
const u32 seg = (addr >> 29);
|
||||||
|
if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & PHYSICAL_MEMORY_ADDRESS_MASK) >= RAM_MIRROR_END) ||
|
||||||
|
(((addr & g_ram_mask) + length) > g_ram_size))
|
||||||
|
{
|
||||||
|
while ((addr & 3u) != 0 && length > 0)
|
||||||
|
{
|
||||||
|
if (!CPU::SafeWriteMemoryByte(addr, 0)) [[unlikely]]
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addr++;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
while (length >= 4)
|
||||||
|
{
|
||||||
|
if (!CPU::SafeWriteMemoryWord(addr, 0)) [[unlikely]]
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addr += 4;
|
||||||
|
length -= 4;
|
||||||
|
}
|
||||||
|
while (length > 0)
|
||||||
|
{
|
||||||
|
if (!CPU::SafeWriteMemoryByte(addr, 0)) [[unlikely]]
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addr++;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path: all in RAM, no wraparound.
|
||||||
|
std::memset(&g_ram[addr & g_ram_mask], 0, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void* CPU::GetDirectReadMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size, TickCount* read_ticks)
|
void* CPU::GetDirectReadMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size, TickCount* read_ticks)
|
||||||
{
|
{
|
||||||
using namespace Bus;
|
using namespace Bus;
|
||||||
|
@ -185,6 +185,7 @@ bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
|
|||||||
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
|
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
|
||||||
bool SafeWriteMemoryBytes(VirtualMemoryAddress addr, const void* data, u32 length);
|
bool SafeWriteMemoryBytes(VirtualMemoryAddress addr, const void* data, u32 length);
|
||||||
bool SafeWriteMemoryBytes(VirtualMemoryAddress addr, const std::span<const u8> data);
|
bool SafeWriteMemoryBytes(VirtualMemoryAddress addr, const std::span<const u8> data);
|
||||||
|
bool SafeZeroMemoryBytes(VirtualMemoryAddress addr, u32 length);
|
||||||
|
|
||||||
// External IRQs
|
// External IRQs
|
||||||
void SetIRQRequest(bool state);
|
void SetIRQRequest(bool state);
|
||||||
|
@ -949,8 +949,8 @@ void FullscreenUI::DestroyResources()
|
|||||||
|
|
||||||
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetDiscImageFilters()
|
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetDiscImageFilters()
|
||||||
{
|
{
|
||||||
return {"*.bin", "*.cue", "*.iso", "*.img", "*.chd", "*.ecm", "*.mds", "*.psexe",
|
return {"*.bin", "*.cue", "*.iso", "*.img", "*.chd", "*.ecm", "*.mds", "*.cpe", "*.elf",
|
||||||
"*.ps-exe", "*.exe", "*.psx", "*.psf", "*.minipsf", "*.m3u", "*.pbp"};
|
"*.psexe", "*.ps-exe", "*.exe", "*.psx", "*.psf", "*.minipsf", "*.m3u", "*.pbp"};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullscreenUI::DoStartPath(std::string path, std::string state, std::optional<bool> fast_boot)
|
void FullscreenUI::DoStartPath(std::string path, std::string state, std::optional<bool> fast_boot)
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
LOG_CHANNEL(PSFLoader);
|
LOG_CHANNEL(FileLoader);
|
||||||
|
|
||||||
namespace PSFLoader {
|
namespace PSFLoader {
|
||||||
static bool LoadLibraryPSF(const std::string& path, bool use_pc_sp, Error* error, u32 depth = 0);
|
static bool LoadLibraryPSF(const std::string& path, bool use_pc_sp, Error* error, u32 depth = 0);
|
||||||
|
@ -793,7 +793,7 @@ bool System::IsExePath(std::string_view path)
|
|||||||
{
|
{
|
||||||
return (StringUtil::EndsWithNoCase(path, ".exe") || StringUtil::EndsWithNoCase(path, ".psexe") ||
|
return (StringUtil::EndsWithNoCase(path, ".exe") || StringUtil::EndsWithNoCase(path, ".psexe") ||
|
||||||
StringUtil::EndsWithNoCase(path, ".ps-exe") || StringUtil::EndsWithNoCase(path, ".psx") ||
|
StringUtil::EndsWithNoCase(path, ".ps-exe") || StringUtil::EndsWithNoCase(path, ".psx") ||
|
||||||
StringUtil::EndsWithNoCase(path, ".cpe"));
|
StringUtil::EndsWithNoCase(path, ".cpe") || StringUtil::EndsWithNoCase(path, ".elf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsPsfPath(std::string_view path)
|
bool System::IsPsfPath(std::string_view path)
|
||||||
@ -811,7 +811,7 @@ bool System::IsLoadablePath(std::string_view path)
|
|||||||
{
|
{
|
||||||
static constexpr const std::array extensions = {
|
static constexpr const std::array extensions = {
|
||||||
".bin", ".cue", ".img", ".iso", ".chd", ".ecm", ".mds", // discs
|
".bin", ".cue", ".img", ".iso", ".chd", ".ecm", ".mds", // discs
|
||||||
".exe", ".psexe", ".ps-exe", ".psx", ".cpe", // exes
|
".exe", ".psexe", ".ps-exe", ".psx", ".cpe", ".elf", // exes
|
||||||
".psf", ".minipsf", // psf
|
".psf", ".minipsf", // psf
|
||||||
".psxgpu", ".psxgpu.zst", ".psxgpu.xz", // gpu dump
|
".psxgpu", ".psxgpu.zst", ".psxgpu.xz", // gpu dump
|
||||||
".m3u", // playlists
|
".m3u", // playlists
|
||||||
|
@ -61,11 +61,12 @@
|
|||||||
LOG_CHANNEL(Host);
|
LOG_CHANNEL(Host);
|
||||||
|
|
||||||
static constexpr char DISC_IMAGE_FILTER[] = QT_TRANSLATE_NOOP(
|
static constexpr char DISC_IMAGE_FILTER[] = QT_TRANSLATE_NOOP(
|
||||||
"MainWindow", "All File Types (*.bin *.img *.iso *.cue *.chd *.ecm *.mds *.pbp *.exe *.psexe *.ps-exe *.psx *.psf "
|
"MainWindow",
|
||||||
"*.minipsf *.m3u *.psxgpu);;Single-Track Raw Images (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD "
|
"All File Types (*.bin *.img *.iso *.cue *.chd *.cpe *.ecm *.mds *.pbp *.elf *.exe *.psexe *.ps-exe *.psx *.psf "
|
||||||
"Images (*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images "
|
"*.minipsf *.m3u *.psxgpu);;Single-Track Raw Images (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD Images "
|
||||||
"(*.mds);;PlayStation EBOOTs (*.pbp *.PBP);;PlayStation Executables (*.exe *.psexe *.ps-exe, "
|
"(*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images (*.mds);;PlayStation EBOOTs (*.pbp "
|
||||||
"*.psx);;Portable Sound Format Files (*.psf *.minipsf);;Playlists (*.m3u);;PSX GPU Dumps (*.psxgpu)");
|
"*.PBP);;PlayStation Executables (*.cpe *.elf *.exe *.psexe *.ps-exe, *.psx);;Portable Sound Format Files (*.psf "
|
||||||
|
"*.minipsf);;Playlists (*.m3u);;PSX GPU Dumps (*.psxgpu)");
|
||||||
|
|
||||||
MainWindow* g_main_window = nullptr;
|
MainWindow* g_main_window = nullptr;
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ add_library(util
|
|||||||
compress_helpers.h
|
compress_helpers.h
|
||||||
cue_parser.cpp
|
cue_parser.cpp
|
||||||
cue_parser.h
|
cue_parser.h
|
||||||
|
elf_file.cpp
|
||||||
|
elf_file.h
|
||||||
gpu_device.cpp
|
gpu_device.cpp
|
||||||
gpu_device.h
|
gpu_device.h
|
||||||
gpu_framebuffer_manager.h
|
gpu_framebuffer_manager.h
|
||||||
|
204
src/util/elf_file.cpp
Normal file
204
src/util/elf_file.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
#include "elf_file.h"
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/file_system.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
LOG_CHANNEL(FileLoader);
|
||||||
|
|
||||||
|
static constexpr s64 MAX_ELF_FILE_SIZE = 32 * 1024 * 1024;
|
||||||
|
|
||||||
|
ELFFile::ELFFile() = default;
|
||||||
|
|
||||||
|
ELFFile::~ELFFile() = default;
|
||||||
|
|
||||||
|
const ELFFile::Elf32_Ehdr& ELFFile::GetELFHeader() const
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<const Elf32_Ehdr*>(&m_data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ELFFile::GetEntryPoint() const
|
||||||
|
{
|
||||||
|
return GetELFHeader().e_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ELFFile::Elf32_Shdr* ELFFile::GetSectionHeader(u32 index) const
|
||||||
|
{
|
||||||
|
const Elf32_Ehdr& hdr = GetELFHeader();
|
||||||
|
if (index == SHN_UNDEF || index >= hdr.e_shnum || hdr.e_shentsize < sizeof(Elf32_Shdr))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const size_t offset = hdr.e_shoff + index * static_cast<size_t>(hdr.e_shentsize);
|
||||||
|
if ((offset + sizeof(Elf32_Shdr)) > m_data.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return reinterpret_cast<const Elf32_Shdr*>(&m_data[offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view ELFFile::GetSectionName(const Elf32_Shdr& section) const
|
||||||
|
{
|
||||||
|
const Elf32_Shdr* strhdr = GetSectionHeader(GetELFHeader().e_shstrndx);
|
||||||
|
if (!strhdr || section.sh_name >= strhdr->sh_size)
|
||||||
|
return std::string_view();
|
||||||
|
|
||||||
|
const size_t file_offset = strhdr->sh_offset;
|
||||||
|
const u32 start_offset = section.sh_name;
|
||||||
|
u32 current_offset = start_offset;
|
||||||
|
while (current_offset < strhdr->sh_size && (current_offset + file_offset) < m_data.size())
|
||||||
|
{
|
||||||
|
if (m_data[file_offset + current_offset] == '\0')
|
||||||
|
break;
|
||||||
|
|
||||||
|
current_offset++;
|
||||||
|
}
|
||||||
|
if (current_offset == start_offset)
|
||||||
|
return std::string_view();
|
||||||
|
|
||||||
|
return std::string_view(reinterpret_cast<const char*>(&m_data[file_offset + start_offset]),
|
||||||
|
current_offset - start_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ELFFile::GetSectionCount() const
|
||||||
|
{
|
||||||
|
return GetELFHeader().e_shnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ELFFile::Elf32_Phdr* ELFFile::GetProgramHeader(u32 index) const
|
||||||
|
{
|
||||||
|
const Elf32_Ehdr& hdr = GetELFHeader();
|
||||||
|
if (index >= hdr.e_phnum || hdr.e_phentsize < sizeof(Elf32_Phdr))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const size_t offset = hdr.e_phoff + index * static_cast<size_t>(hdr.e_phentsize);
|
||||||
|
if ((offset + sizeof(Elf32_Phdr)) > m_data.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return reinterpret_cast<const Elf32_Phdr*>(&m_data[offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ELFFile::GetProgramHeaderCount() const
|
||||||
|
{
|
||||||
|
return GetELFHeader().e_phnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ELFFile::Open(const char* path, Error* error)
|
||||||
|
{
|
||||||
|
auto fp = FileSystem::OpenManagedCFile(path, "rb", error);
|
||||||
|
if (!fp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const s64 size = FileSystem::FSize64(fp.get(), error);
|
||||||
|
if (size < 0)
|
||||||
|
return false;
|
||||||
|
if (size >= MAX_ELF_FILE_SIZE)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "File is too large.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataArray data(static_cast<size_t>(size));
|
||||||
|
if (std::fread(data.data(), data.size(), 1, fp.get()) != 1)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "fread() failed: ", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Open(std::move(data), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ELFFile::Open(DataArray data, Error* error)
|
||||||
|
{
|
||||||
|
m_data = std::move(data);
|
||||||
|
|
||||||
|
static constexpr const u8 EXPECTED_HEADER[4] = {'\177', 'E', 'L', 'F'};
|
||||||
|
|
||||||
|
if (m_data.size() < sizeof(Elf32_Ehdr) || std::memcmp(m_data.data(), EXPECTED_HEADER, sizeof(EXPECTED_HEADER)) != 0)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Invalid header.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Elf32_Ehdr& hdr = GetELFHeader();
|
||||||
|
if (hdr.e_machine != EM_MIPS)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Unsupported machine type {}.", hdr.e_machine);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 section_count = GetSectionCount();
|
||||||
|
const u32 proghdr_count = GetProgramHeaderCount();
|
||||||
|
|
||||||
|
DEV_LOG("ELF Sections={} ProgramHeaders={} Entry=0x{:08X}", section_count, proghdr_count, hdr.e_entry);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < section_count; i++)
|
||||||
|
{
|
||||||
|
const Elf32_Shdr* shdr = GetSectionHeader(i);
|
||||||
|
if (!shdr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DEV_LOG("Section {}: Name={} Size={}", i, GetSectionName(*shdr), shdr->sh_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < proghdr_count; i++)
|
||||||
|
{
|
||||||
|
const Elf32_Phdr* phdr = GetProgramHeader(i);
|
||||||
|
if (!phdr || phdr->p_type != PT_LOAD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DEV_LOG("Program Header {}: Load {} at 0x{:08X}", i, phdr->p_filesz, phdr->p_vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ELFFile::LoadExecutableSections(const LoadExecutableSectionCallback& callback, Error* error) const
|
||||||
|
{
|
||||||
|
const u32 entry = GetELFHeader().e_entry;
|
||||||
|
bool loaded_entry = false;
|
||||||
|
|
||||||
|
const u32 ph_count = GetProgramHeaderCount();
|
||||||
|
for (u32 i = 0; i < ph_count; i++)
|
||||||
|
{
|
||||||
|
const Elf32_Phdr* phdr = GetProgramHeader(i);
|
||||||
|
if (!phdr)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Failed to find program header {}", i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phdr->p_type != PT_LOAD)
|
||||||
|
{
|
||||||
|
// ignore section
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<const u8> data;
|
||||||
|
if (phdr->p_filesz > 0)
|
||||||
|
{
|
||||||
|
if ((phdr->p_offset + static_cast<size_t>(phdr->p_filesz)) > m_data.size())
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Program header {} is out of file range {} {} {}", i, phdr->p_offset, phdr->p_filesz,
|
||||||
|
m_data.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = m_data.cspan(phdr->p_offset, phdr->p_filesz);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callback(data, phdr->p_vaddr, std::max(phdr->p_memsz, phdr->p_filesz), error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
loaded_entry |= (entry >= phdr->p_vaddr && entry < (phdr->p_vaddr + phdr->p_memsz));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loaded_entry)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Entry point 0x{:08X} not loaded.", entry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
116
src/util/elf_file.h
Normal file
116
src/util/elf_file.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/heap_array.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
|
class ELFFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using DataArray = DynamicHeapArray<u8>;
|
||||||
|
|
||||||
|
// ELF header constants
|
||||||
|
static constexpr u8 EI_NIDENT = 16;
|
||||||
|
static constexpr u16 ET_EXEC = 2;
|
||||||
|
static constexpr u16 ET_DYN = 3;
|
||||||
|
static constexpr u16 EM_MIPS = 8;
|
||||||
|
static constexpr u16 SHN_UNDEF = 0;
|
||||||
|
static constexpr u32 SHT_NULL = 0;
|
||||||
|
static constexpr u32 SHT_PROGBITS = 1;
|
||||||
|
static constexpr u32 SHT_SYMTAB = 2;
|
||||||
|
static constexpr u32 SHT_STRTAB = 3;
|
||||||
|
static constexpr u32 SHT_RELA = 4;
|
||||||
|
static constexpr u32 SHT_HASH = 5;
|
||||||
|
static constexpr u32 SHT_DYNAMIC = 6;
|
||||||
|
static constexpr u32 SHT_NOTE = 7;
|
||||||
|
static constexpr u32 SHT_NOBITS = 8;
|
||||||
|
static constexpr u32 SHT_REL = 9;
|
||||||
|
static constexpr u32 SHT_SHLIB = 10;
|
||||||
|
static constexpr u32 SHT_DYNSYM = 11;
|
||||||
|
static constexpr u32 SHT_NUM = 12;
|
||||||
|
static constexpr u32 PT_NULL = 0;
|
||||||
|
static constexpr u32 PT_LOAD = 1;
|
||||||
|
static constexpr u32 PT_DYNAMIC = 2;
|
||||||
|
static constexpr u32 PT_INTERP = 3;
|
||||||
|
static constexpr u32 PT_NOTE = 4;
|
||||||
|
static constexpr u32 PT_SHLIB = 5;
|
||||||
|
static constexpr u32 PT_PHDR = 6;
|
||||||
|
static constexpr u32 PT_TLS = 7;
|
||||||
|
|
||||||
|
// ELF Header structure
|
||||||
|
struct Elf32_Ehdr
|
||||||
|
{
|
||||||
|
u8 e_ident[EI_NIDENT]; // Magic number and other information
|
||||||
|
u16 e_type; // Object file type
|
||||||
|
u16 e_machine; // Architecture
|
||||||
|
u32 e_version; // Object file version
|
||||||
|
u32 e_entry; // Entry point virtual address
|
||||||
|
u32 e_phoff; // Program header table file offset
|
||||||
|
u32 e_shoff; // Section header table file offset
|
||||||
|
u32 e_flags; // Processor-specific flags
|
||||||
|
u16 e_ehsize; // ELF header size in bytes
|
||||||
|
u16 e_phentsize; // Program header table entry size
|
||||||
|
u16 e_phnum; // Program header table entry count
|
||||||
|
u16 e_shentsize; // Section header table entry size
|
||||||
|
u16 e_shnum; // Section header table entry count
|
||||||
|
u16 e_shstrndx; // Section header string table index
|
||||||
|
};
|
||||||
|
|
||||||
|
// Section header structure
|
||||||
|
struct Elf32_Shdr
|
||||||
|
{
|
||||||
|
u32 sh_name; // Section name (string tbl index)
|
||||||
|
u32 sh_type; // Section type
|
||||||
|
u32 sh_flags; // Section flags
|
||||||
|
u32 sh_addr; // Section virtual addr at execution
|
||||||
|
u32 sh_offset; // Section file offset
|
||||||
|
u32 sh_size; // Section size in bytes
|
||||||
|
u32 sh_link; // Link to another section
|
||||||
|
u32 sh_info; // Additional section information
|
||||||
|
u32 sh_addralign; // Section alignment
|
||||||
|
u32 sh_entsize; // Entry size if section holds table
|
||||||
|
};
|
||||||
|
|
||||||
|
// Program header structure
|
||||||
|
struct Elf32_Phdr
|
||||||
|
{
|
||||||
|
u32 p_type;
|
||||||
|
u32 p_offset;
|
||||||
|
u32 p_vaddr;
|
||||||
|
u32 p_paddr;
|
||||||
|
u32 p_filesz;
|
||||||
|
u32 p_memsz;
|
||||||
|
u32 p_flags;
|
||||||
|
u32 p_align;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ELFFile();
|
||||||
|
~ELFFile();
|
||||||
|
|
||||||
|
const Elf32_Ehdr& GetELFHeader() const;
|
||||||
|
u32 GetEntryPoint() const;
|
||||||
|
|
||||||
|
const Elf32_Shdr* GetSectionHeader(u32 index) const;
|
||||||
|
std::string_view GetSectionName(const Elf32_Shdr& section) const;
|
||||||
|
u32 GetSectionCount() const;
|
||||||
|
|
||||||
|
const Elf32_Phdr* GetProgramHeader(u32 index) const;
|
||||||
|
u32 GetProgramHeaderCount() const;
|
||||||
|
|
||||||
|
bool Open(const char* path, Error* error);
|
||||||
|
bool Open(DataArray data, Error* error);
|
||||||
|
|
||||||
|
using LoadExecutableSectionCallback =
|
||||||
|
std::function<bool(std::span<const u8> data, u32 dest_vaddr, u32 dest_size, Error* error)>;
|
||||||
|
bool LoadExecutableSections(const LoadExecutableSectionCallback& callback, Error* error) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataArray m_data;
|
||||||
|
};
|
@ -3,6 +3,7 @@
|
|||||||
<Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
|
<Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="compress_helpers.h" />
|
<ClInclude Include="compress_helpers.h" />
|
||||||
|
<ClInclude Include="elf_file.h" />
|
||||||
<ClInclude Include="image.h" />
|
<ClInclude Include="image.h" />
|
||||||
<ClInclude Include="imgui_animated.h" />
|
<ClInclude Include="imgui_animated.h" />
|
||||||
<ClInclude Include="audio_stream.h" />
|
<ClInclude Include="audio_stream.h" />
|
||||||
@ -131,6 +132,7 @@
|
|||||||
<ClCompile Include="d3d12_texture.cpp" />
|
<ClCompile Include="d3d12_texture.cpp" />
|
||||||
<ClCompile Include="d3d_common.cpp" />
|
<ClCompile Include="d3d_common.cpp" />
|
||||||
<ClCompile Include="dinput_source.cpp" />
|
<ClCompile Include="dinput_source.cpp" />
|
||||||
|
<ClCompile Include="elf_file.cpp" />
|
||||||
<ClCompile Include="gpu_device.cpp" />
|
<ClCompile Include="gpu_device.cpp" />
|
||||||
<ClCompile Include="gpu_shader_cache.cpp" />
|
<ClCompile Include="gpu_shader_cache.cpp" />
|
||||||
<ClCompile Include="gpu_texture.cpp" />
|
<ClCompile Include="gpu_texture.cpp" />
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
<ClInclude Include="sockets.h" />
|
<ClInclude Include="sockets.h" />
|
||||||
<ClInclude Include="media_capture.h" />
|
<ClInclude Include="media_capture.h" />
|
||||||
<ClInclude Include="compress_helpers.h" />
|
<ClInclude Include="compress_helpers.h" />
|
||||||
|
<ClInclude Include="elf_file.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="state_wrapper.cpp" />
|
<ClCompile Include="state_wrapper.cpp" />
|
||||||
@ -152,6 +153,7 @@
|
|||||||
<ClCompile Include="sockets.cpp" />
|
<ClCompile Include="sockets.cpp" />
|
||||||
<ClCompile Include="media_capture.cpp" />
|
<ClCompile Include="media_capture.cpp" />
|
||||||
<ClCompile Include="compress_helpers.cpp" />
|
<ClCompile Include="compress_helpers.cpp" />
|
||||||
|
<ClCompile Include="elf_file.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="metal_shaders.metal" />
|
<None Include="metal_shaders.metal" />
|
||||||
|
Loading…
Reference in New Issue
Block a user