Add -E option for setting environment variables
[nativeclient.git] / ncv / ncfileutil.c
blob3fea867a4819475ebe770ed8a440d569959f0284
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * ncfileutil.c - open an executable file. FOR TESTING ONLY.
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <assert.h>
39 #if NACL_WINDOWS
40 #include <io.h>
41 #define open _open
42 #else
43 #include <unistd.h>
44 #endif
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <sys/types.h>
49 #include "native_client/include/portability.h"
50 #include "native_client/ncv/ncfileutil.h"
51 #include "native_client/ncv/ncdecode.h"
52 #include "native_client/ncv/ncdisasmtab.h"
54 /* This module is intended for testing use only, not for production use */
55 /* in sel_ldr. To prevent unintended production usage, define a symbol */
56 /* that will cause a load-time error for sel_ldr. */
57 int gNaClValidateImage_foo = 0;
58 void NaClValidateImage() { gNaClValidateImage_foo += 1; }
60 #if !NACL_WINDOWS
61 /* redefine INLINE for non-Windows platforms */
62 #define INLINE inline
63 #endif
65 static INLINE uint32_t NCPageTrunc(uint32_t v) {
66 return (v & ~(kNCFileUtilPageSize - 1));
69 static INLINE uint32_t NCPageRound(uint32_t v) {
70 return(NCPageTrunc(v + (kNCFileUtilPageSize - 1)));
73 /***********************************************************************/
74 /* THIS ROUTINE IS FOR DEBUGGING/TESTING ONLY, NOT FOR SECURE RUNTIME */
75 /* ALL PAGES ARE LEFT WRITEABLE BY THIS LOADER. */
76 /***********************************************************************/
77 /* Loading a NC executable from a host file */
78 static int readat(const int fd, void *buf, const size_t sz, const size_t at)
80 size_t sofar = 0;
81 int nread;
82 char *cbuf = (char *)buf;
84 if (0 > lseek(fd, at, SEEK_SET)) {
85 fprintf(stderr, "readat: lseek failed\n");
86 return -1;
88 /* Strangely this loop is needed on Windows. It seems the read() */
89 /* implementation doesn't always return as many bytes as requested */
90 /* so you have to keep on trying. */
91 do {
92 nread = read(fd, &cbuf[sofar], sz - sofar);
93 if (nread <= 0) {
94 fprintf(stderr, "readat: read failed\n");
95 return -1;
97 sofar += nread;
98 } while (sz != sofar);
99 return nread;
102 static int nc_load(ncfile *ncf, int fd)
104 #define NCLOADFAIL(_args) \
105 { fprintf(stderr, "nc_load(%s):", ncf->fname); \
106 fprintf _args ; return -1; }
108 Elf32_Ehdr h;
109 Elf32_Phdr ph[kMaxPhnum];
110 ssize_t nread;
111 size_t vmemlo, vmemhi, shsize, phsize;
112 int i;
114 /* Read and check the ELF header */
115 nread = readat(fd, &h, sizeof(h), 0);
116 if (nread < 0 || (size_t) nread < sizeof(h)) {
117 NCLOADFAIL((stderr, "could not read ELF header\n"));
120 /* do a bunch of sanity checks */
121 if (strncmp((char *)h.e_ident, ELFMAG, strlen(ELFMAG))) {
122 NCLOADFAIL((stderr, "bad magic number\n"));
124 if (h.e_ident[EI_OSABI] != ELFOSABI_NACL) {
125 fprintf(stderr, "%s: bad OS ABI %x\n", ncf->fname, h.e_ident[EI_OSABI]);
126 // return;
128 if (h.e_ident[EI_ABIVERSION] != EF_NACL_ABIVERSION) {
129 fprintf(stderr, "%s: bad ABI version %d\n", ncf->fname,
130 h.e_ident[EI_ABIVERSION]);
131 // return;
133 if ((h.e_flags & EF_NACL_ALIGN_MASK) == EF_NACL_ALIGN_16) {
134 ncf->ncalign = 16;
135 } else if ((h.e_flags & EF_NACL_ALIGN_MASK) == EF_NACL_ALIGN_32) {
136 ncf->ncalign = 32;
137 } else {
138 fprintf(stderr, "%s: bad align mask %x\n", ncf->fname,
139 (uint32_t)(h.e_flags & EF_NACL_ALIGN_MASK));
140 ncf->ncalign = 16;
141 // return;
144 /* Read the program header table */
145 if (h.e_phnum <= 0 || h.e_phnum > kMaxPhnum) {
146 NCLOADFAIL((stderr, "h.e_phnum %d > kMaxPhnum %d\n",
147 h.e_phnum, kMaxPhnum));
149 phsize = h.e_phnum * sizeof(*ph);
150 nread = readat(fd, ph, phsize, h.e_phoff);
151 if (nread < 0 || (size_t) nread < phsize) return -1;
153 /* Iterate through the program headers to find the virtual */
154 /* size of loaded text. */
155 vmemlo = 0xffffffff;
156 vmemhi = 0;
157 for (i = 0; i < h.e_phnum; i++) {
158 if (ph[i].p_type != PT_LOAD) continue;
159 if (0 == (ph[i].p_flags & PF_X)) continue;
160 /* This is executable text. Check low and high addrs */
161 if (vmemlo > ph[i].p_vaddr) vmemlo = ph[i].p_vaddr;
162 if (vmemhi < ph[i].p_vaddr + ph[i].p_memsz) {
163 vmemhi = ph[i].p_vaddr + ph[i].p_memsz;
166 vmemhi = NCPageRound(vmemhi);
167 ncf->size = vmemhi - vmemlo;
168 ncf->vbase = vmemlo;
169 if (vmemlo != NCPageTrunc(vmemlo)) {
170 NCLOADFAIL((stderr, "vmemlo is not aligned\n"));
172 ncf->data = (uint8_t *)calloc(1, ncf->size);
173 if (NULL == ncf->data) {
174 NCLOADFAIL((stderr, "calloc(1, %d) failed\n", (int)ncf->size));
177 /* Load program text segments */
178 for (i = 0; i < h.e_phnum; i++) {
179 const Elf32_Phdr *p = &ph[i];
180 if (p->p_type != PT_LOAD) continue;
181 if (0 == (ph[i].p_flags & PF_X)) continue;
183 assert(ncf->size >= NCPageRound(p->p_vaddr - ncf->vbase + p->p_memsz));
184 nread = readat(fd, &(ncf->data[p->p_vaddr - ncf->vbase]),
185 p->p_filesz, p->p_offset);
186 if (nread < 0 || (size_t) nread < p->p_filesz) {
187 NCLOADFAIL((stderr, "could not read segment %d (%d < %d)\n",
188 i, (int)nread, p->p_filesz));
191 /* load the section headers */
192 ncf->shnum = h.e_shnum;
193 shsize = ncf->shnum * sizeof(*ncf->sheaders);
194 ncf->sheaders = (Elf32_Shdr *)calloc(1, shsize);
195 if (NULL == ncf->sheaders) {
196 NCLOADFAIL((stderr, "calloc(1, %d) failed\n", (int)shsize));
198 nread = readat(fd, ncf->sheaders, shsize, h.e_shoff);
199 if (nread < 0 || (size_t) nread < shsize) {
200 NCLOADFAIL((stderr, "could not read section headers\n"));
203 /* success! */
204 return 0;
207 ncfile *nc_loadfile(const char *filename)
209 ncfile *ncf;
210 int fd;
211 int rdflags = O_RDONLY;
212 #if NACL_WINDOWS
213 rdflags |= _O_BINARY;
214 #endif
215 fd = open(filename, rdflags);
216 if (fd < 0) return NULL;
218 /* Allocate the ncfile structure */
219 ncf = calloc(1, sizeof(ncfile));
220 if (ncf == NULL) return NULL;
221 ncf->size = 0;
222 ncf->data = NULL;
223 ncf->fname = filename;
225 if (nc_load(ncf, fd) < 0) {
226 close(fd);
227 free(ncf);
228 return NULL;
230 close(fd);
231 return ncf;
235 void nc_freefile(ncfile *ncf)
237 if (ncf->data != NULL) free(ncf->data);
238 free(ncf);
240 /***********************************************************************/
242 void GetVBaseAndLimit(ncfile *ncf, uint32_t *vbase, uint32_t *vlimit) {
243 int ii;
244 uint32_t base = 0xffffffff;
245 uint32_t limit = 0;
247 for (ii = 0; ii < ncf->shnum; ii++) {
248 if ((ncf->sheaders[ii].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR) {
249 if (ncf->sheaders[ii].sh_addr < base) base = ncf->sheaders[ii].sh_addr;
250 if (ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size > limit)
251 limit = ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size;
254 *vbase = base;
255 *vlimit = limit;