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.
47 #include <sys/types.h>
49 #include "native_client/include/portability.h"
50 #include "native_client/ncv/ncfileutil.h"
52 /* This module is intended for testing use only, not for production use */
53 /* in sel_ldr. To prevent unintended production usage, define a symbol */
54 /* that will cause a load-time error for sel_ldr. */
55 int gNaClValidateImage_foo
= 0;
56 void NaClValidateImage() { gNaClValidateImage_foo
+= 1; }
59 /* redefine INLINE for non-Windows platforms */
63 static INLINE
uint32_t NCPageTrunc(uint32_t v
) {
64 return (v
& ~(kNCFileUtilPageSize
- 1));
67 static INLINE
uint32_t NCPageRound(uint32_t v
) {
68 return(NCPageTrunc(v
+ (kNCFileUtilPageSize
- 1)));
71 /***********************************************************************/
72 /* THIS ROUTINE IS FOR DEBUGGING/TESTING ONLY, NOT FOR SECURE RUNTIME */
73 /* ALL PAGES ARE LEFT WRITEABLE BY THIS LOADER. */
74 /***********************************************************************/
75 /* Loading a NC executable from a host file */
76 static int readat(const int fd
, void *buf
, const size_t sz
, const size_t at
)
80 char *cbuf
= (char *)buf
;
82 if (0 > lseek(fd
, at
, SEEK_SET
)) {
83 fprintf(stderr
, "readat: lseek failed\n");
86 /* Strangely this loop is needed on Windows. It seems the read() */
87 /* implementation doesn't always return as many bytes as requested */
88 /* so you have to keep on trying. */
90 nread
= read(fd
, &cbuf
[sofar
], sz
- sofar
);
92 fprintf(stderr
, "readat: read failed\n");
96 } while (sz
!= sofar
);
100 static int nc_load(ncfile
*ncf
, int fd
)
102 #define NCLOADFAIL(_args) \
104 fprintf(stderr, "nc_load(%s):", ncf->fname); \
105 fprintf _args ; return -1; \
110 size_t vmemlo
, vmemhi
, shsize
, phsize
;
113 /* Read and check the ELF header */
114 nread
= readat(fd
, &h
, sizeof(h
), 0);
115 if (nread
< 0 || (size_t) nread
< sizeof(h
)) {
116 NCLOADFAIL((stderr
, "could not read ELF header\n"));
119 /* do a bunch of sanity checks */
120 if (strncmp((char *)h
.e_ident
, ELFMAG
, strlen(ELFMAG
))) {
121 NCLOADFAIL((stderr
, "bad magic number\n"));
123 if (h
.e_ident
[EI_OSABI
] != ELFOSABI_NACL
) {
124 fprintf(stderr
, "%s: bad OS ABI %x\n", ncf
->fname
, h
.e_ident
[EI_OSABI
]);
127 if (h
.e_ident
[EI_ABIVERSION
] != EF_NACL_ABIVERSION
) {
128 fprintf(stderr
, "%s: bad ABI version %d\n", ncf
->fname
,
129 h
.e_ident
[EI_ABIVERSION
]);
132 if ((h
.e_flags
& EF_NACL_ALIGN_MASK
) == EF_NACL_ALIGN_16
) {
134 } else if ((h
.e_flags
& EF_NACL_ALIGN_MASK
) == EF_NACL_ALIGN_32
) {
137 fprintf(stderr
, "%s: bad align mask %x\n", ncf
->fname
,
138 (uint32_t)(h
.e_flags
& EF_NACL_ALIGN_MASK
));
143 /* Read the program header table */
144 if (h
.e_phnum
<= 0 || h
.e_phnum
> kMaxPhnum
) {
145 NCLOADFAIL((stderr
, "h.e_phnum %d > kMaxPhnum %d\n",
146 h
.e_phnum
, kMaxPhnum
));
148 ncf
->phnum
= h
.e_phnum
;
149 ncf
->pheaders
= (Elf32_Phdr
*)calloc(h
.e_phnum
, sizeof(Elf32_Phdr
));
150 if (NULL
== ncf
->pheaders
) {
151 NCLOADFAIL((stderr
, "calloc(%d, %"PRIdS
") failed\n",
153 sizeof(Elf32_Phdr
)));
155 phsize
= h
.e_phnum
* sizeof(*ncf
->pheaders
);
156 nread
= readat(fd
, ncf
->pheaders
, phsize
, h
.e_phoff
);
157 if (nread
< 0 || (size_t) nread
< phsize
) return -1;
159 /* Iterate through the program headers to find the virtual */
160 /* size of loaded text. */
163 for (i
= 0; i
< h
.e_phnum
; i
++) {
164 if (ncf
->pheaders
[i
].p_type
!= PT_LOAD
) continue;
165 if (0 == (ncf
->pheaders
[i
].p_flags
& PF_X
)) continue;
166 /* This is executable text. Check low and high addrs */
167 if (vmemlo
> ncf
->pheaders
[i
].p_vaddr
) vmemlo
= ncf
->pheaders
[i
].p_vaddr
;
168 if (vmemhi
< ncf
->pheaders
[i
].p_vaddr
+ ncf
->pheaders
[i
].p_memsz
) {
169 vmemhi
= ncf
->pheaders
[i
].p_vaddr
+ ncf
->pheaders
[i
].p_memsz
;
172 vmemhi
= NCPageRound(vmemhi
);
173 ncf
->size
= vmemhi
- vmemlo
;
175 if (vmemlo
!= NCPageTrunc(vmemlo
)) {
176 NCLOADFAIL((stderr
, "vmemlo is not aligned\n"));
178 ncf
->data
= (uint8_t *)calloc(1, ncf
->size
);
179 if (NULL
== ncf
->data
) {
180 NCLOADFAIL((stderr
, "calloc(1, %d) failed\n", (int)ncf
->size
));
183 /* Load program text segments */
184 for (i
= 0; i
< h
.e_phnum
; i
++) {
185 const Elf32_Phdr
*p
= &ncf
->pheaders
[i
];
186 if (p
->p_type
!= PT_LOAD
) continue;
187 if (0 == (ncf
->pheaders
[i
].p_flags
& PF_X
)) continue;
189 assert(ncf
->size
>= NCPageRound(p
->p_vaddr
- ncf
->vbase
+ p
->p_memsz
));
190 nread
= readat(fd
, &(ncf
->data
[p
->p_vaddr
- ncf
->vbase
]),
191 p
->p_filesz
, p
->p_offset
);
192 if (nread
< 0 || (size_t) nread
< p
->p_filesz
) {
193 NCLOADFAIL((stderr
, "could not read segment %d (%d < %d)\n",
194 i
, (int)nread
, p
->p_filesz
));
197 /* load the section headers */
198 ncf
->shnum
= h
.e_shnum
;
199 shsize
= ncf
->shnum
* sizeof(*ncf
->sheaders
);
200 ncf
->sheaders
= (Elf32_Shdr
*)calloc(1, shsize
);
201 if (NULL
== ncf
->sheaders
) {
202 NCLOADFAIL((stderr
, "calloc(1, %d) failed\n", (int)shsize
));
204 nread
= readat(fd
, ncf
->sheaders
, shsize
, h
.e_shoff
);
205 if (nread
< 0 || (size_t) nread
< shsize
) {
206 NCLOADFAIL((stderr
, "could not read section headers\n"));
213 ncfile
*nc_loadfile(const char *filename
)
217 int rdflags
= O_RDONLY
;
219 rdflags
|= _O_BINARY
;
221 fd
= open(filename
, rdflags
);
222 if (fd
< 0) return NULL
;
224 /* Allocate the ncfile structure */
225 ncf
= calloc(1, sizeof(ncfile
));
226 if (ncf
== NULL
) return NULL
;
229 ncf
->fname
= filename
;
231 if (nc_load(ncf
, fd
) < 0) {
241 void nc_freefile(ncfile
*ncf
)
243 if (ncf
->data
!= NULL
) free(ncf
->data
);
246 /***********************************************************************/
248 void GetVBaseAndLimit(ncfile
*ncf
, uint32_t *vbase
, uint32_t *vlimit
) {
250 uint32_t base
= 0xffffffff;
253 for (ii
= 0; ii
< ncf
->shnum
; ii
++) {
254 if ((ncf
->sheaders
[ii
].sh_flags
& SHF_EXECINSTR
) == SHF_EXECINSTR
) {
255 if (ncf
->sheaders
[ii
].sh_addr
< base
) base
= ncf
->sheaders
[ii
].sh_addr
;
256 if (ncf
->sheaders
[ii
].sh_addr
+ ncf
->sheaders
[ii
].sh_size
> limit
)
257 limit
= ncf
->sheaders
[ii
].sh_addr
+ ncf
->sheaders
[ii
].sh_size
;