source: mainline/uspace/lib/c/generic/elf/elf_mod.c@ 84239b1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 84239b1 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/*
2 * Copyright (c) 2006 Sergey Bondari
3 * Copyright (c) 2006 Jakub Jermar
4 * Copyright (c) 2011 Jiri Svoboda
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup generic
32 * @{
33 */
34
35/**
36 * @file
37 * @brief Userspace ELF module loader.
38 *
39 * This module allows loading ELF binaries (both executables and
40 * shared objects) from VFS. The current implementation allocates
41 * anonymous memory, fills it with segment data and then adjusts
42 * the memory areas' flags to the final value. In the future,
43 * the segments will be mapped directly from the file.
44 */
45
46#include <errno.h>
47#include <stdio.h>
48#include <vfs/vfs.h>
49#include <stddef.h>
50#include <stdint.h>
51#include <align.h>
52#include <assert.h>
53#include <as.h>
54#include <elf/elf.h>
55#include <smc.h>
56#include <loader/pcb.h>
57#include <entry_point.h>
58#include <str_error.h>
59#include <stdlib.h>
60
61#include <elf/elf_load.h>
62
63#define DPRINTF(...)
64
65static const char *error_codes[] = {
66 "no error",
67 "invalid image",
68 "address space error",
69 "incompatible image",
70 "unsupported image type",
71 "irrecoverable error",
72 "file io error"
73};
74
75static unsigned int elf_load_module(elf_ld_t *elf, size_t so_bias);
76static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry);
77static int section_header(elf_ld_t *elf, elf_section_header_t *entry);
78static int load_segment(elf_ld_t *elf, elf_segment_header_t *entry);
79
80/** Load ELF binary from a file.
81 *
82 * Load an ELF binary from the specified file. If the file is
83 * an executable program, it is loaded unbiased. If it is a shared
84 * object, it is loaded with the bias @a so_bias. Some information
85 * extracted from the binary is stored in a elf_info_t structure
86 * pointed to by @a info.
87 *
88 * @param file ELF file.
89 * @param so_bias Bias to use if the file is a shared object.
90 * @param info Pointer to a structure for storing information
91 * extracted from the binary.
92 *
93 * @return EE_OK on success or EE_xx error code.
94 *
95 */
96int elf_load_file(int file, size_t so_bias, eld_flags_t flags, elf_finfo_t *info)
97{
98 elf_ld_t elf;
99
100 int ofile;
101 errno_t rc = vfs_clone(file, -1, true, &ofile);
102 if (rc == EOK) {
103 rc = vfs_open(ofile, MODE_READ);
104 }
105 if (rc != EOK) {
106 return EE_IO;
107 }
108
109 elf.fd = ofile;
110 elf.info = info;
111 elf.flags = flags;
112
113 int ret = elf_load_module(&elf, so_bias);
114
115 vfs_put(ofile);
116 return ret;
117}
118
119int elf_load_file_name(const char *path, size_t so_bias, eld_flags_t flags,
120 elf_finfo_t *info)
121{
122 int file;
123 errno_t rc = vfs_lookup(path, 0, &file);
124 if (rc == EOK) {
125 int ret = elf_load_file(file, so_bias, flags, info);
126 vfs_put(file);
127 return ret;
128 } else {
129 return EE_IO;
130 }
131}
132
133/** Load an ELF binary.
134 *
135 * The @a elf structure contains the loader state, including
136 * an open file, from which the binary will be loaded,
137 * a pointer to the @c info structure etc.
138 *
139 * @param elf Pointer to loader state buffer.
140 * @param so_bias Bias to use if the file is a shared object.
141 * @return EE_OK on success or EE_xx error code.
142 */
143static unsigned int elf_load_module(elf_ld_t *elf, size_t so_bias)
144{
145 elf_header_t header_buf;
146 elf_header_t *header = &header_buf;
147 aoff64_t pos = 0;
148 size_t nr;
149 int i, ret;
150 errno_t rc;
151
152 rc = vfs_read(elf->fd, &pos, header, sizeof(elf_header_t), &nr);
153 if (rc != EOK || nr != sizeof(elf_header_t)) {
154 DPRINTF("Read error.\n");
155 return EE_IO;
156 }
157
158 elf->header = header;
159
160 /* Identify ELF */
161 if (header->e_ident[EI_MAG0] != ELFMAG0 ||
162 header->e_ident[EI_MAG1] != ELFMAG1 ||
163 header->e_ident[EI_MAG2] != ELFMAG2 ||
164 header->e_ident[EI_MAG3] != ELFMAG3) {
165 DPRINTF("Invalid header.\n");
166 return EE_INVALID;
167 }
168
169 /* Identify ELF compatibility */
170 if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
171 header->e_machine != ELF_MACHINE ||
172 header->e_ident[EI_VERSION] != EV_CURRENT ||
173 header->e_version != EV_CURRENT ||
174 header->e_ident[EI_CLASS] != ELF_CLASS) {
175 DPRINTF("Incompatible data/version/class.\n");
176 return EE_INCOMPATIBLE;
177 }
178
179 if (header->e_phentsize != sizeof(elf_segment_header_t)) {
180 DPRINTF("e_phentsize: %u != %zu\n", header->e_phentsize,
181 sizeof(elf_segment_header_t));
182 return EE_INCOMPATIBLE;
183 }
184
185 if (header->e_shentsize != sizeof(elf_section_header_t)) {
186 DPRINTF("e_shentsize: %u != %zu\n", header->e_shentsize,
187 sizeof(elf_section_header_t));
188 return EE_INCOMPATIBLE;
189 }
190
191 /* Check if the object type is supported. */
192 if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
193 DPRINTF("Object type %d is not supported\n", header->e_type);
194 return EE_UNSUPPORTED;
195 }
196
197 /* Shared objects can be loaded with a bias */
198 if (header->e_type == ET_DYN)
199 elf->bias = so_bias;
200 else
201 elf->bias = 0;
202
203 elf->info->interp = NULL;
204 elf->info->dynamic = NULL;
205
206 /* Walk through all segment headers and process them. */
207 for (i = 0; i < header->e_phnum; i++) {
208 elf_segment_header_t segment_hdr;
209
210 pos = header->e_phoff + i * sizeof(elf_segment_header_t);
211 rc = vfs_read(elf->fd, &pos, &segment_hdr,
212 sizeof(elf_segment_header_t), &nr);
213 if (rc != EOK || nr != sizeof(elf_segment_header_t)) {
214 DPRINTF("Read error.\n");
215 return EE_IO;
216 }
217
218 ret = segment_header(elf, &segment_hdr);
219 if (ret != EE_OK)
220 return ret;
221 }
222
223 DPRINTF("Parse sections.\n");
224
225 /* Inspect all section headers and proccess them. */
226 for (i = 0; i < header->e_shnum; i++) {
227 elf_section_header_t section_hdr;
228
229 pos = header->e_shoff + i * sizeof(elf_section_header_t);
230 rc = vfs_read(elf->fd, &pos, &section_hdr,
231 sizeof(elf_section_header_t), &nr);
232 if (rc != EOK || nr != sizeof(elf_section_header_t)) {
233 DPRINTF("Read error.\n");
234 return EE_IO;
235 }
236
237 ret = section_header(elf, &section_hdr);
238 if (ret != EE_OK)
239 return ret;
240 }
241
242 elf->info->entry =
243 (entry_point_t)((uint8_t *)header->e_entry + elf->bias);
244
245 DPRINTF("Done.\n");
246
247 return EE_OK;
248}
249
250/** Print error message according to error code.
251 *
252 * @param rc Return code returned by elf_load().
253 *
254 * @return NULL terminated description of error.
255 */
256const char *elf_error(unsigned int rc)
257{
258 assert(rc < sizeof(error_codes) / sizeof(char *));
259
260 return error_codes[rc];
261}
262
263/** Process TLS program header.
264 *
265 * @param elf Pointer to loader state buffer.
266 * @param hdr TLS program header
267 * @param info Place to store TLS info
268 */
269static void tls_program_header(elf_ld_t *elf, elf_segment_header_t *hdr,
270 elf_tls_info_t *info)
271{
272 info->tdata = (void *)((uint8_t *)hdr->p_vaddr + elf->bias);
273 info->tdata_size = hdr->p_filesz;
274 info->tbss_size = hdr->p_memsz - hdr->p_filesz;
275 info->tls_align = hdr->p_align;
276}
277
278/** Process segment header.
279 *
280 * @param elf Pointer to loader state buffer.
281 * @param entry Segment header.
282 *
283 * @return EE_OK on success, error code otherwise.
284 */
285static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry)
286{
287 switch (entry->p_type) {
288 case PT_NULL:
289 case PT_PHDR:
290 case PT_NOTE:
291 break;
292 case PT_LOAD:
293 return load_segment(elf, entry);
294 break;
295 case PT_INTERP:
296 /* Assume silently interp == "/app/dload" */
297 elf->info->interp = "/app/dload";
298 break;
299 case PT_DYNAMIC:
300 /* Record pointer to dynamic section into info structure */
301 elf->info->dynamic =
302 (void *)((uint8_t *)entry->p_vaddr + elf->bias);
303 DPRINTF("dynamic section found at %p\n",
304 (void *)elf->info->dynamic);
305 break;
306 case 0x70000000:
307 /* FIXME: MIPS reginfo */
308 break;
309 case PT_TLS:
310 /* Parse TLS program header */
311 tls_program_header(elf, entry, &elf->info->tls);
312 DPRINTF("TLS header found at %p\n",
313 (void *)((uint8_t *)entry->p_vaddr + elf->bias));
314 break;
315 case PT_SHLIB:
316// case PT_LOPROC:
317// case PT_HIPROC:
318 default:
319 DPRINTF("Segment p_type %d unknown.\n", entry->p_type);
320 return EE_UNSUPPORTED;
321 break;
322 }
323 return EE_OK;
324}
325
326/** Load segment described by program header entry.
327 *
328 * @param elf Loader state.
329 * @param entry Program header entry describing segment to be loaded.
330 *
331 * @return EE_OK on success, error code otherwise.
332 */
333int load_segment(elf_ld_t *elf, elf_segment_header_t *entry)
334{
335 void *a;
336 int flags = 0;
337 uintptr_t bias;
338 uintptr_t base;
339 void *seg_ptr;
340 uintptr_t seg_addr;
341 size_t mem_sz;
342 aoff64_t pos;
343 errno_t rc;
344 size_t nr;
345
346 bias = elf->bias;
347
348 seg_addr = entry->p_vaddr + bias;
349 seg_ptr = (void *) seg_addr;
350
351 DPRINTF("Load segment at addr %p, size 0x%zx\n", (void *) seg_addr,
352 entry->p_memsz);
353
354 if (entry->p_align > 1) {
355 if ((entry->p_offset % entry->p_align) !=
356 (seg_addr % entry->p_align)) {
357 DPRINTF("Align check 1 failed offset%%align=0x%zx, "
358 "vaddr%%align=0x%zx\n",
359 entry->p_offset % entry->p_align,
360 seg_addr % entry->p_align);
361 return EE_INVALID;
362 }
363 }
364
365 /* Final flags that will be set for the memory area */
366
367 if (entry->p_flags & PF_X)
368 flags |= AS_AREA_EXEC;
369 if (entry->p_flags & PF_W)
370 flags |= AS_AREA_WRITE;
371 if (entry->p_flags & PF_R)
372 flags |= AS_AREA_READ;
373 flags |= AS_AREA_CACHEABLE;
374
375 base = ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE);
376 mem_sz = entry->p_memsz + (entry->p_vaddr - base);
377
378 DPRINTF("Map to seg_addr=%p-%p.\n", (void *) seg_addr,
379 (void *) (entry->p_vaddr + bias +
380 ALIGN_UP(entry->p_memsz, PAGE_SIZE)));
381
382 /*
383 * For the course of loading, the area needs to be readable
384 * and writeable.
385 */
386 a = as_area_create((uint8_t *) base + bias, mem_sz,
387 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE,
388 AS_AREA_UNPAGED);
389 if (a == AS_MAP_FAILED) {
390 DPRINTF("memory mapping failed (%p, %zu)\n",
391 (void *) (base + bias), mem_sz);
392 return EE_MEMORY;
393 }
394
395 DPRINTF("as_area_create(%p, %#zx, %d) -> %p\n",
396 (void *) (base + bias), mem_sz, flags, (void *) a);
397
398 /*
399 * Load segment data
400 */
401 pos = entry->p_offset;
402 rc = vfs_read(elf->fd, &pos, seg_ptr, entry->p_filesz, &nr);
403 if (rc != EOK || nr != entry->p_filesz) {
404 DPRINTF("read error\n");
405 return EE_IO;
406 }
407
408 /*
409 * The caller wants to modify the segments first. He will then
410 * need to set the right access mode and ensure SMC coherence.
411 */
412 if ((elf->flags & ELDF_RW) != 0) return EE_OK;
413
414// printf("set area flags to %d\n", flags);
415 rc = as_area_change_flags(seg_ptr, flags);
416 if (rc != EOK) {
417 DPRINTF("Failed to set memory area flags.\n");
418 return EE_MEMORY;
419 }
420
421 if (flags & AS_AREA_EXEC) {
422 /* Enforce SMC coherence for the segment */
423 if (smc_coherence(seg_ptr, entry->p_filesz))
424 return EE_MEMORY;
425 }
426
427 return EE_OK;
428}
429
430/** Process section header.
431 *
432 * @param elf Loader state.
433 * @param entry Segment header.
434 *
435 * @return EE_OK on success, error code otherwise.
436 */
437static int section_header(elf_ld_t *elf, elf_section_header_t *entry)
438{
439 switch (entry->sh_type) {
440 case SHT_PROGBITS:
441 if (entry->sh_flags & SHF_TLS) {
442 /* .tdata */
443 }
444 break;
445 case SHT_NOBITS:
446 if (entry->sh_flags & SHF_TLS) {
447 /* .tbss */
448 }
449 break;
450 case SHT_DYNAMIC:
451 /* Record pointer to dynamic section into info structure */
452 elf->info->dynamic =
453 (void *)((uint8_t *)entry->sh_addr + elf->bias);
454 DPRINTF("Dynamic section found at %p.\n",
455 (void *) elf->info->dynamic);
456 break;
457 default:
458 break;
459 }
460
461 return EE_OK;
462}
463
464/** @}
465 */
Note: See TracBrowser for help on using the repository browser.