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"
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; }
61 /* redefine INLINE for non-Windows platforms */
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
)
82 char *cbuf
= (char *)buf
;
84 if (0 > lseek(fd
, at
, SEEK_SET
)) {
85 fprintf(stderr
, "readat: lseek failed\n");
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. */
92 nread
= read(fd
, &cbuf
[sofar
], sz
- sofar
);
94 fprintf(stderr
, "readat: read failed\n");
98 } while (sz
!= sofar
);
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; }
109 Elf32_Phdr ph
[kMaxPhnum
];
111 size_t vmemlo
, vmemhi
, shsize
, phsize
;
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
]);
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
]);
133 if ((h
.e_flags
& EF_NACL_ALIGN_MASK
) == EF_NACL_ALIGN_16
) {
135 } else if ((h
.e_flags
& EF_NACL_ALIGN_MASK
) == EF_NACL_ALIGN_32
) {
138 fprintf(stderr
, "%s: bad align mask %x\n", ncf
->fname
,
139 (uint32_t)(h
.e_flags
& EF_NACL_ALIGN_MASK
));
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. */
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
;
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"));
207 ncfile
*nc_loadfile(const char *filename
)
211 int rdflags
= O_RDONLY
;
213 rdflags
|= _O_BINARY
;
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
;
223 ncf
->fname
= filename
;
225 if (nc_load(ncf
, fd
) < 0) {
235 void nc_freefile(ncfile
*ncf
)
237 if (ncf
->data
!= NULL
) free(ncf
->data
);
240 /***********************************************************************/
242 void GetVBaseAndLimit(ncfile
*ncf
, uint32_t *vbase
, uint32_t *vlimit
) {
244 uint32_t base
= 0xffffffff;
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
;