-
Notifications
You must be signed in to change notification settings - Fork 63
/
elf.c
336 lines (296 loc) · 12 KB
/
elf.c
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <autoconf.h>
#include <string.h>
#include <sel4/sel4.h>
#include <elf/elf.h>
#include <cpio/cpio.h>
#include <vka/capops.h>
#include <sel4utils/thread.h>
#include <sel4utils/util.h>
#include <sel4utils/mapping.h>
#include <sel4utils/elf.h>
/* This library works with our cpio set up in the build system */
extern char _cpio_archive[];
/*
* Convert ELF permissions into seL4 permissions.
*
* @param permissions elf permissions
* @return seL4 permissions
*/
static inline seL4_CapRights_t
rights_from_elf(unsigned long permissions)
{
bool canRead = permissions & PF_R || permissions & PF_X;
bool canWrite = permissions & PF_W;
return seL4_CapRights_new(false, canRead, canWrite);
}
static int
load_segment(vspace_t *loadee_vspace, vspace_t *loader_vspace,
vka_t *loadee_vka, vka_t *loader_vka,
char *src, size_t segment_size, size_t file_size, uint32_t dst,
reservation_t reservation)
{
int error = seL4_NoError;
if (file_size > segment_size) {
ZF_LOGE("Error, file_size %zu > segment_size %zu", file_size, segment_size);
return seL4_InvalidArgument;
}
/* create a slot to map a page into the loader address space */
seL4_CPtr loader_slot;
cspacepath_t loader_frame_cap;
error = vka_cspace_alloc(loader_vka, &loader_slot);
if (error) {
ZF_LOGE("Failed to allocate cslot by loader vka: %d", error);
return error;
}
vka_cspace_make_path(loader_vka, loader_slot, &loader_frame_cap);
/* We work a page at a time */
unsigned int pos = 0;
while (pos < segment_size && error == seL4_NoError) {
void *loader_vaddr = 0;
void *loadee_vaddr = (void *) ((seL4_Word)ROUND_DOWN(dst, PAGE_SIZE_4K));
/* create and map the frame in the loadee address space */
error = vspace_new_pages_at_vaddr(loadee_vspace, loadee_vaddr, 1, seL4_PageBits, reservation);
if (error != seL4_NoError) {
ZF_LOGE("ERROR: failed to allocate frame by loadee vka: %d", error);
continue;
}
/* copy the frame cap to map into the loader address space */
cspacepath_t loadee_frame_cap;
vka_cspace_make_path(loadee_vka, vspace_get_cap(loadee_vspace, loadee_vaddr),
&loadee_frame_cap);
error = vka_cnode_copy(&loader_frame_cap, &loadee_frame_cap, seL4_AllRights);
if (error != seL4_NoError) {
ZF_LOGE("ERROR: failed to copy frame cap into loader cspace: %d", error);
continue;
}
/* map the frame into the loader address space */
loader_vaddr = vspace_map_pages(loader_vspace, &loader_frame_cap.capPtr, NULL, seL4_AllRights,
1, seL4_PageBits, 1);
if (loader_vaddr == NULL) {
ZF_LOGE("failed to map frame into loader vspace.");
error = -1;
continue;
}
/* finally copy the data */
int nbytes = PAGE_SIZE_4K - (dst & PAGE_MASK_4K);
if (pos < file_size) {
memcpy(loader_vaddr + (dst % PAGE_SIZE_4K), (void*)src, MIN(nbytes, file_size - pos));
}
/* Note that we don't need to explicitly zero frames as seL4 gives us zero'd frames */
#ifdef CONFIG_ARCH_ARM
/* Flush the caches */
seL4_ARM_Page_Unify_Instruction(loader_frame_cap.capPtr, 0, PAGE_SIZE_4K);
seL4_ARM_Page_Unify_Instruction(loadee_frame_cap.capPtr, 0, PAGE_SIZE_4K);
#endif /* CONFIG_ARCH_ARM */
/* now unmap the page in the loader address space */
vspace_unmap_pages(loader_vspace, (void *) loader_vaddr, 1, seL4_PageBits, VSPACE_PRESERVE);
vka_cnode_delete(&loader_frame_cap);
pos += nbytes;
dst += nbytes;
src += nbytes;
}
/* clear the cslot */
vka_cspace_free(loader_vka, loader_frame_cap.capPtr);
return error;
}
int
sel4utils_elf_num_regions(const char *image_name)
{
unsigned long elf_size;
assert(image_name);
char *elf_file = cpio_get_file(_cpio_archive, image_name, &elf_size);
if (elf_file == NULL) {
ZF_LOGE("ERROR: failed to load elf file %s", image_name);
return 0;
}
int num_headers = elf_getNumProgramHeaders(elf_file);
int loadable_headers = 0;
for (int i = 0; i < num_headers; i++) {
/* Skip non-loadable segments (such as debugging data). */
if (elf_getProgramHeaderType(elf_file, i) == PT_LOAD) {
loadable_headers++;
}
}
return loadable_headers;
}
static int
make_region(vspace_t *loadee, unsigned long flags, unsigned long segment_size,
unsigned long vaddr, sel4utils_elf_region_t *region, int anywhere)
{
region->cacheable = 1;
region->rights = rights_from_elf(flags);
region->elf_vstart = (void*)PAGE_ALIGN_4K(vaddr);
region->size = PAGE_ALIGN_4K(vaddr + segment_size - 1) + PAGE_SIZE_4K - (uint32_t)((seL4_Word)region->elf_vstart);
if (loadee) {
if (anywhere) {
region->reservation = vspace_reserve_range(loadee, region->size, region->rights, 1, (void**)®ion->reservation_vstart);
} else {
region->reservation_vstart = region->elf_vstart;
region->reservation = vspace_reserve_range_at(loadee,
region->elf_vstart,
region->size,
region->rights,
1);
}
return !region->reservation.res;
}
return 0;
}
void *
sel4utils_elf_reserve(vspace_t *loadee, const char *image_name, sel4utils_elf_region_t *regions)
{
unsigned long elf_size;
char *elf_file = cpio_get_file(_cpio_archive, image_name, &elf_size);
if (elf_file == NULL) {
ZF_LOGE("ERROR: failed to load elf file %s", image_name);
return NULL;
}
int num_headers = elf_getNumProgramHeaders(elf_file);
int region = 0;
for (int i = 0; i < num_headers; i++) {
unsigned long flags, segment_size, vaddr;
/* Skip non-loadable segments (such as debugging data). */
if (elf_getProgramHeaderType(elf_file, i) == PT_LOAD) {
/* Fetch information about this segment. */
segment_size = elf_getProgramHeaderMemorySize(elf_file, i);
vaddr = elf_getProgramHeaderVaddr(elf_file, i);
flags = elf_getProgramHeaderFlags(elf_file, i);
if (make_region(loadee, flags, segment_size, vaddr, ®ions[region], 0)) {
for (region--; region >= 0; region--) {
vspace_free_reservation(loadee, regions[region].reservation);
regions[region].reservation.res = NULL;
}
ZF_LOGE("Failed to create reservation");
return NULL;
}
region++;
}
}
uint64_t entry_point = elf_getEntryPoint(elf_file);
if ((uint32_t) (entry_point >> 32) != 0) {
ZF_LOGE("ERROR: this code hasn't been tested for 64bit!");
return NULL;
}
assert(entry_point != 0);
return (void*)(seL4_Word)entry_point;
}
void *
sel4utils_elf_load_record_regions(vspace_t *loadee, vspace_t *loader, vka_t *loadee_vka, vka_t *loader_vka, const char *image_name, sel4utils_elf_region_t *regions, int mapanywhere)
{
unsigned long elf_size;
char *elf_file = cpio_get_file(_cpio_archive, image_name, &elf_size);
if (elf_file == NULL) {
ZF_LOGE("ERROR: failed to load elf file %s", image_name);
return NULL;
}
int num_headers = elf_getNumProgramHeaders(elf_file);
int error = seL4_NoError;
int region_count = 0;
uint64_t entry_point = elf_getEntryPoint(elf_file);
if ((uint32_t) (entry_point >> 32) != 0) {
ZF_LOGE("ERROR: this code hasn't been tested for 64bit!");
return NULL;
}
assert(entry_point != 0);
for (int i = 0; i < num_headers; i++) {
char *source_addr;
unsigned long flags, file_size, segment_size, vaddr;
/* Skip non-loadable segments (such as debugging data). */
if (elf_getProgramHeaderType(elf_file, i) == PT_LOAD) {
/* Fetch information about this segment. */
source_addr = elf_file + elf_getProgramHeaderOffset(elf_file, i);
file_size = elf_getProgramHeaderFileSize(elf_file, i);
segment_size = elf_getProgramHeaderMemorySize(elf_file, i);
vaddr = elf_getProgramHeaderVaddr(elf_file, i);
flags = elf_getProgramHeaderFlags(elf_file, i);
/* make reservation */
sel4utils_elf_region_t region;
error = make_region(loadee, flags, segment_size, vaddr, ®ion, mapanywhere);
if (error) {
ZF_LOGE("Failed to reserve region");
break;
}
unsigned long offset = vaddr - PAGE_ALIGN_4K(vaddr);
/* Copy it across to the vspace */
ZF_LOGI(" * Loading segment %08x-->%08x", (int)vaddr, (int)(vaddr + segment_size));
error = load_segment(loadee, loader, loadee_vka, loader_vka, source_addr,
segment_size, file_size, offset + (uint32_t)((seL4_Word)region.reservation_vstart), region.reservation);
if (error) {
ZF_LOGE("Failed to load segment");
break;
}
/* record the region if requested */
if (regions) {
regions[region_count] = region;
} else {
vspace_free_reservation(loadee, region.reservation);
}
/* increment the count of the number of loaded regions */
region_count++;
}
}
return error == seL4_NoError ? (void*)(seL4_Word)entry_point : NULL;
}
uintptr_t sel4utils_elf_get_vsyscall(const char *image_name)
{
unsigned long elf_size;
char *elf_file = cpio_get_file(_cpio_archive, image_name, &elf_size);
if (elf_file == NULL) {
ZF_LOGE("ERROR: failed to lookup elf file %s", image_name);
return 0;
}
/* See if we can find the __vsyscall section */
void *addr = elf_getSectionNamed(elf_file, "__vsyscall");
if (addr) {
/* Hope everything is good and just dereference it */
return *(uintptr_t*)addr;
} else {
return 0;
}
}
void *
sel4utils_elf_load(vspace_t *loadee, vspace_t *loader, vka_t *loadee_vka, vka_t *loader_vka, const char *image_name)
{
return sel4utils_elf_load_record_regions(loadee, loader, loadee_vka, loader_vka, image_name, NULL, 0);
}
uint32_t
sel4utils_elf_num_phdrs(const char *image_name)
{
unsigned long elf_size;
char *elf_file = cpio_get_file(_cpio_archive, image_name, &elf_size);
return elf_getNumProgramHeaders(elf_file);
}
void
sel4utils_elf_read_phdrs(const char *image_name, uint32_t max_phdrs, Elf_Phdr *phdrs)
{
unsigned long elf_size;
char *elf_file = cpio_get_file(_cpio_archive, image_name, &elf_size);
uint32_t num_phdrs = elf_getNumProgramHeaders(elf_file);
for (uint32_t i = 0; i < num_phdrs && i < max_phdrs; i++) {
/* cannot take addresses directly from the final struct as the types might
* be different, so store into locals first */
uint64_t p_vaddr, p_paddr, p_filesz, p_offset, p_memsz;
elf_getProgramHeaderInfo(elf_file, i, &p_vaddr, &p_paddr, &p_filesz, &p_offset, &p_memsz);
phdrs[i] = (Elf_Phdr) {
.p_type = elf_getProgramHeaderType(elf_file, i),
.p_offset = p_offset,
.p_vaddr = p_vaddr,
.p_paddr = p_paddr,
.p_filesz = p_filesz,
.p_memsz = p_memsz,
.p_flags = elf_getProgramHeaderFlags(elf_file,i),
.p_align = elf_getProgramHeaderAlign(elf_file, i)
};
}
}