2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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"
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?
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
)
85 char *cbuf
= (char *)buf
;
87 if (0 > lseek(fd
, at
, SEEK_SET
)) {
88 fprintf(stderr
, "readat: lseek failed\n");
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. */
95 nread
= read(fd
, &cbuf
[sofar
], sz
- sofar
);
97 fprintf(stderr
, "readat: read failed\n");
101 } while (sz
!= sofar
);
105 static int nc_load(ncfile
*ncf
, int fd
)
107 #define NCLOADFAIL(_args) \
109 fprintf(stderr, "nc_load(%s):", ncf->fname); \
110 fprintf _args ; return -1; \
115 size_t vmemlo
, vmemhi
, shsize
, phsize
;
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
]);
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
]);
137 if ((h
.e_flags
& EF_NACL_ALIGN_MASK
) == EF_NACL_ALIGN_16
) {
139 } else if ((h
.e_flags
& EF_NACL_ALIGN_MASK
) == EF_NACL_ALIGN_32
) {
142 fprintf(stderr
, "%s: bad align mask %x\n", ncf
->fname
,
143 (uint32_t)(h
.e_flags
& EF_NACL_ALIGN_MASK
));
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",
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. */
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
;
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"));
218 ncfile
*nc_loadfile(const char *filename
)
222 int rdflags
= O_RDONLY
;
224 rdflags
|= _O_BINARY
;
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
;
234 ncf
->fname
= filename
;
236 if (nc_load(ncf
, fd
) < 0) {
246 void nc_freefile(ncfile
*ncf
)
248 if (ncf
->data
!= NULL
) free(ncf
->data
);
251 /***********************************************************************/
253 void GetVBaseAndLimit(ncfile
*ncf
, uint32_t *vbase
, uint32_t *vlimit
) {
255 uint32_t base
= 0xffffffff;
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
;