Files
KytyPS5/src/loader/elf.cpp
T
2026-07-16 10:21:41 +02:00

647 lines
17 KiB
C++

#include "loader/elf.h"
#include "common/assert.h"
#include "common/file.h"
#include "common/logging/log.h"
#include <cstring>
#include <memory>
namespace Loader {
static std::unique_ptr<SelfHeader> LoadSelf(Common::File& f) {
if (f.Remaining() < sizeof(SelfHeader)) {
return nullptr;
}
auto self = std::make_unique<SelfHeader>();
f.Read(self.get(), sizeof(SelfHeader));
return self;
}
static std::unique_ptr<SelfSegment[]> LoadSelfSegments(Common::File& f, uint16_t num) {
auto segs = std::make_unique<SelfSegment[]>(num);
f.Read(segs.get(), sizeof(SelfSegment) * num);
return segs;
}
static std::unique_ptr<Elf64_Ehdr> LoadEhdr64(Common::File& f) {
if (f.Remaining() < sizeof(Elf64_Ehdr)) {
return nullptr;
}
auto ehdr = std::make_unique<Elf64_Ehdr>();
f.Read(ehdr.get(), sizeof(Elf64_Ehdr));
return ehdr;
}
static void SaveEhdr64(Common::File& f, const Elf64_Ehdr* ehdr) {
EXIT_IF(ehdr == nullptr);
uint32_t bytes_written = 0;
f.Write(ehdr, sizeof(Elf64_Ehdr), &bytes_written);
EXIT_IF(bytes_written == 0);
}
static std::unique_ptr<Elf64_Phdr[]> LoadPhdr64(Common::File& f, uint64_t offset, Elf64_Half num) {
auto phdr = std::make_unique<Elf64_Phdr[]>(num);
f.Seek(offset);
f.Read(phdr.get(), sizeof(Elf64_Phdr) * num);
return phdr;
}
static void SavePhdr64(Common::File& f, uint64_t offset, Elf64_Half num, const Elf64_Phdr* phdr) {
EXIT_IF(phdr == nullptr);
uint32_t bytes_written = 0;
f.Seek(offset);
f.Write(phdr, sizeof(Elf64_Phdr) * num, &bytes_written);
EXIT_IF(bytes_written == 0);
}
static std::unique_ptr<Elf64_Shdr[]> LoadShdr64(Common::File& f, uint64_t offset, Elf64_Half num) {
if (num == 0) {
return nullptr;
}
auto shdr = std::make_unique<Elf64_Shdr[]>(num);
f.Seek(offset);
f.Read(shdr.get(), sizeof(Elf64_Shdr) * num);
return shdr;
}
static void SaveShdr64(Common::File& f, uint64_t offset, Elf64_Half num, const Elf64_Shdr* shdr) {
if (num == 0) {
return;
}
EXIT_IF(shdr == nullptr);
uint32_t bytes_written = 0;
f.Seek(offset);
f.Write(shdr, sizeof(Elf64_Shdr) * num, &bytes_written);
EXIT_IF(bytes_written == 0);
}
static std::unique_ptr<uint8_t[]> LoadDynamic64(Elf64* f, uint64_t offset, uint64_t size) {
auto dynamic_data = std::make_unique<uint8_t[]>(size);
// f.Seek(offset);
// f.Read(dynamic_data, size);
f->LoadSegment(reinterpret_cast<uint64_t>(dynamic_data.get()), offset, size);
return dynamic_data;
}
static std::unique_ptr<char[]> LoadStrTable(Common::File& f, uint64_t offset, uint32_t size) {
if (size == 0) {
return nullptr;
}
auto str_table = std::make_unique<char[]>(size);
f.Seek(offset);
f.Read(str_table.get(), size);
return str_table;
}
static void DbgPrintEhdr64(Elf64_Ehdr* ehdr, Common::File& f) {
f.Printf("ehdr->e_ident = ");
for (auto i: ehdr->e_ident) {
f.Printf("%02x", i);
}
f.Printf("\n");
f.Printf("ehdr->e_type = 0x%04" PRIx16 "\n", ehdr->e_type);
f.Printf("ehdr->e_machine = 0x%04" PRIx16 "\n", ehdr->e_machine);
f.Printf("ehdr->e_version = 0x%08" PRIx32 "\n", ehdr->e_version);
f.Printf("ehdr->e_entry = 0x%016" PRIx64 "\n", ehdr->e_entry);
f.Printf("ehdr->e_phoff = 0x%016" PRIx64 "\n", ehdr->e_phoff);
f.Printf("ehdr->e_shoff = 0x%016" PRIx64 "\n", ehdr->e_shoff);
f.Printf("ehdr->e_flags = 0x%08" PRIx32 "\n", ehdr->e_flags);
f.Printf("ehdr->e_ehsize = 0x%04" PRIx16 "\n", ehdr->e_ehsize);
f.Printf("ehdr->e_phentsize = 0x%04" PRIx16 "\n", ehdr->e_phentsize);
f.Printf("ehdr->e_phnum = %" PRIu16 "\n", ehdr->e_phnum);
f.Printf("ehdr->e_shentsize = 0x%04" PRIx16 "\n", ehdr->e_shentsize);
f.Printf("ehdr->e_shnum = %" PRIu16 "\n", ehdr->e_shnum);
f.Printf("ehdr->e_shstrndx = %" PRIu16 "\n", ehdr->e_shstrndx);
}
static void DbgPrintPhdr64(Elf64_Phdr* phdr, Common::File& f) {
f.Printf("phdr->p_type = 0x%08" PRIx32 "\n", phdr->p_type);
f.Printf("phdr->p_flags = 0x%08" PRIx32 "\n", phdr->p_flags);
f.Printf("phdr->p_offset = 0x%016" PRIx64 "\n", phdr->p_offset);
f.Printf("phdr->p_vaddr = 0x%016" PRIx64 "\n", phdr->p_vaddr);
f.Printf("phdr->p_paddr = 0x%016" PRIx64 "\n", phdr->p_paddr);
f.Printf("phdr->p_filesz = 0x%016" PRIx64 "\n", phdr->p_filesz);
f.Printf("phdr->p_memsz = 0x%016" PRIx64 "\n", phdr->p_memsz);
f.Printf("phdr->p_align = 0x%016" PRIx64 "\n", phdr->p_align);
}
static void DbgPrintShdr64(Elf64_Shdr* shdr, Common::File& f) {
f.Printf("shdr->sh_name = %d\n", shdr->sh_name);
f.Printf("shdr->sh_type = 0x%08" PRIx32 "\n", shdr->sh_type);
f.Printf("shdr->sh_flags = 0x%016" PRIx64 "\n", shdr->sh_flags);
f.Printf("shdr->sh_addr = 0x%016" PRIx64 "\n", shdr->sh_addr);
f.Printf("shdr->sh_offset = 0x%016" PRIx64 "\n", shdr->sh_offset);
f.Printf("shdr->sh_size = 0x%016" PRIx64 "\n", shdr->sh_size);
f.Printf("shdr->sh_link = %" PRId32 "\n", shdr->sh_link);
f.Printf("shdr->sh_info = 0x%08" PRIx32 "\n", shdr->sh_info);
f.Printf("shdr->sh_addralign = 0x%016" PRIx64 "\n", shdr->sh_addralign);
f.Printf("shdr->sh_entsize = 0x%016" PRIx64 "\n", shdr->sh_entsize);
}
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define DBG_NAME(tag) \
case tag: name = #tag; break;
static void DbgPrintDynamic64(const Elf64_Dyn* dyn, Common::File& f) {
const char* name = "Unknown";
switch (dyn->d_tag) {
DBG_NAME(DT_OS_HASH)
DBG_NAME(DT_HASH)
DBG_NAME(DT_OS_STRTAB)
DBG_NAME(DT_OS_STRSZ)
DBG_NAME(DT_STRTAB)
DBG_NAME(DT_STRSZ)
DBG_NAME(DT_OS_SYMTAB)
DBG_NAME(DT_SYMTAB)
DBG_NAME(DT_OS_HASHSZ)
DBG_NAME(DT_OS_SYMTABSZ)
DBG_NAME(DT_INIT)
DBG_NAME(DT_FINI)
DBG_NAME(DT_OS_PLTGOT)
DBG_NAME(DT_PLTGOT)
DBG_NAME(DT_OS_JMPREL)
DBG_NAME(DT_JMPREL)
DBG_NAME(DT_OS_PLTRELSZ)
DBG_NAME(DT_PLTRELSZ)
DBG_NAME(DT_OS_PLTREL)
DBG_NAME(DT_PLTREL)
DBG_NAME(DT_OS_RELA)
DBG_NAME(DT_RELA)
DBG_NAME(DT_OS_RELASZ)
DBG_NAME(DT_RELASZ)
DBG_NAME(DT_OS_RELAENT)
DBG_NAME(DT_RELAENT)
DBG_NAME(DT_INIT_ARRAY)
DBG_NAME(DT_INIT_ARRAYSZ)
DBG_NAME(DT_FINI_ARRAY)
DBG_NAME(DT_FINI_ARRAYSZ)
DBG_NAME(DT_PREINIT_ARRAY)
DBG_NAME(DT_PREINIT_ARRAYSZ)
DBG_NAME(DT_OS_SYMENT)
DBG_NAME(DT_SYMENT)
DBG_NAME(DT_DEBUG)
DBG_NAME(DT_TEXTREL)
DBG_NAME(DT_FLAGS)
DBG_NAME(DT_NEEDED)
DBG_NAME(DT_OS_NEEDED_MODULE)
DBG_NAME(DT_OS_NEEDED_MODULE_1)
DBG_NAME(DT_OS_IMPORT_LIB)
DBG_NAME(DT_OS_IMPORT_LIB_1)
DBG_NAME(DT_OS_IMPORT_LIB_ATTR)
DBG_NAME(DT_OS_FINGERPRINT)
DBG_NAME(DT_OS_ORIGINAL_FILENAME)
DBG_NAME(DT_OS_ORIGINAL_FILENAME_1)
DBG_NAME(DT_OS_MODULE_INFO)
DBG_NAME(DT_OS_MODULE_INFO_1)
DBG_NAME(DT_OS_MODULE_ATTR)
DBG_NAME(DT_SONAME)
DBG_NAME(DT_OS_EXPORT_LIB)
DBG_NAME(DT_OS_EXPORT_LIB_1)
DBG_NAME(DT_OS_EXPORT_LIB_ATTR)
DBG_NAME(DT_RELACOUNT)
DBG_NAME(DT_NULL)
}
f.Printf("d_tag = 0x%016" PRIx64 ", d_val = 0x%016" PRIx64 ", name = %s\n", dyn->d_tag,
dyn->d_un.d_val, name);
}
Elf64::~Elf64() {
Clear();
}
void Elf64::LoadSegment(uint64_t vaddr, uint64_t file_offset, uint64_t size) {
EXIT_IF(m_f == nullptr);
if (m_self != nullptr) {
EXIT_IF(m_self_segments == nullptr);
EXIT_IF(m_phdr == nullptr);
for (uint16_t i = 0; i < m_self->segments_num; i++) {
const auto& seg = m_self_segments[i];
if ((seg.type & 0x800u) != 0) {
auto phdr_id = ((seg.type >> 20u) & 0xFFFu);
const auto& phdr = m_phdr[phdr_id];
if (file_offset >= phdr.p_offset && file_offset < phdr.p_offset + phdr.p_filesz) {
EXIT_NOT_IMPLEMENTED(seg.decompressed_size != phdr.p_filesz);
EXIT_NOT_IMPLEMENTED(seg.compressed_size != seg.decompressed_size);
auto offset = file_offset - phdr.p_offset;
EXIT_NOT_IMPLEMENTED(offset + size > seg.decompressed_size);
m_f->Seek(offset + seg.offset);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
return;
}
}
}
if (m_f->Size() - m_self->file_size == size) {
m_f->Seek(m_self->file_size);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
return;
}
EXIT("missing self segment\n");
} else {
m_f->Seek(file_offset);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
}
}
const Elf64_Dyn* Elf64::GetDynValue(Elf64_Sxword tag) const {
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == tag) {
return dyn;
}
}
return nullptr;
}
std::vector<const Elf64_Dyn*> Elf64::GetDynList(Elf64_Sxword tag) const {
std::vector<const Elf64_Dyn*> ret;
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == tag) {
ret.push_back(dyn);
}
}
return ret;
}
bool Elf64::IsShared() const {
return (m_ehdr->e_type == ET_DYNAMIC);
}
bool Elf64::IsNextGen() const {
return (m_ehdr->e_ident[EI_ABIVERSION] == 2);
}
const char* Elf64::GetSectionName(int index) const {
if (m_ehdr == nullptr || m_shdr == nullptr || m_str_table == nullptr || index < 0 ||
index >= m_ehdr->e_shnum) {
return nullptr;
}
const auto name_offset = m_shdr[index].sh_name;
if (name_offset >= m_str_table_size) {
return nullptr;
}
const auto remaining = m_str_table_size - name_offset;
if (std::memchr(m_str_table.get() + name_offset, '\0', remaining) == nullptr) {
return nullptr;
}
return m_str_table.get() + name_offset;
}
void Elf64::Clear() {
if (m_f != nullptr) {
m_f->Close();
}
m_f.reset();
m_self.reset();
m_ehdr.reset();
m_self_segments.reset();
m_phdr.reset();
m_shdr.reset();
m_str_table.reset();
m_str_table_size = 0;
m_dynamic.reset();
m_dynamic_data.reset();
}
void Elf64::DbgDump(const std::string& folder) {
auto folder_str = Common::FixDirectorySlash(folder);
Common::File::CreateDirectories(folder_str);
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++) {
if (m_phdr[i].p_filesz == 0u) {
continue;
}
char str[512];
int s = snprintf(str, 512, "phdr_%03d", i);
EXIT_NOT_IMPLEMENTED(s >= 512);
Common::File fout;
fout.Create(folder_str + str);
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_phdr[i].p_filesz));
// m_f->Seek(m_phdr[i].p_offset);
// m_f->Read(buf, static_cast<uint32_t>(m_phdr[i].p_filesz));
LoadSegment(reinterpret_cast<uint64_t>(buf.get()), m_phdr[i].p_offset, m_phdr[i].p_filesz);
fout.Write(buf.get(), static_cast<uint32_t>(m_phdr[i].p_filesz));
fout.Close();
}
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++) {
if (m_shdr[i].sh_size == 0u) {
continue;
}
char str[512];
int s = snprintf(str, 512, "shdr_%03d", i);
EXIT_NOT_IMPLEMENTED(s >= 512);
Common::File fout;
fout.Create(folder_str + str);
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_shdr[i].sh_size));
m_f->Seek(m_shdr[i].sh_offset);
m_f->Read(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size));
fout.Write(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size));
fout.Close();
}
Common::File fout;
fout.Create(folder_str + "ehdr.txt");
DbgPrintEhdr64(m_ehdr.get(), fout);
fout.Close();
fout.Create(folder_str + "phdr.txt");
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++) {
fout.Printf("--- phdr [%d] ---\n", i);
DbgPrintPhdr64(m_phdr.get() + i, fout);
}
fout.Close();
fout.Create(folder_str + "shdr.txt");
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++) {
const char* section_name = GetSectionName(i);
fout.Printf("--- shdr [%d] %s ---\n", i, section_name != nullptr ? section_name : "");
DbgPrintShdr64(m_shdr.get() + i, fout);
}
fout.Close();
fout.Create(folder_str + "dynamic.txt");
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++) {
DbgPrintDynamic64(dyn, fout);
}
fout.Close();
}
uint64_t Elf64::GetEntry() {
return m_ehdr->e_entry;
}
bool Elf64::IsSelf() const {
if (m_f == nullptr || m_f->IsInvalid()) {
return false;
}
if (m_self == nullptr) {
return false;
}
const bool known_magic = (m_self->ident[0] == 0x4f && m_self->ident[1] == 0x15 &&
m_self->ident[2] == 0x3d && m_self->ident[3] == 0x1d) ||
(m_self->ident[0] == 0x54 && m_self->ident[1] == 0x14 &&
m_self->ident[2] == 0xf5 && m_self->ident[3] == 0xee);
if (!known_magic) {
return false;
}
const bool known_ident_tail =
(m_self->ident[4] == 0x00 && m_self->ident[5] == 0x01 && m_self->ident[6] == 0x01 &&
m_self->ident[7] == 0x12 && m_self->ident[8] == 0x01 && m_self->ident[9] == 0x01 &&
m_self->ident[10] == 0x00 && m_self->ident[11] == 0x00 && m_self->unknown == 0x22) ||
(m_self->ident[4] == 0x10 && m_self->ident[5] == 0x01 && m_self->ident[6] == 0x01 &&
m_self->ident[7] == 0x12 && m_self->ident[8] == 0x01 && m_self->ident[9] == 0x01 &&
m_self->ident[10] == 0x00 && m_self->ident[11] == 0x10 && m_self->unknown == 0x32);
if (!known_ident_tail) {
LOGF("Unknown SELF file\n");
return false;
}
return true;
}
bool Elf64::IsValid() const {
if (m_f == nullptr || m_f->IsInvalid()) {
return false;
}
if (m_ehdr == nullptr) {
return false;
}
if (m_ehdr->e_ident[EI_MAG0] != '\x7f' || m_ehdr->e_ident[EI_MAG1] != 'E' ||
m_ehdr->e_ident[EI_MAG2] != 'L' || m_ehdr->e_ident[EI_MAG3] != 'F') {
LOGF("Not an ELF file\n");
return false;
}
if (m_ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
LOGF("ehdr->e_ident[EI_CLASS] (0x%x) != ELFCLASS64\n", m_ehdr->e_ident[EI_CLASS]);
return false;
}
if (m_ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
LOGF("ehdr->e_ident[EI_DATA] (0x%x) != ELFDATA2LSB\n", m_ehdr->e_ident[EI_DATA]);
return false;
}
if (m_ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
LOGF("ehdr->e_ident[EI_VERSION] != EV_CURRENT\n");
return false;
}
if (m_ehdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD) {
LOGF("ehdr->e_ident[EI_OSABI] (0x%x) != ELFOSABI_FREEBSD\n", m_ehdr->e_ident[EI_OSABI]);
return false;
}
if (m_ehdr->e_ident[EI_ABIVERSION] != 0 && m_ehdr->e_ident[EI_ABIVERSION] != 2) {
LOGF("ehdr->e_ident[EI_ABIVERSION] (0x%x) != (0 or 2)\n", m_ehdr->e_ident[EI_ABIVERSION]);
return false;
}
if (m_ehdr->e_type != ET_DYNEXEC && m_ehdr->e_type != ET_DYNAMIC) {
LOGF("ehdr->e_type (%04x) != ET_DYNEXEC && m_ehdr->e_type != ET_DYNAMIC\n", m_ehdr->e_type);
return false;
}
if (m_ehdr->e_machine != EM_X86_64) {
LOGF("ehdr->e_machine (%04x) != EM_X86_64\n", m_ehdr->e_machine);
return false;
}
if (m_ehdr->e_version != EV_CURRENT) {
LOGF("ehdr->e_version != EV_CURRENT\n");
return false;
}
if (m_ehdr->e_phentsize != sizeof(Elf64_Phdr)) {
LOGF("ehdr->e_phentsize != sizeof(Elf64_Phdr)\n");
return false;
}
if (m_ehdr->e_shentsize > 0 && m_ehdr->e_shentsize != sizeof(Elf64_Shdr)) {
LOGF("ehdr->e_shentsize (%d) != sizeof(Elf64_Shdr)\n", m_ehdr->e_shentsize);
return false;
}
return true;
}
void Elf64::Open(const std::filesystem::path& file_name) {
Clear();
m_f = std::make_unique<Common::File>();
m_f->Open(file_name, Common::File::Mode::Read);
if (m_f->IsInvalid()) {
EXIT("Can't open %s\n", Common::PathToString(file_name).c_str());
}
m_self = LoadSelf(*m_f);
if (!IsSelf()) {
m_self.reset();
m_f->Seek(0);
} else {
m_self_segments = LoadSelfSegments(*m_f, m_self->segments_num);
}
auto ehdr_pos = m_f->Tell();
m_ehdr = LoadEhdr64(*m_f);
if (!IsValid()) {
m_ehdr.reset();
}
if (m_ehdr != nullptr /*&& m_self == nullptr*/) {
m_phdr = LoadPhdr64(*m_f, ehdr_pos + m_ehdr->e_phoff, m_ehdr->e_phnum);
if (m_self == nullptr) {
m_shdr = LoadShdr64(*m_f, ehdr_pos + m_ehdr->e_shoff, m_ehdr->e_shnum);
if (m_shdr != nullptr) {
if (m_ehdr->e_shstrndx < m_ehdr->e_shnum) {
m_str_table_size = static_cast<uint32_t>(m_shdr[m_ehdr->e_shstrndx].sh_size);
m_str_table =
LoadStrTable(*m_f, m_shdr[m_ehdr->e_shstrndx].sh_offset, m_str_table_size);
}
}
} else if (m_ehdr->e_shnum != 0) {
LOGF("SELF: skipping ELF section table: shoff=0x%016" PRIx64 ", shnum=%" PRIu16 "\n",
m_ehdr->e_shoff, m_ehdr->e_shnum);
}
for (Elf64_Half i = 0; i < m_ehdr->e_phnum; i++) {
switch (m_phdr[i].p_type) {
case PT_DYNAMIC:
m_dynamic = LoadDynamic64(this, m_phdr[i].p_offset, m_phdr[i].p_filesz);
break;
case PT_OS_DYNLIBDATA:
m_dynamic_data = LoadDynamic64(this, m_phdr[i].p_offset, m_phdr[i].p_filesz);
break;
default: break;
}
}
}
}
void Elf64::Save(const std::filesystem::path& file_name) {
EXIT_IF(!IsValid());
if (IsValid()) {
Common::File f;
f.Create(file_name);
if (f.IsInvalid()) {
EXIT("Can't create %s\n", Common::PathToString(file_name).c_str());
}
SaveEhdr64(f, m_ehdr.get());
SavePhdr64(f, m_ehdr->e_phoff, m_ehdr->e_phnum, m_phdr.get());
SaveShdr64(f, m_ehdr->e_shoff, m_ehdr->e_shnum, m_shdr.get());
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++) {
if (m_phdr[i].p_filesz == 0u) {
continue;
}
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_phdr[i].p_filesz));
LoadSegment(reinterpret_cast<uint64_t>(buf.get()), m_phdr[i].p_offset,
m_phdr[i].p_filesz);
uint32_t bytes_written = 0;
f.Seek(m_phdr[i].p_offset);
f.Write(buf.get(), static_cast<uint32_t>(m_phdr[i].p_filesz), &bytes_written);
EXIT_IF(bytes_written == 0);
}
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++) {
if (m_shdr[i].sh_size == 0u) {
continue;
}
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_shdr[i].sh_size));
m_f->Seek(m_shdr[i].sh_offset);
m_f->Read(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size));
uint32_t bytes_written = 0;
f.Seek(m_shdr[i].sh_offset);
f.Write(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size), &bytes_written);
EXIT_IF(bytes_written == 0);
}
f.Close();
}
}
} // namespace Loader