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

Unified Diff: src/trusted/service_runtime/elf_util.c

Issue 389022: first step in factoring out code dealing with elf into a separate library.... (Closed) Base URL: http://nativeclient.googlecode.com/svn/trunk/src/native_client/
Patch Set: '' Created 11 years, 1 month 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
« no previous file with comments | « src/trusted/service_runtime/elf_util.h ('k') | src/trusted/service_runtime/mmap_test.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/trusted/service_runtime/elf_util.c
===================================================================
--- src/trusted/service_runtime/elf_util.c (revision 0)
+++ src/trusted/service_runtime/elf_util.c (revision 0)
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/*
+ * NaCl helper functions to deal with elf images
+ */
+
+#include "native_client/src/include/portability.h"
+
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "native_client/src/include/elf_constants.h"
+#include "native_client/src/include/nacl_elf.h"
+#include "native_client/src/include/nacl_macros.h"
+
+#include "native_client/src/shared/platform/nacl_log.h"
+
+#include "native_client/src/trusted/service_runtime/elf_util.h"
+#include "native_client/src/trusted/service_runtime/nacl_config.h"
+
+/* private */
+struct NaClElfImage {
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr phdrs[NACL_MAX_PROGRAM_HEADERS];
+ int loadable[NACL_MAX_PROGRAM_HEADERS];
+};
+
+
+enum NaClPhdrCheckAction {
+ PCA_NONE,
+ PCA_TEXT_CHECK,
+ PCA_IGNORE /* ignore this segment. currently used only for PT_PHDR. */
+};
+
+
+struct NaClPhdrChecks {
+ Elf32_Word p_type;
+ Elf32_Word p_flags; /* rwx */
+ enum NaClPhdrCheckAction action;
+ int required; /* only for text for now */
+ Elf32_Word p_vaddr; /* if non-zero, vaddr must be this */
+};
+
+/*
+ * Other than empty segments, these are the only ones that are allowed.
+ */
+static const struct NaClPhdrChecks nacl_phdr_check_data[] = {
+ /* phdr */
+ { PT_PHDR, PF_R, PCA_IGNORE, 0, 0, },
+ /* text */
+ { PT_LOAD, PF_R|PF_X, PCA_TEXT_CHECK, 1, NACL_TRAMPOLINE_END, },
+ /* rodata */
+ { PT_LOAD, PF_R, PCA_NONE, 0, 0, },
+ /* data/bss */
+ { PT_LOAD, PF_R|PF_W, PCA_NONE, 0, 0, },
+#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
+ /* arm exception handling unwind info (for c++)*/
+ /* TODO(robertm): for some reason this does NOT end up in ro maybe because
+ * it is relocatable. Try hacking the linker script to move it.
+ */
+ { PT_ARM_EXIDX, PF_R, PCA_IGNORE, 0, 0, },
+#endif
+ /*
+ * allow optional GNU stack permission marker, but require that the
+ * stack is non-executable.
+ */
+ { PT_GNU_STACK, PF_R|PF_W, PCA_NONE, 0, 0, },
+};
+
+
+
+static void NaClDumpElfHeader(Elf32_Ehdr *elf_hdr) {
+#define DUMP(m,f) do { NaClLog(2, \
+ #m " = %" f "\n", \
+ elf_hdr->m); } while (0)
+ DUMP(e_ident+1, ".3s");
+ DUMP(e_type, "#x");
+ DUMP(e_machine, "#x");
+ DUMP(e_version, "#x");
+ DUMP(e_entry, "#x");
+ DUMP(e_phoff, "#x");
+ DUMP(e_shoff, "#x");
+ DUMP(e_flags, "#x");
+ DUMP(e_ehsize, "#x");
+ DUMP(e_phentsize, "#x");
+ DUMP(e_phnum, "#x");
+ DUMP(e_shentsize, "#x");
+ DUMP(e_shnum, "#x");
+ DUMP(e_shstrndx, "#x");
+#undef DUMP
+ NaClLog(2, "sizeof(Elf32_Ehdr) = %x\n", (int) sizeof *elf_hdr);
+}
+
+
+static void NaClDumpElfProgramHeader(Elf32_Phdr *phdr) {
+ #define DUMP(mem) do { \
+ NaClLog(2, "%s: %x\n", #mem, phdr->mem); \
+ } while (0)
+
+ DUMP(p_type);
+ DUMP(p_offset);
+ DUMP(p_vaddr);
+ DUMP(p_paddr);
+ DUMP(p_filesz);
+ DUMP(p_memsz);
+ DUMP(p_flags);
+ NaClLog(2, " (%s %s %s)\n",
+ (phdr->p_flags & PF_R) ? "PF_R" : "",
+ (phdr->p_flags & PF_W) ? "PF_W" : "",
+ (phdr->p_flags & PF_X) ? "PF_X" : "");
+ DUMP(p_align);
+ #undef DUMP
+ NaClLog(2, "\n");
+ }
+
+
+NaClErrorCode NaClElfImageValidateAbi(struct NaClElfImage *image) {
+ const Elf32_Ehdr *hdr = &image->ehdr;
+
+ if (ELFOSABI_NACL != hdr->e_ident[EI_OSABI]) {
+ NaClLog(LOG_ERROR, "Expected OSABI %d, got %d\n",
+ ELFOSABI_NACL,
+ hdr->e_ident[EI_OSABI]);
+ return LOAD_BAD_ABI;
+ }
+
+ if (EF_NACL_ABIVERSION != hdr->e_ident[EI_ABIVERSION]) {
+ NaClLog(LOG_ERROR, "Expected ABIVERSION %d, got %d\n",
+ EF_NACL_ABIVERSION,
+ hdr->e_ident[EI_ABIVERSION]);
+ return LOAD_BAD_ABI;
+ }
+
+ return LOAD_OK;
+}
+
+
+NaClErrorCode NaClElfImageValidateElfHeader(struct NaClElfImage *image) {
+ const Elf32_Ehdr *hdr = &image->ehdr;
+
+ if (memcmp(hdr->e_ident, ELFMAG, SELFMAG)) {
+ NaClLog(LOG_ERROR, "bad elf magic\n");
+ return LOAD_BAD_ELF_MAGIC;
+ }
+
+ if (ELFCLASS32 != hdr->e_ident[EI_CLASS]) {
+ NaClLog(LOG_ERROR, "bad elf class\n");
+ return LOAD_NOT_32_BIT;
+ }
+
+ if (ET_EXEC != hdr->e_type) {
+ NaClLog(LOG_ERROR, "non executable\n");
+ return LOAD_NOT_EXEC;
+ }
+
+ if (EM_EXPECTED_BY_NACL != hdr->e_machine) {
+ NaClLog(LOG_ERROR, "bad machine\n");
+ return LOAD_BAD_MACHINE;
+ }
+
+ if (EV_CURRENT != hdr->e_version) {
+ NaClLog(LOG_ERROR, "bad elf version\n");
+ return LOAD_BAD_ELF_VERS;
+ }
+
+ return LOAD_OK;
+}
+
+/* TODO(robertm): decouple validation from computation of
+ text_region_bytes and max_vaddr */
+NaClErrorCode NaClElfImageValidateProgramHeaders(
+ struct NaClElfImage *image,
+ uint32_t addr_bits,
+ uint32_t *text_region_bytes,
+ uintptr_t *max_vaddr) {
+ /*
+ * Scan phdrs and do sanity checks in-line. Verify that the load
+ * address is NACL_TRAMPOLINE_END, that we have a single text
+ * segment. Data and TLS segments are not required, though it is
+ * hard to avoid with standard tools, but in any case there should
+ * be at most one each. Ensure that no segment's vaddr is outside
+ * of the address space. Ensure that PT_GNU_STACK is present, and
+ * that x is off.
+ */
+ const Elf32_Ehdr *hdr = &image->ehdr;
+ int seen_seg[NACL_ARRAY_SIZE(nacl_phdr_check_data)];
+
+ int segnum;
+ const Elf32_Phdr *php;
+ size_t j;
+
+ *max_vaddr = NACL_TRAMPOLINE_END;
+
+ /*
+ * nacl_phdr_check_data is small, so O(|check_data| * nap->elf_hdr.e_phum)
+ * is okay.
+ */
+ memset(seen_seg, 0, sizeof seen_seg);
+ for (segnum = 0; segnum < hdr->e_phnum; ++segnum) {
+ php = &image->phdrs[segnum];
+ NaClLog(3, "Looking at segment %d, type 0x%x, p_flags 0x%x\n",
+ segnum, php->p_type, php->p_flags);
+ for (j = 0; j < NACL_ARRAY_SIZE(nacl_phdr_check_data); ++j) {
+ if (php->p_type == nacl_phdr_check_data[j].p_type
+ && php->p_flags == nacl_phdr_check_data[j].p_flags) {
+ NaClLog(2, "Matched nacl_phdr_check_data[%"PRIdS"]\n", j);
+ if (seen_seg[j] > 0) {
+ NaClLog(2, "Segment %d is a type that has been seen\n", segnum);
+ return LOAD_DUP_SEGMENT;
+ }
+ ++seen_seg[j];
+
+ if (PCA_IGNORE == nacl_phdr_check_data[j].action) {
+ NaClLog(3, "Ignoring\n");
+ goto next_seg;
+ }
+
+ if (0 != php->p_memsz) {
+ /*
+ * We will load this segment later. Do the sanity checks.
+ */
+ if (0 != nacl_phdr_check_data[j].p_vaddr
+ && (nacl_phdr_check_data[j].p_vaddr != php->p_vaddr)) {
+ NaClLog(2,
+ ("Segment %d: bad virtual address: 0x%08x,"
+ " expected 0x%08x\n"),
+ segnum,
+ php->p_vaddr,
+ nacl_phdr_check_data[j].p_vaddr);
+ return LOAD_SEGMENT_BAD_LOC;
+ }
+ if (php->p_vaddr < NACL_TRAMPOLINE_END) {
+ NaClLog(2, "Segment %d: virtual address (0x%08x) too low\n",
+ segnum,
+ php->p_vaddr);
+ return LOAD_SEGMENT_OUTSIDE_ADDRSPACE;
+ }
+ /*
+ * integer overflow? Elf32_Addr and Elf32_Word are uint32_t,
+ * so the addition/comparison is well defined.
+ */
+ if (php->p_vaddr + php->p_memsz < php->p_vaddr) {
+ NaClLog(2,
+ "Segment %d: p_memsz caused integer overflow\n",
+ segnum);
+ return LOAD_SEGMENT_OUTSIDE_ADDRSPACE;
+ }
+ if (php->p_vaddr + php->p_memsz >= (1U << addr_bits)) {
+ NaClLog(2,
+ "Segment %d: too large, ends at 0x%08x\n",
+ segnum,
+ php->p_vaddr + php->p_memsz);
+ return LOAD_SEGMENT_OUTSIDE_ADDRSPACE;
+ }
+ if (php->p_filesz > php->p_memsz) {
+ NaClLog(2,
+ ("Segment %d: file size 0x%08x larger"
+ " than memory size 0x%08x\n"),
+ segnum,
+ php->p_filesz,
+ php->p_memsz);
+ return LOAD_SEGMENT_BAD_PARAM;
+ }
+
+ image->loadable[segnum] = 1;
+ /* record our decision that we will load this segment */
+
+ /*
+ * NACL_TRAMPOLINE_END <= p_vaddr
+ * <= p_vaddr + p_memsz
+ * < (1U << nap->addr_bits)
+ */
+ if (*max_vaddr < php->p_vaddr + php->p_memsz) {
+ *max_vaddr = php->p_vaddr + php->p_memsz;
+ }
+ }
+
+ switch (nacl_phdr_check_data[j].action) {
+ case PCA_NONE:
+ break;
+ case PCA_TEXT_CHECK:
+ if (0 == php->p_memsz) {
+ return LOAD_BAD_ELF_TEXT;
+ }
+ *text_region_bytes = php->p_filesz;
+ break;
+ case PCA_IGNORE:
+ break;
+ }
+ goto next_seg;
+ }
+ }
+ /* segment not in nacl_phdr_check_data */
+ if (0 == php->p_memsz) {
+ NaClLog(3, "Segment %d zero size: ignored\n", segnum);
+ continue;
+ }
+ NaClLog(2,
+ "Segment %d is of unexpected type 0x%x, flag 0x%x\n",
+ segnum,
+ php->p_type,
+ php->p_flags);
+ return LOAD_BAD_SEGMENT;
+ next_seg:
+ {}
+ }
+ for (j = 0; j < NACL_ARRAY_SIZE(nacl_phdr_check_data); ++j) {
+ if (nacl_phdr_check_data[j].required && !seen_seg[j]) {
+ return LOAD_REQUIRED_SEG_MISSING;
+ }
+ }
+
+ /*
+ * Memory allocation will use NaClRoundPage(nap->break_addr), but
+ * the system notion of break is always an exact address. Even
+ * though we must allocate and make accessible multiples of pages,
+ * the linux-style brk system call (which returns current break on
+ * failure) permits an arbitrarily aligned address as argument.
+ */
+
+ return LOAD_OK;
+}
+
+
+struct NaClElfImage *NaClElfImageNew(struct Gio *gp) {
+ struct NaClElfImage *result;
+ struct NaClElfImage image;
+ int cur_ph;
+
+ memset(image.loadable, 0, sizeof image.loadable);
+ if ((*gp->vtbl->Read)(gp,
+ &image.ehdr,
+ sizeof image.ehdr)
+ != sizeof image.ehdr) {
+ /* Consider making this fatal */
+ NaClLog(2, "could not load elf headers\n");
+ return 0;
+ }
+
+ NaClDumpElfHeader(&image.ehdr);
+
+ /* read program headers */
+ if (image.ehdr.e_phnum > NACL_MAX_PROGRAM_HEADERS) {
+ /* Consider making this fatal */
+ NaClLog(2, "too many prog headers\n");
+ return 0;
+ }
+
+ if (image.ehdr.e_phentsize < sizeof image.phdrs[0]) {
+ NaClLog(2, "bad prog headers size\n");
+ return 0;
+ }
+
+ if ((*gp->vtbl->Seek)(gp,
+ image.ehdr.e_phoff,
+ SEEK_SET) == -1) {
+ NaClLog(2, "cannot seek tp prog headers\n");
+ return 0;
+ }
+
+ if ((*gp->vtbl->Read)(gp,
+ &image.phdrs[0],
+ image.ehdr.e_phnum * sizeof image.phdrs[0])
+ != (int32_t) (image.ehdr.e_phnum * sizeof image.phdrs[0])) {
+ NaClLog(2, "cannot load tp prog headers\n");
+ return 0;
+ }
+
+ for (cur_ph = 0; cur_ph < image.ehdr.e_phnum; ++cur_ph) {
+ NaClDumpElfProgramHeader(&image.phdrs[cur_ph]);
+ }
+
+ /* we delay allocating till the end to avoid cleanup code */
+ result = malloc(sizeof image);
+ if (result == 0) {
+ NaClLog(LOG_FATAL, "no enough memory for image meta data\n");
+ return 0;
+ }
+ memcpy(result, &image, sizeof image);
+ return result;
+}
+
+
+NaClErrorCode NaClElfImageLoad(struct NaClElfImage *image,
+ struct Gio *gp,
+ uint32_t addr_bits,
+ uintptr_t mem_start) {
+ int segnum;
+ uintptr_t paddr;
+ uintptr_t end_vaddr;
+
+ for (segnum = 0; segnum < image->ehdr.e_phnum; ++segnum) {
+ const Elf32_Phdr *php = &image->phdrs[segnum];
+
+ /* did we decide that we will load this segment earlier? */
+ if (!image->loadable[segnum]) {
+ continue;
+ }
+
+ NaClLog(2, "loading segment %d", segnum);
+ end_vaddr = php->p_vaddr + php->p_filesz;
+ /* integer overflow? */
+ if (end_vaddr < php->p_vaddr) {
+ NaClLog(LOG_FATAL, "parameter error should have been detected already\n");
+ }
+ /*
+ * is the end virtual address within the NaCl application's
+ * address space? if it is, it implies that the start virtual
+ * address is also.
+ */
+ if (end_vaddr >= (1U << addr_bits)) {
+ NaClLog(LOG_FATAL, "parameter error should have been detected already\n");
+ }
+
+ paddr = mem_start + php->p_vaddr;
+
+ if ((*gp->vtbl->Seek)(gp, php->p_offset, SEEK_SET) == -1) {
+ NaClLog(LOG_ERROR, "seek failure segment %d", segnum);
+ return LOAD_SEGMENT_BAD_PARAM;
+ }
+ if ((Elf32_Word) (*gp->vtbl->Read)(gp, (void *) paddr, php->p_filesz)
+ != php->p_filesz) {
+ NaClLog(LOG_ERROR, "load failure segment %d", segnum);
+ return LOAD_SEGMENT_BAD_PARAM;
+ }
+ /* region from p_filesz to p_memsz should already be zero filled */
+ }
+
+ return LOAD_OK;
+}
+
+
+void NaClElfImageDelete(struct NaClElfImage *image) {
+ free(image);
+}
+
+
+uint32_t NaClElfImageGetEntryPoint(struct NaClElfImage *image) {
+ return image->ehdr.e_entry;
+}
+
+
+/* TODO(robertm): this code should enforce that either 16 or 32 bit alignment is
+ is set - there are currently some problems with ARM, though
+*/
+int NaClElfImageGetAlignBoundary(struct NaClElfImage *image) {
+ unsigned long eflags = image->ehdr.e_flags & EF_NACL_ALIGN_MASK;
+ if (eflags) {
+ if (eflags == EF_NACL_ALIGN_16) {
+ return 16;
+ } else if (eflags == EF_NACL_ALIGN_32) {
+ return 32;
+ } else {
+ NaClLog(LOG_ERROR, "strange alignment");
+ return 0;
+ }
+ } else {
+ return 32;
+ }
+}
« no previous file with comments | « src/trusted/service_runtime/elf_util.h ('k') | src/trusted/service_runtime/mmap_test.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698