1 /*
2  * Copyright (c) 2015 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <lib/elf.h>
9 #include <assert.h>
10 #include <lk/debug.h>
11 #include <endian.h>
12 #include <lk/err.h>
13 #include <lk/trace.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <arch/ops.h>
17 
18 #define LOCAL_TRACE 0
19 
20 /* conditionally define a 32 or 64 bit version of the data structures
21  * we care about, based on our bitness.
22  */
23 #if WITH_ELF32
24 typedef struct Elf32_Ehdr elf_ehdr_t;
25 typedef struct Elf32_Phdr elf_phdr_t;
26 
27 #define ELF_OFF_PRINT_U "%u"
28 #define ELF_OFF_PRINT_X "%x"
29 #define ELF_ADDR_PRINT_U "%u"
30 #define ELF_ADDR_PRINT_X "%x"
31 #else
32 typedef struct Elf64_Ehdr elf_ehdr_t;
33 typedef struct Elf64_Phdr elf_phdr_t;
34 
35 #define ELF_OFF_PRINT_U "%llu"
36 #define ELF_OFF_PRINT_X "%llx"
37 #define ELF_ADDR_PRINT_U "%llu"
38 #define ELF_ADDR_PRINT_X "%llx"
39 #endif
40 
41 struct read_hook_memory_args {
42     const uint8_t *ptr;
43     size_t len;
44 };
45 
elf_read_hook_memory(struct elf_handle * handle,void * buf,uint64_t offset,size_t len)46 static ssize_t elf_read_hook_memory(struct elf_handle *handle, void *buf, uint64_t offset, size_t len) {
47     LTRACEF("handle %p, buf %p, offset %lld, len %zu\n", handle, buf, offset, len);
48 
49     struct read_hook_memory_args *args = handle->read_hook_arg;
50 
51     DEBUG_ASSERT(args);
52     DEBUG_ASSERT(buf);
53     DEBUG_ASSERT(handle);
54     DEBUG_ASSERT(handle->open);
55 
56     ssize_t toread = len;
57     if (offset >= args->len)
58         toread = 0;
59     if (offset + len >= args->len)
60         toread = args->len - offset;
61 
62     memcpy(buf, args->ptr + offset, toread);
63 
64     LTRACEF("returning %ld\n", toread);
65 
66     return toread;
67 }
68 
elf_open_handle(elf_handle_t * handle,elf_read_hook_t read_hook,void * read_hook_arg,bool free_read_hook_arg)69 status_t elf_open_handle(elf_handle_t *handle, elf_read_hook_t read_hook, void *read_hook_arg, bool free_read_hook_arg) {
70     if (!handle)
71         return ERR_INVALID_ARGS;
72     if (!read_hook)
73         return ERR_INVALID_ARGS;
74 
75     memset(handle, 0, sizeof(*handle));
76 
77     handle->read_hook = read_hook;
78     handle->read_hook_arg = read_hook_arg;
79     handle->free_read_hook_arg = free_read_hook_arg;
80 
81     handle->open = true;
82 
83     return NO_ERROR;
84 }
85 
elf_open_handle_memory(elf_handle_t * handle,const void * ptr,size_t len)86 status_t elf_open_handle_memory(elf_handle_t *handle, const void *ptr, size_t len) {
87     struct read_hook_memory_args *args = malloc(sizeof(struct read_hook_memory_args));
88 
89     args->ptr = ptr;
90     args->len = len;
91 
92     status_t err = elf_open_handle(handle, elf_read_hook_memory, (void *)args, true);
93     if (err < 0)
94         free(args);
95 
96     return err;
97 }
98 
elf_close_handle(elf_handle_t * handle)99 void elf_close_handle(elf_handle_t *handle) {
100     if (!handle || !handle->open)
101         return;
102 
103     handle->open = false;
104 
105     if (handle->free_read_hook_arg)
106         free(handle->read_hook_arg);
107 
108     free(handle->pheaders);
109 }
110 
verify_eheader(const void * header)111 static int verify_eheader(const void *header) {
112     const elf_ehdr_t *eheader = header;
113 
114     if (memcmp(eheader->e_ident, ELF_MAGIC, 4) != 0)
115         return ERR_NOT_FOUND;
116 
117 #if WITH_ELF32
118     if (eheader->e_ident[EI_CLASS] != ELFCLASS32)
119         return ERR_NOT_FOUND;
120 #else
121     if (eheader->e_ident[EI_CLASS] != ELFCLASS64)
122         return ERR_NOT_FOUND;
123 #endif
124 
125 #if BYTE_ORDER == LITTLE_ENDIAN
126     if (eheader->e_ident[EI_DATA] != ELFDATA2LSB)
127         return ERR_NOT_FOUND;
128 #elif BYTE_ORDER == BIG_ENDIAN
129     if (eheader->e_ident[EI_DATA] != ELFDATA2MSB)
130         return ERR_NOT_FOUND;
131 #endif
132 
133     if (eheader->e_ident[EI_VERSION] != EV_CURRENT)
134         return ERR_NOT_FOUND;
135 
136     if (eheader->e_phoff == 0)
137         return ERR_NOT_FOUND;
138 
139     if (eheader->e_phentsize < sizeof(elf_phdr_t))
140         return ERR_NOT_FOUND;
141 
142 #if ARCH_ARM
143     if (eheader->e_machine != EM_ARM)
144         return ERR_NOT_FOUND;
145 #elif ARCH_ARM64
146     if (eheader->e_machine != EM_AARCH64)
147         return ERR_NOT_FOUND;
148 #elif ARCH_X86
149     if (eheader->e_machine != EM_386)
150         return ERR_NOT_FOUND;
151 #elif ARCH_X86_64
152     if (eheader->e_machine != EM_X86_64)
153         return ERR_NOT_FOUND;
154 #elif ARCH_MICROBLAZE
155     if (eheader->e_machine != EM_MICROBLAZE)
156         return ERR_NOT_FOUND;
157 #elif ARCH_RISCV
158     if (eheader->e_machine != EM_RISCV)
159         return ERR_NOT_FOUND;
160 #elif ARCH_VPU
161     if (eheader->e_machine != EM_VC4)
162         return ERR_NOT_FOUND;
163 #else
164 #error find proper EM_ define for your machine
165 #endif
166 
167     return NO_ERROR;
168 }
169 
elf_load(elf_handle_t * handle)170 status_t elf_load(elf_handle_t *handle) {
171     if (!handle)
172         return ERR_INVALID_ARGS;
173     if (!handle->open)
174         return ERR_NOT_READY;
175 
176     // validate that this is an ELF file
177     ssize_t readerr = handle->read_hook(handle, &handle->eheader, 0, sizeof(handle->eheader));
178     if (readerr < (ssize_t)sizeof(handle->eheader)) {
179         LTRACEF("couldn't read elf header\n");
180         return ERR_NOT_FOUND;
181     }
182 
183     if (verify_eheader(&handle->eheader)) {
184         LTRACEF("header not valid\n");
185         return ERR_NOT_FOUND;
186     }
187 
188     // sanity check number of program headers
189     LTRACEF("number of program headers %u, entry size %u\n", handle->eheader.e_phnum, handle->eheader.e_phentsize);
190     if (handle->eheader.e_phnum > 16 ||
191             handle->eheader.e_phentsize != sizeof(elf_phdr_t)) {
192         LTRACEF("too many program headers or bad size\n");
193         return ERR_NO_MEMORY;
194     }
195 
196     // allocate and read in the program headers
197     handle->pheaders = calloc(1, handle->eheader.e_phnum * handle->eheader.e_phentsize);
198     if (!handle->pheaders) {
199         LTRACEF("failed to allocate memory for program headers\n");
200         return ERR_NO_MEMORY;
201     }
202 
203     readerr = handle->read_hook(handle, handle->pheaders, handle->eheader.e_phoff, handle->eheader.e_phnum * handle->eheader.e_phentsize);
204     if (readerr < (ssize_t)(handle->eheader.e_phnum * handle->eheader.e_phentsize)) {
205         LTRACEF("failed to read program headers\n");
206         return ERR_NO_MEMORY;
207     }
208 
209     LTRACEF("program headers:\n");
210     uint load_count = 0;
211     for (uint i = 0; i < handle->eheader.e_phnum; i++) {
212         // parse the program headers
213         elf_phdr_t *pheader = &handle->pheaders[i];
214 
215         LTRACEF("%u: type %u offset 0x" ELF_OFF_PRINT_X " vaddr "
216                 ELF_ADDR_PRINT_X " paddr " ELF_ADDR_PRINT_X " memsiz "
217                 ELF_ADDR_PRINT_U " filesize " ELF_ADDR_PRINT_U "\n",
218                 i, pheader->p_type, pheader->p_offset, pheader->p_vaddr,
219                 pheader->p_paddr, pheader->p_memsz, pheader->p_filesz);
220 
221         // we only care about PT_LOAD segments at the moment
222         if (pheader->p_type == PT_LOAD) {
223             // if the memory allocation hook exists, call it
224             void *ptr = (void *)(uintptr_t)pheader->p_vaddr;
225 
226             if (handle->mem_alloc_hook) {
227                 status_t err = handle->mem_alloc_hook(handle, &ptr, pheader->p_memsz, load_count, 0);
228                 if (err < 0) {
229                     LTRACEF("mem hook failed, abort\n");
230                     // XXX clean up what we got so far
231                     return err;
232                 }
233             }
234 
235             // read the file portion of the segment into memory at vaddr
236             LTRACEF("reading segment at offset " ELF_OFF_PRINT_U " to address %p\n", pheader->p_offset, ptr);
237             readerr = handle->read_hook(handle, ptr, pheader->p_offset, pheader->p_filesz);
238             if (readerr < (ssize_t)pheader->p_filesz) {
239                 LTRACEF("error %ld reading program header %u\n", readerr, i);
240                 return (readerr < 0) ? readerr : ERR_IO;
241             }
242 
243             // zero out he difference between memsz and filesz
244             size_t tozero = pheader->p_memsz - pheader->p_filesz;
245             if (tozero > 0) {
246                 uint8_t *ptr2 = (uint8_t *)ptr + pheader->p_filesz;
247                 LTRACEF("zeroing memory at %p, size %zu\n", ptr2, tozero);
248                 memset(ptr2, 0, tozero);
249             }
250 
251             // make sure the i&d cache are coherent, if they exist
252             arch_sync_cache_range((addr_t)ptr, pheader->p_memsz);
253 
254             // track the number of load segments we have seen to pass the mem alloc hook
255             load_count++;
256         }
257     }
258 
259     // save the entry point
260     handle->entry = handle->eheader.e_entry;
261 
262     return NO_ERROR;
263 }
264 
265