1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
#include <shared/execbuf.h>
#include <shared/flags.h>
#include <shared/syscalls.h>
#include <user/lib/elf.h>
#include <user/lib/elfload.h>
void elf_execf(libc_file *f) {
const size_t cap = 0x60000;
size_t pos = 0;
void *buf = malloc(cap); // TODO a way to get file size
if (!buf) goto fail;
while (!f->eof) {
long ret = file_read(f, buf, cap - pos);
if (ret < 0) goto fail;
pos += ret;
if (pos >= cap) goto fail;
}
elf_exec(buf);
fail:
free(buf);
}
static bool valid_ehdr(const struct Elf64_Ehdr *h) {
return h->e_ident[0] == 0x7f
&& h->e_ident[1] == 'E'
&& h->e_ident[2] == 'L'
&& h->e_ident[3] == 'F'
&& h->e_machine == EM_X86_64
&& h->e_version == 1;
}
static bool load_phdr(const void *elf, void *exebase, size_t idx) {
const struct Elf64_Ehdr *ehdr = elf;
const struct Elf64_Phdr *phdr = elf + ehdr->e_phoff + idx * ehdr->e_phentsize;
if (phdr->p_type == PT_DYNAMIC) return true;
if (phdr->p_type != PT_LOAD) {
printf("unknown type %x\n", phdr->p_type);
return false;
}
// TODO overlap check
// TODO don't ignore flags
_syscall_memflag(exebase + phdr->p_vaddr, phdr->p_memsz, MEMFLAG_PRESENT);
// TODO check that filesz <= memsz
memcpy(exebase + phdr->p_vaddr, elf + phdr->p_offset, phdr->p_filesz);
return true;
}
static size_t elf_spread(const void *elf) {
const struct Elf64_Ehdr *ehdr = elf;
uintptr_t high = 0, low = ~0;
for (size_t phi = 0; phi < ehdr->e_phnum; phi++) {
const struct Elf64_Phdr *phdr = elf + ehdr->e_phoff + phi * ehdr->e_phentsize;
if (high < phdr->p_vaddr + phdr->p_memsz)
high = phdr->p_vaddr + phdr->p_memsz;
if (low > phdr->p_vaddr)
low = phdr->p_vaddr;
}
return high - low;
}
/* frees memory outside of [low; low + len] and jumps to *entry */
static void freejmp(void *entry, void *low, size_t len) {
uintptr_t high = (uintptr_t)low + len;
uint64_t buf[] = {
EXECBUF_SYSCALL, _SYSCALL_MEMFLAG, 0, (uintptr_t)low, 0, 0,
EXECBUF_SYSCALL, _SYSCALL_MEMFLAG, high, ~0 - 0xF000 - high, 0, 0,
EXECBUF_JMP, (uintptr_t)entry,
};
_syscall_execbuf(buf, sizeof buf);
// should never return
}
static void *elf_loadmem(struct Elf64_Ehdr *ehdr) {
void *exebase;
size_t spread = elf_spread(ehdr);
switch (ehdr->e_type) {
case ET_EXEC:
exebase = (void*)0;
break;
case ET_DYN:
exebase = _syscall_memflag((void*)0x1000, spread, MEMFLAG_FINDFREE);
if (!exebase)
return NULL;
break;
default:
return NULL;
}
for (size_t phi = 0; phi < ehdr->e_phnum; phi++) {
if (!load_phdr((void*)ehdr, exebase, phi)) {
_syscall_memflag(exebase, spread, 0);
return NULL;
}
}
return exebase;
}
void elf_exec(void *base) {
struct Elf64_Ehdr *ehdr = base;
if (!valid_ehdr(ehdr)) return;
void *exebase = elf_loadmem(ehdr);
if (!exebase) return;
freejmp(exebase + ehdr->e_entry, exebase, elf_spread(ehdr) + 0x1000);
}
void *elf_partialexec(void *base) {
struct Elf64_Ehdr *ehdr = base;
if (!valid_ehdr(ehdr)) return NULL;
void *exebase = elf_loadmem(ehdr);
if (!exebase) return NULL;
return exebase + ehdr->e_entry;
}
|