Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(712)

Unified Diff: src/nonsfi/loader/elf_loader.c

Issue 269703002: Non-SFI Mode: Add nonsfi_loader and plumbing to test it (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: Review Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/nonsfi/loader/elf_loader.c
diff --git a/src/nonsfi/loader/elf_loader.c b/src/nonsfi/loader/elf_loader.c
new file mode 100644
index 0000000000000000000000000000000000000000..8f68a1465bc5d07d18e03526fc19e353d72ffa1d
--- /dev/null
+++ b/src/nonsfi/loader/elf_loader.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2014 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "native_client/src/nonsfi/irt/irt_interfaces.h"
+#include "native_client/src/shared/platform/nacl_log.h"
+#include "native_client/src/trusted/service_runtime/nacl_config.h"
+
+
+/*
+ * Note that Non-SFI NaCl uses a 4k page size (in order to allow loading
+ * existing Linux DSOs), in contrast to SFI NaCl's 64k page size (required
+ * for running on Windows).
+ */
+#define PAGE_SIZE 0x1000
+#define PAGE_MASK (PAGE_SIZE - 1)
+#define MAX_PHNUM 128
+
+static uintptr_t PageSizeRoundDown(uintptr_t addr) {
+ return addr & ~PAGE_MASK;
+}
+
+static uintptr_t PageSizeRoundUp(uintptr_t addr) {
+ return PageSizeRoundDown(addr + PAGE_SIZE - 1);
+}
+
+static int ElfFlagsToMmapFlags(int pflags) {
+ return ((pflags & PF_X) != 0 ? PROT_EXEC : 0) |
+ ((pflags & PF_R) != 0 ? PROT_READ : 0) |
+ ((pflags & PF_W) != 0 ? PROT_WRITE : 0);
+}
+
+static void CheckElfHeaders(ElfW(Ehdr) *ehdr) {
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ NaClLog(LOG_FATAL, "Not an ELF file: no ELF header\n");
+ }
+ if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
+ NaClLog(LOG_FATAL, "Unexpected ELF class: not ELFCLASS32\n");
+ }
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+ NaClLog(LOG_FATAL, "Not a little-endian ELF file\n");
+ }
+ if (ehdr->e_type != ET_DYN) {
+ NaClLog(LOG_FATAL, "Not a relocatable ELF object (not ET_DYN)\n");
+ }
+ if (ehdr->e_machine != NACL_ELF_E_MACHINE) {
+ NaClLog(LOG_FATAL, "Unexpected ELF e_machine field\n");
+ }
+ if (ehdr->e_version != EV_CURRENT) {
+ NaClLog(LOG_FATAL, "Unexpected ELF e_version field\n");
+ }
+ if (ehdr->e_ehsize != sizeof(*ehdr)) {
+ NaClLog(LOG_FATAL, "Unexpected ELF e_ehsize field\n");
+ }
+ if (ehdr->e_phentsize != sizeof(ElfW(Phdr))) {
+ NaClLog(LOG_FATAL, "Unexpected ELF e_phentsize field\n");
+ }
+}
+
+static uintptr_t LoadElfFile(const char *filename) {
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ NaClLog(LOG_FATAL, "Failed to open %s: %s\n", filename, strerror(errno));
+ }
+
+ /* Read ELF file headers. */
+ ElfW(Ehdr) ehdr;
+ ssize_t bytes_read = pread(fd, &ehdr, sizeof(ehdr), 0);
+ if (bytes_read != sizeof(ehdr)) {
+ NaClLog(LOG_FATAL, "Failed to read ELF file headers\n");
+ }
+ CheckElfHeaders(&ehdr);
+
+ /* Read ELF program headers. */
+ if (ehdr.e_phnum > MAX_PHNUM) {
+ NaClLog(LOG_FATAL, "ELF file has too many program headers\n");
+ }
+ ElfW(Phdr) phdr[MAX_PHNUM];
+ ssize_t phdrs_size = sizeof(phdr[0]) * ehdr.e_phnum;
+ bytes_read = pread(fd, phdr, phdrs_size, ehdr.e_phoff);
+ if (bytes_read != phdrs_size) {
+ NaClLog(LOG_FATAL, "Failed to read ELF program headers\n");
+ }
+
+ /* Find the first PT_LOAD segment. */
+ size_t phdr_index = 0;
+ while (phdr_index < ehdr.e_phnum && phdr[phdr_index].p_type != PT_LOAD)
+ ++phdr_index;
+ if (phdr_index == ehdr.e_phnum) {
+ NaClLog(LOG_FATAL, "ELF file has no PT_LOAD header\n");
+ }
+
+ /*
+ * ELF requires that PT_LOAD segments be in ascending order of p_vaddr.
+ * Find the last one to calculate the whole address span of the image.
+ */
+ ElfW(Phdr) *first_load = &phdr[phdr_index];
+ ElfW(Phdr) *last_load = &phdr[ehdr.e_phnum - 1];
+ while (last_load > first_load && last_load->p_type != PT_LOAD)
+ --last_load;
+
+ if (first_load->p_vaddr != 0) {
+ NaClLog(LOG_FATAL, "First PT_LOAD segment's load address is not 0\n");
+ }
+ size_t span = last_load->p_vaddr + last_load->p_memsz;
+
+ /* Reserve address space. */
+ void *mapping = mmap(NULL, span, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,
+ -1, 0);
+ if (mapping == MAP_FAILED) {
+ NaClLog(LOG_FATAL, "Failed to reserve address space for executable\n");
+ }
+ uintptr_t load_bias = (uintptr_t) mapping;
+
+ /* Map the PT_LOAD segments. */
+ uintptr_t prev_segment_end = 0;
+ int entry_point_is_valid = 0;
+ ElfW(Phdr) *ph;
+ for (ph = first_load; ph <= last_load; ++ph) {
+ if (ph->p_type != PT_LOAD)
+ continue;
+ int prot = ElfFlagsToMmapFlags(ph->p_flags);
+ uintptr_t segment_start = PageSizeRoundDown(ph->p_vaddr);
+ uintptr_t segment_end = PageSizeRoundUp(ph->p_vaddr + ph->p_memsz);
+ if (segment_start < prev_segment_end) {
+ NaClLog(LOG_FATAL, "PT_LOAD segments overlap or are not sorted\n");
+ }
+ prev_segment_end = segment_end;
+ void *segment_addr = (void *) (load_bias + segment_start);
+ void *map_result = mmap((void *) segment_addr,
+ segment_end - segment_start,
+ prot, MAP_PRIVATE | MAP_FIXED, fd,
+ PageSizeRoundDown(ph->p_offset));
+ if (map_result != segment_addr) {
+ NaClLog(LOG_FATAL, "Failed to map ELF segment\n");
+ }
+
+ if ((ph->p_flags & PF_X) != 0 &&
+ ph->p_vaddr <= ehdr.e_entry &&
+ ehdr.e_entry < ph->p_vaddr + ph->p_filesz) {
+ entry_point_is_valid = 1;
+ }
+
+ /* Handle the BSS. */
+ if (ph->p_memsz < ph->p_filesz) {
+ NaClLog(LOG_FATAL, "Bad ELF segment: p_memsz < p_filesz\n");
+ }
+ if (ph->p_memsz > ph->p_filesz) {
+ if ((ph->p_flags & PF_W) == 0) {
+ NaClLog(LOG_FATAL,
+ "Bad ELF segment: non-writable segment with BSS\n");
+ }
+
+ uintptr_t bss_start = ph->p_vaddr + ph->p_filesz;
+ uintptr_t bss_map_start = PageSizeRoundUp(bss_start);
+ /*
+ * Zero the BSS to the end of the page.
+ *
+ * Zeroing beyond p_memsz might be more than is necessary for Non-SFI
+ * NaCl. On Linux, programs such as ld.so use the rest of the page,
+ * after p_memsz, as part of the brk() heap and assume that it has
+ * been zeroed. Non-SFI NaCl does not provide a brk() heap, though.
+ * However, zeroing to the end of the page is simple enough, and it's
+ * consistent with the case in additional pages must be mapped, which
+ * will all be fully zeroed.
+ */
+ memset((void *) (load_bias + bss_start), 0, bss_map_start - bss_start);
+
+ if (bss_map_start < segment_end) {
+ void *map_addr = (void *) (load_bias + bss_map_start);
+ map_result = mmap(map_addr, segment_end - bss_map_start,
+ prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ -1, 0);
+ if (map_result != map_addr) {
+ NaClLog(LOG_FATAL, "Failed to map BSS for ELF segment\n");
+ }
+ }
+ }
+ }
+
+ if (close(fd) != 0) {
+ NaClLog(LOG_FATAL, "close() failed\n");
+ }
+
+ if (!entry_point_is_valid) {
+ NaClLog(LOG_FATAL, "ELF entry point does not point into an executable "
+ "PT_LOAD segment\n");
+ }
+ return load_bias + ehdr.e_entry;
+}
+
+int main(int argc, char **argv, char **environ) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <executable> <args...>\n", argv[0]);
+ return 1;
+ }
+ const char *nexe_filename = argv[1];
+ uintptr_t entry = LoadElfFile(nexe_filename);
+ return nacl_irt_nonsfi_entry(argc, argv, environ, (nacl_entry_func_t) entry);
+}

Powered by Google App Engine
This is Rietveld 408576698