Debugging: Add code to print backtrace for guest on SIGSEGV
[nativeclient.git] / ncv / ncfileutil.c
blobbc7cf895129af54c70feb53aeae837c587b325e6
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 "native_client/include/portability.h"
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <assert.h>
42 * TODO: move to portability.h and define an OPEN portability macro?
43 * Drawback is that portability.h becomes a black hole of includes
44 * that every source file includes but contains includes that are
45 * mostly not needed, unnecessarily increasing compile time (even w/
46 * pre-compiled headers) and obfuscating what system feature each
47 * source file really needs. Perhaps port/open.h, port/foo.h for
48 * declarations associated with needing foo?
50 #if NACL_WINDOWS
51 #include <io.h>
52 #define open _open
53 #else
54 #include <unistd.h>
55 #endif
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <sys/types.h>
60 #include "native_client/ncv/ncfileutil.h"
62 /* This module is intended for testing use only, not for production use */
63 /* in sel_ldr. To prevent unintended production usage, define a symbol */
64 /* that will cause a load-time error for sel_ldr. */
65 int gNaClValidateImage_foo = 0;
66 void NaClValidateImage() { gNaClValidateImage_foo += 1; }
68 static INLINE uint32_t NCPageTrunc(uint32_t v) {
69 return (v & ~(kNCFileUtilPageSize - 1));
72 static INLINE uint32_t NCPageRound(uint32_t v) {
73 return(NCPageTrunc(v + (kNCFileUtilPageSize - 1)));
76 /***********************************************************************/
77 /* THIS ROUTINE IS FOR DEBUGGING/TESTING ONLY, NOT FOR SECURE RUNTIME */
78 /* ALL PAGES ARE LEFT WRITEABLE BY THIS LOADER. */
79 /***********************************************************************/
80 /* Loading a NC executable from a host file */
81 static int readat(const int fd, void *buf, const size_t sz, const size_t at)
83 size_t sofar = 0;
84 int nread;
85 char *cbuf = (char *)buf;
87 if (0 > lseek(fd, at, SEEK_SET)) {
88 fprintf(stderr, "readat: lseek failed\n");
89 return -1;
91 /* Strangely this loop is needed on Windows. It seems the read() */
92 /* implementation doesn't always return as many bytes as requested */
93 /* so you have to keep on trying. */
94 do {
95 nread = read(fd, &cbuf[sofar], sz - sofar);
96 if (nread <= 0) {
97 fprintf(stderr, "readat: read failed\n");
98 return -1;
100 sofar += nread;
101 } while (sz != sofar);
102 return nread;
105 static int nc_load(ncfile *ncf, int fd)
107 #define NCLOADFAIL(_args) \
108 do { \
109 fprintf(stderr, "nc_load(%s):", ncf->fname); \
110 fprintf _args ; return -1; \
111 } while (0)
113 Elf32_Ehdr h;
114 ssize_t nread;
115 size_t vmemlo, vmemhi, shsize, phsize;
116 int i;
118 /* Read and check the ELF header */
119 nread = readat(fd, &h, sizeof(h), 0);
120 if (nread < 0 || (size_t) nread < sizeof(h)) {
121 NCLOADFAIL((stderr, "could not read ELF header\n"));
124 /* do a bunch of sanity checks */
125 if (strncmp((char *)h.e_ident, ELFMAG, strlen(ELFMAG))) {
126 NCLOADFAIL((stderr, "bad magic number\n"));
128 if (h.e_ident[EI_OSABI] != ELFOSABI_NACL) {
129 fprintf(stderr, "%s: bad OS ABI %x\n", ncf->fname, h.e_ident[EI_OSABI]);
130 /* return; */
132 if (h.e_ident[EI_ABIVERSION] != EF_NACL_ABIVERSION) {
133 fprintf(stderr, "%s: bad ABI version %d\n", ncf->fname,
134 h.e_ident[EI_ABIVERSION]);
135 /* return; */
137 if ((h.e_flags & EF_NACL_ALIGN_MASK) == EF_NACL_ALIGN_16) {
138 ncf->ncalign = 16;
139 } else if ((h.e_flags & EF_NACL_ALIGN_MASK) == EF_NACL_ALIGN_32) {
140 ncf->ncalign = 32;
141 } else {
142 fprintf(stderr, "%s: bad align mask %x\n", ncf->fname,
143 (uint32_t)(h.e_flags & EF_NACL_ALIGN_MASK));
144 ncf->ncalign = 16;
145 /* return; */
148 /* Read the program header table */
149 if (h.e_phnum <= 0 || h.e_phnum > kMaxPhnum) {
150 NCLOADFAIL((stderr, "h.e_phnum %d > kMaxPhnum %d\n",
151 h.e_phnum, kMaxPhnum));
153 ncf->phnum = h.e_phnum;
154 ncf->pheaders = (Elf32_Phdr *)calloc(h.e_phnum, sizeof(Elf32_Phdr));
155 if (NULL == ncf->pheaders) {
156 NCLOADFAIL((stderr, "calloc(%d, %"PRIdS") failed\n",
157 h.e_phnum,
158 sizeof(Elf32_Phdr)));
160 phsize = h.e_phnum * sizeof(*ncf->pheaders);
161 nread = readat(fd, ncf->pheaders, phsize, h.e_phoff);
162 if (nread < 0 || (size_t) nread < phsize) return -1;
164 /* Iterate through the program headers to find the virtual */
165 /* size of loaded text. */
166 vmemlo = 0xffffffff;
167 vmemhi = 0;
168 for (i = 0; i < h.e_phnum; i++) {
169 if (ncf->pheaders[i].p_type != PT_LOAD) continue;
170 if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
171 /* This is executable text. Check low and high addrs */
172 if (vmemlo > ncf->pheaders[i].p_vaddr) vmemlo = ncf->pheaders[i].p_vaddr;
173 if (vmemhi < ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz) {
174 vmemhi = ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz;
177 vmemhi = NCPageRound(vmemhi);
178 ncf->size = vmemhi - vmemlo;
179 ncf->vbase = vmemlo;
180 if (vmemlo != NCPageTrunc(vmemlo)) {
181 NCLOADFAIL((stderr, "vmemlo is not aligned\n"));
183 ncf->data = (uint8_t *)calloc(1, ncf->size);
184 if (NULL == ncf->data) {
185 NCLOADFAIL((stderr, "calloc(1, %d) failed\n", (int)ncf->size));
188 /* Load program text segments */
189 for (i = 0; i < h.e_phnum; i++) {
190 const Elf32_Phdr *p = &ncf->pheaders[i];
191 if (p->p_type != PT_LOAD) continue;
192 if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
194 assert(ncf->size >= NCPageRound(p->p_vaddr - ncf->vbase + p->p_memsz));
195 nread = readat(fd, &(ncf->data[p->p_vaddr - ncf->vbase]),
196 p->p_filesz, p->p_offset);
197 if (nread < 0 || (size_t) nread < p->p_filesz) {
198 NCLOADFAIL((stderr, "could not read segment %d (%d < %d)\n",
199 i, (int)nread, p->p_filesz));
202 /* load the section headers */
203 ncf->shnum = h.e_shnum;
204 shsize = ncf->shnum * sizeof(*ncf->sheaders);
205 ncf->sheaders = (Elf32_Shdr *)calloc(1, shsize);
206 if (NULL == ncf->sheaders) {
207 NCLOADFAIL((stderr, "calloc(1, %d) failed\n", (int)shsize));
209 nread = readat(fd, ncf->sheaders, shsize, h.e_shoff);
210 if (nread < 0 || (size_t) nread < shsize) {
211 NCLOADFAIL((stderr, "could not read section headers\n"));
214 /* success! */
215 return 0;
218 ncfile *nc_loadfile(const char *filename)
220 ncfile *ncf;
221 int fd;
222 int rdflags = O_RDONLY;
223 #if NACL_WINDOWS
224 rdflags |= _O_BINARY;
225 #endif
226 fd = open(filename, rdflags);
227 if (fd < 0) return NULL;
229 /* Allocate the ncfile structure */
230 ncf = calloc(1, sizeof(ncfile));
231 if (ncf == NULL) return NULL;
232 ncf->size = 0;
233 ncf->data = NULL;
234 ncf->fname = filename;
236 if (nc_load(ncf, fd) < 0) {
237 close(fd);
238 free(ncf);
239 return NULL;
241 close(fd);
242 return ncf;
246 void nc_freefile(ncfile *ncf)
248 if (ncf->data != NULL) free(ncf->data);
249 free(ncf);
251 /***********************************************************************/
253 void GetVBaseAndLimit(ncfile *ncf, uint32_t *vbase, uint32_t *vlimit) {
254 int ii;
255 uint32_t base = 0xffffffff;
256 uint32_t limit = 0;
258 for (ii = 0; ii < ncf->shnum; ii++) {
259 if ((ncf->sheaders[ii].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR) {
260 if (ncf->sheaders[ii].sh_addr < base) base = ncf->sheaders[ii].sh_addr;
261 if (ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size > limit)
262 limit = ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size;
265 *vbase = base;
266 *vlimit = limit;