xref: /minix/minix/lib/libexec/exec_elf.c (revision 125832e3)
1 #define _SYSTEM 1
2 
3 #include <minix/type.h>
4 #include <minix/const.h>
5 #include <minix/com.h>
6 #include <minix/syslib.h>
7 #include <sys/param.h>
8 #include <sys/mman.h>
9 #include <assert.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <libexec.h>
13 #include <string.h>
14 #include <machine/elf.h>
15 #include <machine/vmparam.h>
16 #include <machine/memory.h>
17 #include <minix/syslib.h>
18 
19 /* For verbose logging */
20 #define ELF_DEBUG 0
21 
22 /* Support only 32-bit ELF objects */
23 #define __ELF_WORD_SIZE 32
24 
25 #define SECTOR_SIZE 512
26 
27 static int check_header(Elf_Ehdr *hdr);
28 
elf_sane(Elf_Ehdr * hdr)29 static int elf_sane(Elf_Ehdr *hdr)
30 {
31   if (check_header(hdr) != OK) {
32      return 0;
33   }
34 
35   if((hdr->e_type != ET_EXEC) && (hdr->e_type != ET_DYN)) {
36      return 0;
37   }
38 
39   if ((hdr->e_phoff > SECTOR_SIZE) ||
40       (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > SECTOR_SIZE) {
41 #if ELF_DEBUG
42 	printf("libexec: peculiar phoff\n");
43 #endif
44      return 0;
45   }
46 
47   return 1;
48 }
49 
elf_ph_sane(Elf_Phdr * phdr)50 static int elf_ph_sane(Elf_Phdr *phdr)
51 {
52   if (rounddown((uintptr_t)phdr, sizeof(Elf_Addr)) != (uintptr_t)phdr) {
53      return 0;
54   }
55   return 1;
56 }
57 
elf_unpack(char * exec_hdr,size_t hdr_len,Elf_Ehdr ** hdr,Elf_Phdr ** phdr)58 static int elf_unpack(char *exec_hdr,
59 	size_t hdr_len, Elf_Ehdr **hdr, Elf_Phdr **phdr)
60 {
61   if(hdr_len < sizeof(Elf_Ehdr))
62 	return ENOEXEC;
63 
64   *hdr = (Elf_Ehdr *) exec_hdr;
65   if(!elf_sane(*hdr)) {
66   	return ENOEXEC;
67   }
68   *phdr = (Elf_Phdr *)(exec_hdr + (*hdr)->e_phoff);
69   if(!elf_ph_sane(*phdr)) {
70   	return ENOEXEC;
71   }
72 #if 0
73   if((int)((*phdr) + (*hdr)->e_phnum) >= hdr_len) return ENOEXEC;
74 #endif
75   return OK;
76 }
77 
78 #define IS_ELF(ehdr)	((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
79 			 (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
80 			 (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
81 			 (ehdr).e_ident[EI_MAG3] == ELFMAG3)
82 
check_header(Elf_Ehdr * hdr)83 static int check_header(Elf_Ehdr *hdr)
84 {
85   if (!IS_ELF(*hdr) ||
86       hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
87       hdr->e_ident[EI_VERSION] != EV_CURRENT ||
88       hdr->e_phentsize != sizeof(Elf_Phdr) ||
89       hdr->e_version != ELF_TARG_VER)
90       return ENOEXEC;
91 
92   return OK;
93 }
94 
95 /* Return >0 if there is an ELF interpreter (i.e. it is a dynamically linked
96  * executable) and we could extract it successfully.
97  * Return 0 if there isn't one.
98  * Return <0 on error.
99  */
elf_has_interpreter(char * exec_hdr,size_t hdr_len,char * interp,size_t maxsz)100 int elf_has_interpreter(char *exec_hdr,		/* executable header */
101 		size_t hdr_len, char *interp, size_t maxsz)
102 {
103   Elf_Ehdr *hdr = NULL;
104   Elf_Phdr *phdr = NULL;
105   int e, i;
106 
107   if((e=elf_unpack(exec_hdr, hdr_len, &hdr, &phdr)) != OK) return 0;
108 
109   for (i = 0; i < hdr->e_phnum; i++) {
110       switch (phdr[i].p_type) {
111       case PT_INTERP:
112       	  if(!interp) return 1;
113       	  if(phdr[i].p_filesz >= maxsz)
114 	  	return -1;
115 	  if(phdr[i].p_offset + phdr[i].p_filesz >= hdr_len)
116 	  	return -1;
117 	  memcpy(interp, exec_hdr + phdr[i].p_offset, phdr[i].p_filesz);
118 	  interp[phdr[i].p_filesz] = '\0';
119 	  return 1;
120       default:
121 	  continue;
122       }
123   }
124   return 0;
125 }
126 
libexec_load_elf(struct exec_info * execi)127 int libexec_load_elf(struct exec_info *execi)
128 {
129 	Elf_Ehdr *hdr = NULL;
130 	Elf_Phdr *phdr = NULL;
131 	int e, i = 0;
132 	int first = 1;
133 	vir_bytes startv = 0, stacklow;
134 
135 	assert(execi != NULL);
136 	assert(execi->hdr != NULL);
137 
138 	if((e=elf_unpack(execi->hdr, execi->hdr_len, &hdr, &phdr)) != OK) {
139 		return e;
140 	 }
141 
142 	/* this function can load the dynamic linker, but that
143 	 * shouldn't require an interpreter itself.
144 	 */
145 	i = elf_has_interpreter(execi->hdr, execi->hdr_len, NULL, 0);
146 	if(i > 0) {
147 	      return ENOEXEC;
148 	}
149 
150 	execi->stack_size = roundup(execi->stack_size, PAGE_SIZE);
151 	execi->stack_high = rounddown(execi->stack_high, PAGE_SIZE);
152 	stacklow = execi->stack_high - execi->stack_size;
153 
154 	assert(execi->copymem);
155 	assert(execi->clearmem);
156 	assert(execi->allocmem_prealloc_cleared);
157 	assert(execi->allocmem_prealloc_junk);
158 	assert(execi->allocmem_ondemand);
159 
160 	for (i = 0; i < hdr->e_phnum; i++) {
161 		Elf_Phdr *ph = &phdr[i];
162 		off_t file_limit = ph->p_offset + ph->p_filesz;
163 		/* sanity check binary before wiping out the target process */
164 		if(execi->filesize < file_limit) {
165 			return ENOEXEC;
166 		}
167 	}
168 
169 	if(execi->clearproc) execi->clearproc(execi);
170 
171 	for (i = 0; i < hdr->e_phnum; i++) {
172 		vir_bytes seg_membytes, page_offset, p_vaddr, vaddr;
173 		vir_bytes chunk, vfileend, vmemend;
174 		off_t foffset, fbytes;
175 		Elf_Phdr *ph = &phdr[i];
176 		int try_mmap = 1;
177 		u16_t clearend = 0;
178 		int pagechunk;
179 		int mmap_prot = PROT_READ;
180 
181 #if ELF_DEBUG
182 			printf("libexec: -------------------\n");
183 			printf("libexec: phdr %x (%d)\n", (uint32_t)ph, i);
184 #endif
185 		if(!(ph->p_flags & PF_R)) {
186 			printf("libexec: warning: unreadable segment\n");
187 		}
188 
189 		if(ph->p_flags & PF_W) {
190 			mmap_prot |= PROT_WRITE;
191 #if ELF_DEBUG
192 			printf("libexec: adding PROT_WRITE\n");
193 #endif
194 		} else {
195 #if ELF_DEBUG
196 			printf("libexec: not adding PROT_WRITE\n");
197 #endif
198 		}
199 
200 		if (ph->p_type != PT_LOAD || ph->p_memsz == 0) continue;
201 
202 		if((ph->p_vaddr % PAGE_SIZE) != (ph->p_offset % PAGE_SIZE)) {
203 			printf("libexec: unaligned ELF program?\n");
204 			try_mmap = 0;
205 		}
206 
207 		if(!execi->memmap) {
208 			try_mmap = 0;
209 		}
210 
211 		foffset = ph->p_offset;
212 		fbytes = ph->p_filesz;
213 		vaddr = p_vaddr = ph->p_vaddr + execi->load_offset;
214 		seg_membytes = ph->p_memsz;
215 
216 		page_offset = vaddr % PAGE_SIZE;
217 		vaddr -= page_offset;
218 		foffset -= page_offset;
219 		seg_membytes += page_offset;
220 		fbytes += page_offset;
221 		vfileend  = p_vaddr + ph->p_filesz;
222 
223 		/* if there's usable memory after the file end, we have
224 		 * to tell VM to clear the memory part of the page when it's
225 		 * mapped in
226 		 */
227 		if((pagechunk = (vfileend % PAGE_SIZE))
228 			&& ph->p_filesz < ph->p_memsz) {
229 			clearend = PAGE_SIZE - pagechunk;
230 		}
231 
232 		seg_membytes = roundup(seg_membytes, PAGE_SIZE);
233 		fbytes = roundup(fbytes, PAGE_SIZE);
234 
235 		if(first || startv > vaddr) startv = vaddr;
236 		first = 0;
237 
238 		if ((ph->p_flags & PF_X) != 0 && execi->text_size < seg_membytes)
239 			execi->text_size = seg_membytes;
240 		else
241 			execi->data_size = seg_membytes;
242 
243 		if(try_mmap && execi->memmap(execi, vaddr, fbytes, foffset, clearend, mmap_prot) == OK) {
244 #if ELF_DEBUG
245 			printf("libexec: mmap 0x%lx-0x%llx done, clearend 0x%x\n",
246 				vaddr, vaddr+fbytes, clearend);
247 #endif
248 
249 			if(seg_membytes > fbytes) {
250 				int rem_mem = seg_membytes - fbytes;;
251 				vir_bytes remstart = vaddr + fbytes;
252 				if(execi->allocmem_ondemand(execi,
253 					remstart, rem_mem) != OK) {
254 					printf("libexec: mmap extra mem failed\n");
255 					return ENOMEM;
256 				}
257 #if ELF_DEBUG
258 				else printf("libexec: allocated 0x%lx-0x%lx\n",
259 
260 					remstart, remstart+rem_mem);
261 #endif
262 			}
263 		} else {
264 			/* make us some memory */
265 			if(execi->allocmem_prealloc_junk(execi, vaddr, seg_membytes) != OK) {
266 				if(execi->clearproc) execi->clearproc(execi);
267 				return ENOMEM;
268 			}
269 
270 #if ELF_DEBUG
271 			printf("libexec: mmapped 0x%lx-0x%lx\n", vaddr, vaddr+seg_membytes);
272 #endif
273 
274 			/* Copy executable section into it */
275 			if(execi->copymem(execi, ph->p_offset, p_vaddr, ph->p_filesz) != OK) {
276 				if(execi->clearproc) execi->clearproc(execi);
277 				return ENOMEM;
278 			}
279 
280 #if ELF_DEBUG
281 			printf("libexec: copied 0x%lx-0x%lx\n", p_vaddr, p_vaddr+ph->p_filesz);
282 #endif
283 
284 			/* Clear remaining bits */
285 			vmemend = vaddr + seg_membytes;
286 			if((chunk = p_vaddr - vaddr) > 0) {
287 #if ELF_DEBUG
288 				printf("libexec: start clearing 0x%lx-0x%lx\n", vaddr, vaddr+chunk);
289 #endif
290 				execi->clearmem(execi, vaddr, chunk);
291 			}
292 
293 			if((chunk = vmemend - vfileend) > 0) {
294 #if ELF_DEBUG
295 				printf("libexec: end clearing 0x%lx-0x%lx\n", vfileend, vfileend+chunk);
296 #endif
297 				execi->clearmem(execi, vfileend, chunk);
298 			}
299 		}
300 	}
301 
302 	/* Make it a stack */
303 	if(execi->allocmem_ondemand(execi, stacklow, execi->stack_size) != OK) {
304 		if(execi->clearproc) execi->clearproc(execi);
305 		return ENOMEM;
306 	}
307 
308 #if ELF_DEBUG
309 	printf("libexec: stack mmapped 0x%lx-0x%lx\n", stacklow, stacklow+execi->stack_size);
310 #endif
311 
312 	/* record entry point and lowest load vaddr for caller */
313 	execi->pc = hdr->e_entry + execi->load_offset;
314 	execi->load_base = startv;
315 
316 	return OK;
317 }
318 
319