MOS Source Code
Loading...
Searching...
No Matches
elf.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/tasks/elf.hpp"
4
6#include "mos/mm/mm.hpp"
7#include "mos/mm/mmap.hpp"
12#include "mos/tasks/thread.hpp"
13
14#include <elf.h>
15#include <mos/types.hpp>
16#include <mos/vector.hpp>
17#include <mos_stdlib.hpp>
18#include <mos_string.hpp>
19
20MOS_STATIC_ASSERT(sizeof(elf_header_t) == 0x40, "elf_header has wrong size");
21MOS_STATIC_ASSERT(sizeof(elf_program_hdr_t) == 0x38, "elf_program_header has wrong size");
22
23static bool elf_verify_header(const elf_header_t *header)
24{
25 if (header->identity.magic[0] != ELFMAG0)
26 return false;
27
28 if (strncmp(&header->identity.magic[1], "ELF", 3) != 0)
29 return false;
30
31 if (header->identity.bits != ELFCLASS64)
32 return false;
33
35 return false;
36
37 if (header->identity.osabi != 0)
38 return false;
39
40 if (header->identity.version != EV_CURRENT)
41 return false;
42
43 if (header->machine_type != MOS_ELF_PLATFORM)
44 return false;
45
46 return true;
47}
48
49[[nodiscard]] static bool elf_read_file(FsBaseFile *file, void *buf, off_t offset, size_t size)
50{
51 const size_t read = file->pread(buf, size, offset);
52 return read == size;
53}
54
56{
57 MOS_UNUSED(elf);
58 return 0x4000000; // TODO: randomize
59}
60
78
79static void elf_setup_main_thread(Thread *thread, elf_startup_info_t *const info, ptr_t *const out_pargv, ptr_t *const out_penvp)
80{
81 dInfo2<elf> << "cpu " << current_cpu->id << ": setting up a new main thread " << thread << " of process " << thread->owner;
82
83 MOS_ASSERT_X(thread->u_stack.head == thread->u_stack.top, "thread %pt's user stack is not empty", thread);
84 stack_push_val(&thread->u_stack, (uintn) 0);
85
86 const void *stack_envp[info->envp.size() + 1]; // +1 for the null terminator
87 const void *stack_argv[info->argv.size() + 1]; // +1 for the null terminator
88
89 // calculate the size of entire stack usage
90 size_t stack_size = 0;
91 stack_size += sizeof(uintn); // the topmost zero
92 stack_size += info->invocation.size() + 1; // +1 for the null terminator
93
94 for (const auto &env : info->envp)
95 stack_size += env.size() + 1; // +1 for the null terminator
96
97 for (const auto &arg : info->argv)
98 stack_size += arg.size() + 1; // +1 for the null terminator
99
100 stack_size += sizeof(Elf64_auxv_t) * (info->auxv.size() + 2); // AT_EXECFN and AT_NULL
101 stack_size += sizeof(stack_envp); // envp
102 stack_size += sizeof(stack_argv); // argv
103 stack_size += sizeof(uintn); // argc
104
105 // align to 16 bytes
106 const size_t aligned_stack_size = ALIGN_UP(stack_size, 16);
107 thread->u_stack.head = thread->u_stack.top - (aligned_stack_size - stack_size); // so that the stack can be aligned to 16 bytes
108
109 stack_push_val(&thread->u_stack, (uintn) 0);
110
111 void *invocation_ptr = stack_push(&thread->u_stack, info->invocation.data(), info->invocation.size() + 1); // +1 for the null terminator
112
113 info->AddAuxvEntry(AT_EXECFN, (ptr_t) invocation_ptr);
114 info->AddAuxvEntry(AT_NULL, 0);
115
116 // ! copy the environment to the stack in reverse order !
117 if (info->envp.empty())
118 goto no_envp;
119
120 for (int i = info->envp.size() - 1; i >= 0; i--)
121 {
122 const size_t len = info->envp[i].size() + 1; // +1 for the null terminator
123 stack_envp[i] = stack_push(&thread->u_stack, info->envp[i].c_str(), len);
124 }
125
126no_envp:
127 stack_envp[info->envp.size()] = NULL;
128
129 // ! copy the argv to the stack in reverse order !
130 if (info->argv.empty())
131 goto no_argv;
132
133 for (int i = info->argv.size() - 1; i >= 0; i--)
134 {
135 const size_t len = info->argv[i].size() + 1; // +1 for the null terminator
136 stack_argv[i] = stack_push(&thread->u_stack, info->argv[i].c_str(), len);
137 }
138
139no_argv:
140 stack_argv[info->argv.size()] = NULL;
141
142 stack_push(&thread->u_stack, info->auxv.data(), sizeof(Elf64_auxv_t) * info->auxv.size()); // auxv
143 *out_penvp = (ptr_t) stack_push(&thread->u_stack, &stack_envp, sizeof(char *) * (info->envp.size() + 1)); // envp
144 *out_pargv = (ptr_t) stack_push(&thread->u_stack, &stack_argv, sizeof(char *) * (info->argv.size() + 1)); // argv
145 stack_push_val(&thread->u_stack, (uintn) info->argv.size()); // argc
146 MOS_ASSERT(thread->u_stack.head % 16 == 0);
147}
148
149static void elf_map_segment(const elf_program_hdr_t *const ph, ptr_t map_bias, MMContext *mm, FsBaseFile *file)
150{
152 dInfo2<elf> << "program header " //
153 << (ph->flags() & ELF_PF_R ? 'r' : '-') //
154 << (ph->flags() & ELF_PF_W ? 'w' : '-') //
155 << (ph->flags() & ELF_PF_X ? 'x' : '-') //
156 << ", type '" << ph->header_type << "' at " << ph->vaddr;
157
158 MOS_ASSERT(ph->data_offset % MOS_PAGE_SIZE == ph->vaddr % MOS_PAGE_SIZE); // offset ≡ vaddr (mod page size)
159 MOS_ASSERT_X(ph->size_in_file <= ph->size_in_mem, "invalid ELF: size in file is larger than size in memory");
160
161 const VMFlags flags = [pflags = ph->flags()]()
162 {
163 VMFlags f = VM_USER;
164 if (pflags & ELF_PF_R)
165 f |= VM_READ;
166 if (pflags & ELF_PF_W)
167 f |= VM_WRITE;
168 if (pflags & ELF_PF_X)
169 f |= VM_EXEC;
170 return f;
171 }();
172
173 const ptr_t aligned_vaddr = ALIGN_DOWN_TO_PAGE(ph->vaddr);
174 const size_t npages = (ALIGN_UP_TO_PAGE(ph->vaddr + ph->size_in_mem) - aligned_vaddr) / MOS_PAGE_SIZE;
175 const size_t aligned_size = ALIGN_DOWN_TO_PAGE(ph->data_offset);
176
177 const ptr_t map_start = map_bias + aligned_vaddr;
178 dInfo2<elf> << " mapping " << npages << " pages at " << map_start << " (bias at " << map_bias << ") from offset " << aligned_size << "...";
179
180 const ptr_t vaddr = mmap_file(mm, map_start, MMAP_PRIVATE | MMAP_EXACT, flags, npages, file, aligned_size);
181 MOS_ASSERT_X(vaddr == map_start, "failed to map ELF segment at " PTR_FMT, aligned_vaddr);
182
183 if (ph->size_in_file < ph->size_in_mem)
184 {
185 dInfo2<elf> << " ... and zeroing " << (ph->size_in_mem - ph->size_in_file) << " bytes at " << (map_bias + ph->vaddr + ph->size_in_file);
186 memzero((char *) map_bias + ph->vaddr + ph->size_in_file, ph->size_in_mem - ph->size_in_file);
187 }
188
189 dInfo2<elf> << " ... done";
190}
191
192static ptr_t elf_map_interpreter(const char *path, MMContext *mm)
193{
194 auto interp_file = vfs_openat(AT_FDCWD, path, OPEN_READ | OPEN_EXECUTE);
195 if (interp_file.isErr())
196 return 0;
197
198 interp_file->ref();
199
200 elf_header_t elf;
201 if (!elf_read_and_verify_executable(interp_file.get(), &elf))
202 {
203 mEmerg << "failed to verify ELF header for '" << dentry_name(interp_file->dentry) << "'";
204 interp_file->unref();
205 return 0;
206 }
207
208 ptr_t entry = 0;
209
210 for (size_t i = 0; i < elf.ph.count; i++)
211 {
213 if (!elf_read_file(interp_file.get(), &ph, elf.ph_offset + i * elf.ph.entry_size, elf.ph.entry_size))
214 {
215 mEmerg << "failed to read program header " << i << " for '" << dentry_name(interp_file->dentry) << "'";
216 interp_file->unref();
217 return 0;
218 }
219
220 if (ph.header_type == ELF_PT_LOAD)
221 {
222 // interpreter is always loaded at vaddr 0
223 elf_map_segment(&ph, MOS_ELF_INTERPRETER_BASE_OFFSET, mm, interp_file.get());
224 entry = elf.entry_point;
225 }
226 }
227
228 interp_file->unref();
229 return MOS_ELF_INTERPRETER_BASE_OFFSET + entry;
230}
231
233{
234 bool ret = true;
235
236 info->AddAuxvEntry(AT_PAGESZ, MOS_PAGE_SIZE);
237 info->AddAuxvEntry(AT_UID, 0);
238 info->AddAuxvEntry(AT_EUID, 0);
239 info->AddAuxvEntry(AT_GID, 0);
240 info->AddAuxvEntry(AT_EGID, 0);
242
243 // !! after this point, we must make sure that we switch back to the previous address space before returning from this function !!
244 MMContext *const prev_mm = mm_switch_context(proc->mm);
245
246 bool should_bias = header.object_type == ET_DYN; // only ET_DYN (shared libraries) needs randomization
247 ptrdiff_t map_bias = 0; // ELF segments are loaded at vaddr + load_bias
248
249 bool has_interpreter = false;
250 ptr_t interp_entrypoint = 0;
251 ptr_t auxv_phdr_vaddr = false; // whether we need to add AT_PHDR, AT_PHENT, AT_PHNUM to the auxv vector
252
253 for (size_t i = 0; i < header.ph.count; i++)
254 {
256 if (!elf_read_file(file, &ph, header.ph_offset + i * header.ph.entry_size, header.ph.entry_size))
257 {
258 mEmerg << "failed to read program header " << i << " for '" << dentry_name(file->dentry) << "'";
259 const auto prev = mm_switch_context(prev_mm);
260 (void) prev;
261 return false;
262 }
263
264 switch (ph.header_type)
265 {
266 case ELF_PT_NULL: break; // ignore
267 case ELF_PT_INTERP:
268 {
269 char interp_name[ph.size_in_file];
270 if (!elf_read_file(file, interp_name, ph.data_offset, ph.size_in_file))
271 {
272 mEmerg << "failed to read interpreter name for '" << dentry_name(file->dentry) << "'";
273 const auto prev = mm_switch_context(prev_mm);
274 (void) prev;
275 return false;
276 }
277 dInfo2<elf> << "elf interpreter: " << interp_name;
278 has_interpreter = true;
279 interp_entrypoint = elf_map_interpreter(interp_name, proc->mm);
280 if (!interp_entrypoint)
281 {
282 dInfo2<elf> << "failed to map interpreter '" << interp_name << "'";
283 const auto prev = mm_switch_context(prev_mm);
284 (void) prev;
285 return false;
286 }
287
288 if (should_bias)
289 map_bias = elf_determine_loadbias(&header);
290
291 break;
292 }
293 case ELF_PT_LOAD:
294 {
295 elf_map_segment(&ph, map_bias, proc->mm, file);
296 break;
297 }
298 case ELF_PT_PHDR:
299 {
300 auxv_phdr_vaddr = ph.vaddr;
301 break;
302 }
303
304 case ELF_PT_NOTE: break; // intentionally ignored
305 case ELF_PT_DYNAMIC: break; // will be handled by the dynamic linker
306 case ELF_PT_TLS: break; // will be handled by the dynamic linker or libc
307 default:
308 {
310 dInfo2<elf> << "ignoring OS-specific program header type 0x" << ph.header_type;
312 dInfo2<elf> << "ignoring processor-specific program header type 0x" << ph.header_type;
313 else
314 mWarn << "unknown program header type 0x" << ph.header_type;
315 break;
316 }
317 };
318 }
319
320 if (auxv_phdr_vaddr)
321 {
322 info->AddAuxvEntry(AT_PHDR, map_bias + auxv_phdr_vaddr);
323 info->AddAuxvEntry(AT_PHENT, header.ph.entry_size);
324 info->AddAuxvEntry(AT_PHNUM, header.ph.count);
325 }
326
327 info->AddAuxvEntry(AT_ENTRY, map_bias + header.entry_point); // the entry point of the executable, not the interpreter
328
329 ptr_t user_argv, user_envp;
330 const auto main_thread = proc->main_thread;
331 elf_setup_main_thread(main_thread, info, &user_argv, &user_envp);
333 main_thread, //
334 has_interpreter ? interp_entrypoint : header.entry_point, //
335 main_thread->u_stack.head, //
336 info->argv.size(), //
337 user_argv, //
338 user_envp //
339 );
340
341 MMContext *prev = mm_switch_context(prev_mm);
342 MOS_UNUSED(prev);
343
344 return ret;
345}
346
348{
349 if (!elf_read_file(file, header, 0, sizeof(elf_header_t)))
350 return false;
351
352 const bool valid = elf_verify_header(header);
353 if (!valid)
354 return false;
355
356 if (header->object_type != ET_EXEC && header->object_type != ET_DYN)
357 return false;
358
359 return true;
360}
361
362[[nodiscard]] static bool elf_fill_process(Process *proc, FsBaseFile *file, mos::string_view path, const mos::vector<mos::string> &argv,
363 const mos::vector<mos::string> &envp)
364{
365 bool ret = false;
366
367 file->ref();
368
369 elf_header_t elf;
370 if (!elf_read_and_verify_executable(file, &elf))
371 {
372 mEmerg << "failed to verify ELF header for '" << dentry_name(file->dentry) << "'";
373 file->unref(); // close the file, we should have the file's refcount == 0 here
374 return ret;
375 }
376
377 elf_startup_info_t info{ .invocation = path, .argv = argv, .envp = envp };
378 ret = elf_do_fill_process(proc, file, elf, &info);
379
380 file->unref(); // close the file, we should have the file's refcount == 0 here
381 return ret;
382}
383
385{
386 auto file = vfs_openat(AT_FDCWD, path, OPEN_READ | OPEN_EXECUTE);
387 if (file.isErr())
388 {
389 mos_warn("failed to open '%s'", path.data());
390 return NULL;
391 }
392 file->ref();
393
394 auto proc = process_new(parent, file->dentry->name, ios);
395 if (!proc)
396 {
397 mos_warn("failed to create process for '%s'", dentry_name(file->dentry).c_str());
398 file->unref();
399 return proc;
400 }
401
402 const bool filled = elf_fill_process(proc, file.get(), path, argv, envp);
403 thread_complete_init(proc->main_thread);
404 scheduler_add_thread(proc->main_thread);
405
406 if (!filled)
407 {
408 // TODO how do we make sure that the process is cleaned up properly?
409 process_exit(std::move(proc), 0, SIGKILL);
410 proc = NULL;
411 }
412
413 file->unref(); // close the file, we should have the file's refcount == 0 here
414 return proc;
415}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.hpp:12
#define MOS_ASSERT(cond)
Definition assert.hpp:19
#define mos_warn(fmt,...)
Definition assert.hpp:22
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#define MOS_ELF_INTERPRETER_BASE_OFFSET
Definition autoconf.h:21
const CharT * data() const
const Char * c_str() const
Definition string.hpp:259
auto size() const noexcept
Definition vector.hpp:98
auto data(this auto &Self) noexcept
Definition vector.hpp:90
Process * elf_create_process(mos::string_view path, Process *parent, const mos::vector< mos::string > &argv, const mos::vector< mos::string > &envp, const stdio_t *ios)
Definition elf.cpp:384
static ptr_t elf_determine_loadbias(elf_header_t *elf)
Definition elf.cpp:55
__nodiscard bool elf_do_fill_process(Process *proc, FsBaseFile *file, elf_header_t header, elf_startup_info_t *info)
Definition elf.cpp:232
static ptr_t elf_map_interpreter(const char *path, MMContext *mm)
Definition elf.cpp:192
static bool elf_verify_header(const elf_header_t *header)
Definition elf.cpp:23
static void elf_map_segment(const elf_program_hdr_t *const ph, ptr_t map_bias, MMContext *mm, FsBaseFile *file)
Definition elf.cpp:149
static void elf_setup_main_thread(Thread *thread, elf_startup_info_t *const info, ptr_t *const out_pargv, ptr_t *const out_penvp)
Definition elf.cpp:79
static bool elf_read_file(FsBaseFile *file, void *buf, off_t offset, size_t size)
Definition elf.cpp:49
static bool elf_fill_process(Process *proc, FsBaseFile *file, mos::string_view path, const mos::vector< mos::string > &argv, const mos::vector< mos::string > &envp)
Definition elf.cpp:362
bool elf_read_and_verify_executable(FsBaseFile *file, elf_header_t *header)
Definition elf.cpp:347
@ ELF_PT_PROCESSOR_LO
Definition elf.hpp:76
@ ELF_PT_PHDR
Definition elf.hpp:69
@ ELF_PT_DYNAMIC
Definition elf.hpp:65
@ ELF_PT_LOAD
Definition elf.hpp:64
@ ELF_PT_OS_HIGH
Definition elf.hpp:75
@ ELF_PT_OS_LOW
Definition elf.hpp:74
@ ELF_PT_PROCESSOR_HI
Definition elf.hpp:77
@ ELF_PT_NOTE
Definition elf.hpp:67
@ ELF_PT_INTERP
Definition elf.hpp:66
@ ELF_PT_NULL
Definition elf.hpp:63
@ ELF_PT_TLS
Definition elf.hpp:70
#define ELF_ENDIANNESS_MOS_DEFAULT
Definition elf.hpp:22
@ ELF_PF_X
Definition elf.hpp:82
@ ELF_PF_W
Definition elf.hpp:83
@ ELF_PF_R
Definition elf.hpp:84
@ OPEN_READ
Definition fs_types.h:28
@ OPEN_EXECUTE
Definition fs_types.h:30
#define stack_push_val(stack, val)
Definition stack.hpp:26
MOSAPI void * stack_push(downwards_stack_t *stack, const void *data, size_t size)
Definition stack.cpp:32
MOSAPI s32 strncmp(const char *str1, const char *str2, size_t n)
MOSAPI void(1, 2) fatal_abort(const char *fmt
__nodiscard MMContext * mm_switch_context(MMContext *new_ctx)
Definition mm.cpp:124
PtrResult< FsBaseFile > vfs_openat(int fd, mos::string_view path, OpenFlags flags)
Open a file at a given path.
Definition vfs.cpp:585
@ MMAP_PRIVATE
Definition mm_types.h:17
@ MMAP_EXACT
Definition mm_types.h:16
@ VM_READ
Definition mm_types.hpp:13
@ VM_EXEC
Definition mm_types.hpp:15
@ VM_WRITE
Definition mm_types.hpp:14
@ VM_USER
Definition mm_types.hpp:17
ptr_t mmap_file(MMContext *ctx, ptr_t hint_addr, MMapFlags flags, VMFlags VMFlags, size_t n_pages, IO *io, off_t offset)
Map a file into the current process's address space.
Definition mmap.cpp:71
#define MOS_IN_RANGE(addr, start, end)
Definition mos_global.h:79
#define __nodiscard
Definition mos_global.h:35
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:76
#define ALIGN_DOWN_TO_PAGE(addr)
Definition mos_global.h:77
#define MOS_UNUSED(x)
Definition mos_global.h:65
#define MOS_STATIC_ASSERT
Definition mos_global.h:14
#define ALIGN_UP(addr, size)
Definition mos_global.h:74
basic_string_view< char > string_view
#define NULL
Definition pb_syshdr.h:46
#define current_cpu
Definition platform.hpp:32
void process_exit(Process *&&proc, u8 exit_code, signal_t signal)
Definition process.cpp:289
Process * process_new(Process *parelagsnt, mos::string_view name, const stdio_t *ios)
Definition process.cpp:141
#define MOS_ELF_PLATFORM
void platform_context_setup_main_thread(Thread *thread, ptr_t entry, ptr_t sp, int argc, ptr_t argv, ptr_t envp)
#define memzero(ptr, size)
void scheduler_add_thread(Thread *thread)
Add a thread to the scheduler, so that it can be scheduled.
Definition schedule.cpp:77
size_t size
Definition slab.cpp:32
dentry_t *const dentry
virtual size_t pread(void *buf, size_t count, off_t offset) final
Definition io.cpp:69
IO * ref()
Definition io.hpp:57
IO * unref()
Definition io.hpp:69
Thread * main_thread
mos::string name
MMContext * mm
Process * owner
downwards_stack_t u_stack
user-mode stack
elf_identity_t identity
Definition elf.hpp:40
int machine_type
Definition elf.hpp:42
int object_type
Definition elf.hpp:41
u16 count
Definition elf.hpp:55
size_t ph_offset
Definition elf.hpp:47
struct elf_header_t::@372374123200070342027241342357315321025311300122 ph
ptr_t entry_point
Definition elf.hpp:46
u16 entry_size
Definition elf.hpp:55
char magic[4]
Definition elf.hpp:27
u32 version
Definition elf.hpp:30
u32 endianness
Definition elf.hpp:29
elf_program_header_type header_type
Definition elf.hpp:89
ptr_t size_in_mem
Definition elf.hpp:95
Flags< elf_ph_flags > flags() const
Definition elf.hpp:98
ptr_t size_in_file
Definition elf.hpp:94
ptr_t data_offset
Definition elf.hpp:91
mos::vector< mos::string > envp
Definition elf.hpp:109
mos::string_view invocation
Definition elf.hpp:106
mos::vector< mos::string > argv
Definition elf.hpp:108
mos::vector< Elf64_auxv_t > auxv
Definition elf.hpp:107
void AddAuxvEntry(u64 type, u64 val)
Definition elf.hpp:111
A wrapper type for the standard I/O streams.
Definition process.hpp:17
constexpr auto dInfo2
Definition syslog.hpp:151
constexpr auto mWarn
Definition syslog.hpp:154
#define f(_fmt)
Definition syslog.hpp:160
constexpr auto mEmerg
Definition syslog.hpp:155
Thread * thread_complete_init(Thread *thread)
Definition thread.cpp:165
ssize_t off_t
Definition types.h:80
unsigned long uintn
Definition types.h:26
#define PTR_FMT
Definition types.h:29
unsigned long ptr_t
Definition types.h:21
mos::string dentry_name(const dentry_t *dentry)