-
Notifications
You must be signed in to change notification settings - Fork 0
/
loadelf.c
136 lines (106 loc) · 4.21 KB
/
loadelf.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
#include "loadelf.h"
#include "bootinfo.h"
#include "compiler.h"
#include "elf.h"
#include "string.h"
#include "uniboot.h"
#include "xefi.h"
static void elf_check_header(Elf64_Ehdr *hdr) {
if (strncmp((char *)hdr->e_ident, ELFMAG, SELFMAG)) {
xefi_fatal("app.elf: invalid ELF header magic value", EFI_LOAD_ERROR);
}
if (hdr->e_ident[EI_CLASS] != ELFCLASS64) {
xefi_fatal("app.elf: only 64-bit ELF booting is supported", EFI_LOAD_ERROR);
}
if (hdr->e_type != ET_EXEC) {
xefi_fatal("app.elf: only ELF executable file booting is supported", EFI_LOAD_ERROR);
}
}
static void elf_load_segments(void *data) {
Elf64_Ehdr *hdr = data;
void *phdr_p = data + hdr->e_phoff;
efi_physical_addr previous_end = 0; // we use it to track adjusted PT_LOAD segments that share the same page
for (int i = 0; i < hdr->e_phnum; i++) {
Elf64_Phdr *phdr = phdr_p;
if (phdr->p_type != PT_LOAD)
continue;
efi_physical_addr addr = phdr->p_paddr;
efi_physical_addr start = ROUND_DOWN(addr, PAGE_SIZE);
efi_physical_addr end = ROUND_UP(addr + phdr->p_memsz, PAGE_SIZE);
if (previous_end > start)
start = previous_end;
previous_end = end;
size_t pages = (end - start) / PAGE_SIZE;
if (pages) {
efi_status r = gBS->AllocatePages(AllocateAddress, EfiLoaderData, pages, &start);
if (r) {
xefi_fatal("load_elf: Cannot allocate buffer", r);
}
}
// copy ELF segment data
memcpy((void *)addr, data + phdr->p_offset, phdr->p_filesz);
// the end of segment is zero-outed area (e.g. BSS)
memset((void *)addr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
phdr_p += hdr->e_phentsize;
}
}
static void uniboot_populate_segments(void *data) {
Elf64_Ehdr *hdr = data;
size_t segment_list_size = sizeof(struct uniboot_segment_list) + hdr->e_phnum * sizeof(struct uniboot_segment);
struct uniboot_entry *entry = bootinfo_alloc(struct uniboot_entry);
entry->type = UNIBOOT_ENTRY_SEGMENT_LIST;
entry->length = segment_list_size;
struct uniboot_segment_list *segs = bootinfo_alloc_size(segment_list_size);
segs->num = hdr->e_phnum;
void *phdr_p = data + hdr->e_phoff;
for (int i = 0; i < hdr->e_phnum; i++) {
Elf64_Phdr *phdr = phdr_p;
segs->segments[i].type = phdr->p_type;
segs->segments[i].flags = phdr->p_flags;
segs->segments[i].offset = phdr->p_offset;
segs->segments[i].vaddr = phdr->p_vaddr;
segs->segments[i].paddr = phdr->p_paddr;
segs->segments[i].filesz = phdr->p_filesz;
segs->segments[i].memsz = phdr->p_memsz;
segs->segments[i].align = phdr->p_align;
phdr_p += hdr->e_phentsize;
}
}
static void uniboot_populate_sections(void *data) {
Elf64_Ehdr *hdr = data;
size_t section_list_size = sizeof(struct uniboot_section_list) + hdr->e_shnum * sizeof(struct uniboot_section);
struct uniboot_entry *entry = bootinfo_alloc(struct uniboot_entry);
entry->type = UNIBOOT_ENTRY_SECTION_LIST;
entry->length = section_list_size;
struct uniboot_section_list *list = bootinfo_alloc_size(section_list_size);
list->num = hdr->e_shnum;
struct uniboot_section *sec = list->sections;
void *shdr_p = data + hdr->e_shoff;
for (int i = 0; i < hdr->e_shnum; i++) {
Elf64_Shdr *shdr = shdr_p;
sec[i].name = shdr->sh_name;
sec[i].type = shdr->sh_type;
sec[i].flags = shdr->sh_flags;
sec[i].addr = shdr->sh_addr;
sec[i].size = shdr->sh_size;
sec[i].addralign = shdr->sh_addralign;
sec[i].entsize = shdr->sh_entsize;
shdr_p += hdr->e_shentsize;
}
}
void *elf_load(void *data, size_t size) {
if (!data) {
xefi_fatal("xefi_load_file", EFI_LOAD_ERROR);
}
if (size < sizeof(Elf64_Ehdr)) {
xefi_fatal("app.elf size is too small", EFI_LOAD_ERROR);
}
Elf64_Ehdr *hdr = (Elf64_Ehdr *)data;
elf_check_header(hdr);
void *entry = (void *)hdr->e_entry;
elf_load_segments(data);
uniboot_populate_segments(data);
uniboot_populate_sections(data);
xefi_free(data, size);
return entry;
}