Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/elf_file.cpp
4223 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "elf_file.h"
5
6
#include "common/error.h"
7
#include "common/file_system.h"
8
#include "common/log.h"
9
10
LOG_CHANNEL(FileLoader);
11
12
static constexpr const u8 EXPECTED_ELF_HEADER[4] = {'\177', 'E', 'L', 'F'};
13
static constexpr s64 MAX_ELF_FILE_SIZE = 32 * 1024 * 1024;
14
15
ELFFile::ELFFile() = default;
16
17
ELFFile::~ELFFile() = default;
18
19
const ELFFile::Elf32_Ehdr& ELFFile::GetELFHeader() const
20
{
21
return *reinterpret_cast<const Elf32_Ehdr*>(&m_data[0]);
22
}
23
24
u32 ELFFile::GetEntryPoint() const
25
{
26
return GetELFHeader().e_entry;
27
}
28
29
const ELFFile::Elf32_Shdr* ELFFile::GetSectionHeader(u32 index) const
30
{
31
const Elf32_Ehdr& hdr = GetELFHeader();
32
if (index == SHN_UNDEF || index >= hdr.e_shnum || hdr.e_shentsize < sizeof(Elf32_Shdr))
33
return nullptr;
34
35
const size_t offset = hdr.e_shoff + index * static_cast<size_t>(hdr.e_shentsize);
36
if ((offset + sizeof(Elf32_Shdr)) > m_data.size())
37
return nullptr;
38
39
return reinterpret_cast<const Elf32_Shdr*>(&m_data[offset]);
40
}
41
42
std::string_view ELFFile::GetSectionName(const Elf32_Shdr& section) const
43
{
44
const Elf32_Shdr* strhdr = GetSectionHeader(GetELFHeader().e_shstrndx);
45
if (!strhdr || section.sh_name >= strhdr->sh_size)
46
return std::string_view();
47
48
const size_t file_offset = strhdr->sh_offset;
49
const u32 start_offset = section.sh_name;
50
u32 current_offset = start_offset;
51
while (current_offset < strhdr->sh_size && (current_offset + file_offset) < m_data.size())
52
{
53
if (m_data[file_offset + current_offset] == '\0')
54
break;
55
56
current_offset++;
57
}
58
if (current_offset == start_offset)
59
return std::string_view();
60
61
return std::string_view(reinterpret_cast<const char*>(&m_data[file_offset + start_offset]),
62
current_offset - start_offset);
63
}
64
65
u32 ELFFile::GetSectionCount() const
66
{
67
return GetELFHeader().e_shnum;
68
}
69
70
const ELFFile::Elf32_Phdr* ELFFile::GetProgramHeader(u32 index) const
71
{
72
const Elf32_Ehdr& hdr = GetELFHeader();
73
if (index >= hdr.e_phnum || hdr.e_phentsize < sizeof(Elf32_Phdr))
74
return nullptr;
75
76
const size_t offset = hdr.e_phoff + index * static_cast<size_t>(hdr.e_phentsize);
77
if ((offset + sizeof(Elf32_Phdr)) > m_data.size())
78
return nullptr;
79
80
return reinterpret_cast<const Elf32_Phdr*>(&m_data[offset]);
81
}
82
83
u32 ELFFile::GetProgramHeaderCount() const
84
{
85
return GetELFHeader().e_phnum;
86
}
87
88
bool ELFFile::Open(const char* path, Error* error)
89
{
90
auto fp = FileSystem::OpenManagedCFile(path, "rb", error);
91
if (!fp)
92
return false;
93
94
const s64 size = FileSystem::FSize64(fp.get(), error);
95
if (size < 0)
96
return false;
97
if (size >= MAX_ELF_FILE_SIZE)
98
{
99
Error::SetStringView(error, "File is too large.");
100
return false;
101
}
102
103
DataArray data(static_cast<size_t>(size));
104
if (std::fread(data.data(), data.size(), 1, fp.get()) != 1)
105
{
106
Error::SetErrno(error, "fread() failed: ", errno);
107
return false;
108
}
109
110
return Open(std::move(data), error);
111
}
112
113
bool ELFFile::Open(DataArray data, Error* error)
114
{
115
m_data = std::move(data);
116
117
if (m_data.size() < sizeof(Elf32_Ehdr) ||
118
std::memcmp(m_data.data(), EXPECTED_ELF_HEADER, sizeof(EXPECTED_ELF_HEADER)) != 0)
119
{
120
Error::SetStringView(error, "Invalid header.");
121
return false;
122
}
123
124
const Elf32_Ehdr& hdr = GetELFHeader();
125
if (hdr.e_machine != EM_MIPS)
126
{
127
Error::SetStringFmt(error, "Unsupported machine type {}.", hdr.e_machine);
128
return false;
129
}
130
131
const u32 section_count = GetSectionCount();
132
const u32 proghdr_count = GetProgramHeaderCount();
133
134
DEV_LOG("ELF Sections={} ProgramHeaders={} Entry=0x{:08X}", section_count, proghdr_count, hdr.e_entry);
135
136
for (u32 i = 0; i < section_count; i++)
137
{
138
const Elf32_Shdr* shdr = GetSectionHeader(i);
139
if (!shdr)
140
continue;
141
142
DEV_LOG("Section {}: Name={} Size={}", i, GetSectionName(*shdr), shdr->sh_size);
143
}
144
145
for (u32 i = 0; i < proghdr_count; i++)
146
{
147
const Elf32_Phdr* phdr = GetProgramHeader(i);
148
if (!phdr || phdr->p_type != PT_LOAD)
149
continue;
150
151
DEV_LOG("Program Header {}: Load {} at 0x{:08X}", i, phdr->p_filesz, phdr->p_vaddr);
152
}
153
154
return true;
155
}
156
157
bool ELFFile::LoadExecutableSections(const LoadExecutableSectionCallback& callback, Error* error) const
158
{
159
const u32 entry = GetELFHeader().e_entry;
160
bool loaded_entry = false;
161
162
const u32 ph_count = GetProgramHeaderCount();
163
for (u32 i = 0; i < ph_count; i++)
164
{
165
const Elf32_Phdr* phdr = GetProgramHeader(i);
166
if (!phdr)
167
{
168
Error::SetStringFmt(error, "Failed to find program header {}", i);
169
return false;
170
}
171
172
if (phdr->p_type != PT_LOAD)
173
{
174
// ignore section
175
continue;
176
}
177
178
std::span<const u8> data;
179
if (phdr->p_filesz > 0)
180
{
181
if ((phdr->p_offset + static_cast<size_t>(phdr->p_filesz)) > m_data.size())
182
{
183
Error::SetStringFmt(error, "Program header {} is out of file range {} {} {}", i, phdr->p_offset, phdr->p_filesz,
184
m_data.size());
185
return false;
186
}
187
188
data = m_data.cspan(phdr->p_offset, phdr->p_filesz);
189
}
190
191
if (!callback(data, phdr->p_vaddr, std::max(phdr->p_memsz, phdr->p_filesz), error))
192
return false;
193
194
loaded_entry |= (entry >= phdr->p_vaddr && entry < (phdr->p_vaddr + phdr->p_memsz));
195
}
196
197
if (!loaded_entry)
198
{
199
Error::SetStringFmt(error, "Entry point 0x{:08X} not loaded.", entry);
200
return false;
201
}
202
203
return true;
204
}
205
206
bool ELFFile::IsValidElfHeader(const std::span<const u8> data, Error* error /*= nullptr*/)
207
{
208
if (data.size() < sizeof(Elf32_Ehdr))
209
{
210
Error::SetStringView(error, "Invalid header.");
211
return false;
212
}
213
214
return IsValidElfHeader(reinterpret_cast<const Elf32_Ehdr&>(*data.data()), error);
215
}
216
217
bool ELFFile::IsValidElfHeader(const Elf32_Ehdr& header, Error* error /* = nullptr */)
218
{
219
if (std::memcmp(header.e_ident, EXPECTED_ELF_HEADER, sizeof(EXPECTED_ELF_HEADER)) != 0)
220
{
221
Error::SetStringView(error, "Invalid header.");
222
return false;
223
}
224
225
if (header.e_machine != EM_MIPS)
226
{
227
Error::SetStringFmt(error, "Unsupported machine type {}.", header.e_machine);
228
return false;
229
}
230
231
// probably fine
232
return true;
233
}
234
235