xref: /minix3/minix/lib/libexec/exec_elf.c (revision 125832e358a3620ce03dc9aa9171fa7ca390e057)
1433d6423SLionel Sambuc #define _SYSTEM 1
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc #include <minix/type.h>
4433d6423SLionel Sambuc #include <minix/const.h>
5433d6423SLionel Sambuc #include <minix/com.h>
6433d6423SLionel Sambuc #include <minix/syslib.h>
7433d6423SLionel Sambuc #include <sys/param.h>
8433d6423SLionel Sambuc #include <sys/mman.h>
9433d6423SLionel Sambuc #include <assert.h>
10433d6423SLionel Sambuc #include <unistd.h>
11433d6423SLionel Sambuc #include <errno.h>
12433d6423SLionel Sambuc #include <libexec.h>
13433d6423SLionel Sambuc #include <string.h>
14433d6423SLionel Sambuc #include <machine/elf.h>
15433d6423SLionel Sambuc #include <machine/vmparam.h>
16433d6423SLionel Sambuc #include <machine/memory.h>
17433d6423SLionel Sambuc #include <minix/syslib.h>
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc /* For verbose logging */
20433d6423SLionel Sambuc #define ELF_DEBUG 0
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /* Support only 32-bit ELF objects */
23433d6423SLionel Sambuc #define __ELF_WORD_SIZE 32
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc #define SECTOR_SIZE 512
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc static int check_header(Elf_Ehdr *hdr);
28433d6423SLionel Sambuc 
elf_sane(Elf_Ehdr * hdr)29433d6423SLionel Sambuc static int elf_sane(Elf_Ehdr *hdr)
30433d6423SLionel Sambuc {
31433d6423SLionel Sambuc   if (check_header(hdr) != OK) {
32433d6423SLionel Sambuc      return 0;
33433d6423SLionel Sambuc   }
34433d6423SLionel Sambuc 
35433d6423SLionel Sambuc   if((hdr->e_type != ET_EXEC) && (hdr->e_type != ET_DYN)) {
36433d6423SLionel Sambuc      return 0;
37433d6423SLionel Sambuc   }
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc   if ((hdr->e_phoff > SECTOR_SIZE) ||
40433d6423SLionel Sambuc       (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > SECTOR_SIZE) {
41433d6423SLionel Sambuc #if ELF_DEBUG
42*125832e3SLionel Sambuc 	printf("libexec: peculiar phoff\n");
43433d6423SLionel Sambuc #endif
44433d6423SLionel Sambuc      return 0;
45433d6423SLionel Sambuc   }
46433d6423SLionel Sambuc 
47433d6423SLionel Sambuc   return 1;
48433d6423SLionel Sambuc }
49433d6423SLionel Sambuc 
elf_ph_sane(Elf_Phdr * phdr)50433d6423SLionel Sambuc static int elf_ph_sane(Elf_Phdr *phdr)
51433d6423SLionel Sambuc {
52433d6423SLionel Sambuc   if (rounddown((uintptr_t)phdr, sizeof(Elf_Addr)) != (uintptr_t)phdr) {
53433d6423SLionel Sambuc      return 0;
54433d6423SLionel Sambuc   }
55433d6423SLionel Sambuc   return 1;
56433d6423SLionel Sambuc }
57433d6423SLionel Sambuc 
elf_unpack(char * exec_hdr,size_t hdr_len,Elf_Ehdr ** hdr,Elf_Phdr ** phdr)58433d6423SLionel Sambuc static int elf_unpack(char *exec_hdr,
5965f76edbSDavid van Moolenbroek 	size_t hdr_len, Elf_Ehdr **hdr, Elf_Phdr **phdr)
60433d6423SLionel Sambuc {
616f7ac603SLionel Sambuc   if(hdr_len < sizeof(Elf_Ehdr))
626f7ac603SLionel Sambuc 	return ENOEXEC;
6337f29f55SLionel Sambuc 
64433d6423SLionel Sambuc   *hdr = (Elf_Ehdr *) exec_hdr;
65433d6423SLionel Sambuc   if(!elf_sane(*hdr)) {
66433d6423SLionel Sambuc   	return ENOEXEC;
67433d6423SLionel Sambuc   }
68433d6423SLionel Sambuc   *phdr = (Elf_Phdr *)(exec_hdr + (*hdr)->e_phoff);
69433d6423SLionel Sambuc   if(!elf_ph_sane(*phdr)) {
70433d6423SLionel Sambuc   	return ENOEXEC;
71433d6423SLionel Sambuc   }
72433d6423SLionel Sambuc #if 0
73433d6423SLionel Sambuc   if((int)((*phdr) + (*hdr)->e_phnum) >= hdr_len) return ENOEXEC;
74433d6423SLionel Sambuc #endif
75433d6423SLionel Sambuc   return OK;
76433d6423SLionel Sambuc }
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc #define IS_ELF(ehdr)	((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
79433d6423SLionel Sambuc 			 (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
80433d6423SLionel Sambuc 			 (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
81433d6423SLionel Sambuc 			 (ehdr).e_ident[EI_MAG3] == ELFMAG3)
82433d6423SLionel Sambuc 
check_header(Elf_Ehdr * hdr)83433d6423SLionel Sambuc static int check_header(Elf_Ehdr *hdr)
84433d6423SLionel Sambuc {
85433d6423SLionel Sambuc   if (!IS_ELF(*hdr) ||
86433d6423SLionel Sambuc       hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
87433d6423SLionel Sambuc       hdr->e_ident[EI_VERSION] != EV_CURRENT ||
88433d6423SLionel Sambuc       hdr->e_phentsize != sizeof(Elf_Phdr) ||
89433d6423SLionel Sambuc       hdr->e_version != ELF_TARG_VER)
90433d6423SLionel Sambuc       return ENOEXEC;
91433d6423SLionel Sambuc 
92433d6423SLionel Sambuc   return OK;
93433d6423SLionel Sambuc }
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc /* Return >0 if there is an ELF interpreter (i.e. it is a dynamically linked
96433d6423SLionel Sambuc  * executable) and we could extract it successfully.
97433d6423SLionel Sambuc  * Return 0 if there isn't one.
98433d6423SLionel Sambuc  * Return <0 on error.
99433d6423SLionel Sambuc  */
elf_has_interpreter(char * exec_hdr,size_t hdr_len,char * interp,size_t maxsz)100433d6423SLionel Sambuc int elf_has_interpreter(char *exec_hdr,		/* executable header */
10165f76edbSDavid van Moolenbroek 		size_t hdr_len, char *interp, size_t maxsz)
102433d6423SLionel Sambuc {
103433d6423SLionel Sambuc   Elf_Ehdr *hdr = NULL;
104433d6423SLionel Sambuc   Elf_Phdr *phdr = NULL;
105433d6423SLionel Sambuc   int e, i;
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc   if((e=elf_unpack(exec_hdr, hdr_len, &hdr, &phdr)) != OK) return 0;
108433d6423SLionel Sambuc 
109433d6423SLionel Sambuc   for (i = 0; i < hdr->e_phnum; i++) {
110433d6423SLionel Sambuc       switch (phdr[i].p_type) {
111433d6423SLionel Sambuc       case PT_INTERP:
112433d6423SLionel Sambuc       	  if(!interp) return 1;
113433d6423SLionel Sambuc       	  if(phdr[i].p_filesz >= maxsz)
114433d6423SLionel Sambuc 	  	return -1;
115433d6423SLionel Sambuc 	  if(phdr[i].p_offset + phdr[i].p_filesz >= hdr_len)
116433d6423SLionel Sambuc 	  	return -1;
117433d6423SLionel Sambuc 	  memcpy(interp, exec_hdr + phdr[i].p_offset, phdr[i].p_filesz);
118433d6423SLionel Sambuc 	  interp[phdr[i].p_filesz] = '\0';
119433d6423SLionel Sambuc 	  return 1;
120433d6423SLionel Sambuc       default:
121433d6423SLionel Sambuc 	  continue;
122433d6423SLionel Sambuc       }
123433d6423SLionel Sambuc   }
124433d6423SLionel Sambuc   return 0;
125433d6423SLionel Sambuc }
126433d6423SLionel Sambuc 
libexec_load_elf(struct exec_info * execi)127433d6423SLionel Sambuc int libexec_load_elf(struct exec_info *execi)
128433d6423SLionel Sambuc {
129433d6423SLionel Sambuc 	Elf_Ehdr *hdr = NULL;
130433d6423SLionel Sambuc 	Elf_Phdr *phdr = NULL;
131433d6423SLionel Sambuc 	int e, i = 0;
132433d6423SLionel Sambuc 	int first = 1;
133433d6423SLionel Sambuc 	vir_bytes startv = 0, stacklow;
134433d6423SLionel Sambuc 
135433d6423SLionel Sambuc 	assert(execi != NULL);
136433d6423SLionel Sambuc 	assert(execi->hdr != NULL);
137433d6423SLionel Sambuc 
138433d6423SLionel Sambuc 	if((e=elf_unpack(execi->hdr, execi->hdr_len, &hdr, &phdr)) != OK) {
139433d6423SLionel Sambuc 		return e;
140433d6423SLionel Sambuc 	 }
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 	/* this function can load the dynamic linker, but that
143433d6423SLionel Sambuc 	 * shouldn't require an interpreter itself.
144433d6423SLionel Sambuc 	 */
145433d6423SLionel Sambuc 	i = elf_has_interpreter(execi->hdr, execi->hdr_len, NULL, 0);
146433d6423SLionel Sambuc 	if(i > 0) {
147433d6423SLionel Sambuc 	      return ENOEXEC;
148433d6423SLionel Sambuc 	}
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc 	execi->stack_size = roundup(execi->stack_size, PAGE_SIZE);
151433d6423SLionel Sambuc 	execi->stack_high = rounddown(execi->stack_high, PAGE_SIZE);
152433d6423SLionel Sambuc 	stacklow = execi->stack_high - execi->stack_size;
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc 	assert(execi->copymem);
155433d6423SLionel Sambuc 	assert(execi->clearmem);
156433d6423SLionel Sambuc 	assert(execi->allocmem_prealloc_cleared);
157433d6423SLionel Sambuc 	assert(execi->allocmem_prealloc_junk);
158433d6423SLionel Sambuc 	assert(execi->allocmem_ondemand);
159433d6423SLionel Sambuc 
160433d6423SLionel Sambuc 	for (i = 0; i < hdr->e_phnum; i++) {
161433d6423SLionel Sambuc 		Elf_Phdr *ph = &phdr[i];
162433d6423SLionel Sambuc 		off_t file_limit = ph->p_offset + ph->p_filesz;
163433d6423SLionel Sambuc 		/* sanity check binary before wiping out the target process */
164433d6423SLionel Sambuc 		if(execi->filesize < file_limit) {
165433d6423SLionel Sambuc 			return ENOEXEC;
166433d6423SLionel Sambuc 		}
167433d6423SLionel Sambuc 	}
168433d6423SLionel Sambuc 
169433d6423SLionel Sambuc 	if(execi->clearproc) execi->clearproc(execi);
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc 	for (i = 0; i < hdr->e_phnum; i++) {
172433d6423SLionel Sambuc 		vir_bytes seg_membytes, page_offset, p_vaddr, vaddr;
173433d6423SLionel Sambuc 		vir_bytes chunk, vfileend, vmemend;
174433d6423SLionel Sambuc 		off_t foffset, fbytes;
175433d6423SLionel Sambuc 		Elf_Phdr *ph = &phdr[i];
176433d6423SLionel Sambuc 		int try_mmap = 1;
177433d6423SLionel Sambuc 		u16_t clearend = 0;
178433d6423SLionel Sambuc 		int pagechunk;
179433d6423SLionel Sambuc 		int mmap_prot = PROT_READ;
180433d6423SLionel Sambuc 
181*125832e3SLionel Sambuc #if ELF_DEBUG
182*125832e3SLionel Sambuc 			printf("libexec: -------------------\n");
183*125832e3SLionel Sambuc 			printf("libexec: phdr %x (%d)\n", (uint32_t)ph, i);
184*125832e3SLionel Sambuc #endif
185433d6423SLionel Sambuc 		if(!(ph->p_flags & PF_R)) {
186433d6423SLionel Sambuc 			printf("libexec: warning: unreadable segment\n");
187433d6423SLionel Sambuc 		}
188433d6423SLionel Sambuc 
189433d6423SLionel Sambuc 		if(ph->p_flags & PF_W) {
190433d6423SLionel Sambuc 			mmap_prot |= PROT_WRITE;
191433d6423SLionel Sambuc #if ELF_DEBUG
192433d6423SLionel Sambuc 			printf("libexec: adding PROT_WRITE\n");
193433d6423SLionel Sambuc #endif
194433d6423SLionel Sambuc 		} else {
195433d6423SLionel Sambuc #if ELF_DEBUG
196433d6423SLionel Sambuc 			printf("libexec: not adding PROT_WRITE\n");
197433d6423SLionel Sambuc #endif
198433d6423SLionel Sambuc 		}
199433d6423SLionel Sambuc 
200433d6423SLionel Sambuc 		if (ph->p_type != PT_LOAD || ph->p_memsz == 0) continue;
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc 		if((ph->p_vaddr % PAGE_SIZE) != (ph->p_offset % PAGE_SIZE)) {
203433d6423SLionel Sambuc 			printf("libexec: unaligned ELF program?\n");
204433d6423SLionel Sambuc 			try_mmap = 0;
205433d6423SLionel Sambuc 		}
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc 		if(!execi->memmap) {
208433d6423SLionel Sambuc 			try_mmap = 0;
209433d6423SLionel Sambuc 		}
210433d6423SLionel Sambuc 
211433d6423SLionel Sambuc 		foffset = ph->p_offset;
212433d6423SLionel Sambuc 		fbytes = ph->p_filesz;
213433d6423SLionel Sambuc 		vaddr = p_vaddr = ph->p_vaddr + execi->load_offset;
214433d6423SLionel Sambuc 		seg_membytes = ph->p_memsz;
215433d6423SLionel Sambuc 
216433d6423SLionel Sambuc 		page_offset = vaddr % PAGE_SIZE;
217433d6423SLionel Sambuc 		vaddr -= page_offset;
218433d6423SLionel Sambuc 		foffset -= page_offset;
219433d6423SLionel Sambuc 		seg_membytes += page_offset;
220433d6423SLionel Sambuc 		fbytes += page_offset;
221433d6423SLionel Sambuc 		vfileend  = p_vaddr + ph->p_filesz;
222433d6423SLionel Sambuc 
223433d6423SLionel Sambuc 		/* if there's usable memory after the file end, we have
224433d6423SLionel Sambuc 		 * to tell VM to clear the memory part of the page when it's
225433d6423SLionel Sambuc 		 * mapped in
226433d6423SLionel Sambuc 		 */
227433d6423SLionel Sambuc 		if((pagechunk = (vfileend % PAGE_SIZE))
228433d6423SLionel Sambuc 			&& ph->p_filesz < ph->p_memsz) {
229433d6423SLionel Sambuc 			clearend = PAGE_SIZE - pagechunk;
230433d6423SLionel Sambuc 		}
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc 		seg_membytes = roundup(seg_membytes, PAGE_SIZE);
233433d6423SLionel Sambuc 		fbytes = roundup(fbytes, PAGE_SIZE);
234433d6423SLionel Sambuc 
235433d6423SLionel Sambuc 		if(first || startv > vaddr) startv = vaddr;
236433d6423SLionel Sambuc 		first = 0;
237433d6423SLionel Sambuc 
238433d6423SLionel Sambuc 		if ((ph->p_flags & PF_X) != 0 && execi->text_size < seg_membytes)
239433d6423SLionel Sambuc 			execi->text_size = seg_membytes;
240433d6423SLionel Sambuc 		else
241433d6423SLionel Sambuc 			execi->data_size = seg_membytes;
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 		if(try_mmap && execi->memmap(execi, vaddr, fbytes, foffset, clearend, mmap_prot) == OK) {
244433d6423SLionel Sambuc #if ELF_DEBUG
2450a6a1f1dSLionel Sambuc 			printf("libexec: mmap 0x%lx-0x%llx done, clearend 0x%x\n",
246433d6423SLionel Sambuc 				vaddr, vaddr+fbytes, clearend);
247433d6423SLionel Sambuc #endif
248433d6423SLionel Sambuc 
249433d6423SLionel Sambuc 			if(seg_membytes > fbytes) {
250433d6423SLionel Sambuc 				int rem_mem = seg_membytes - fbytes;;
251433d6423SLionel Sambuc 				vir_bytes remstart = vaddr + fbytes;
252433d6423SLionel Sambuc 				if(execi->allocmem_ondemand(execi,
253433d6423SLionel Sambuc 					remstart, rem_mem) != OK) {
254433d6423SLionel Sambuc 					printf("libexec: mmap extra mem failed\n");
255433d6423SLionel Sambuc 					return ENOMEM;
256433d6423SLionel Sambuc 				}
257433d6423SLionel Sambuc #if ELF_DEBUG
258433d6423SLionel Sambuc 				else printf("libexec: allocated 0x%lx-0x%lx\n",
259433d6423SLionel Sambuc 
260433d6423SLionel Sambuc 					remstart, remstart+rem_mem);
261433d6423SLionel Sambuc #endif
262433d6423SLionel Sambuc 			}
263433d6423SLionel Sambuc 		} else {
264433d6423SLionel Sambuc 			/* make us some memory */
265433d6423SLionel Sambuc 			if(execi->allocmem_prealloc_junk(execi, vaddr, seg_membytes) != OK) {
266433d6423SLionel Sambuc 				if(execi->clearproc) execi->clearproc(execi);
267433d6423SLionel Sambuc 				return ENOMEM;
268433d6423SLionel Sambuc 			}
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc #if ELF_DEBUG
271*125832e3SLionel Sambuc 			printf("libexec: mmapped 0x%lx-0x%lx\n", vaddr, vaddr+seg_membytes);
272433d6423SLionel Sambuc #endif
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc 			/* Copy executable section into it */
275433d6423SLionel Sambuc 			if(execi->copymem(execi, ph->p_offset, p_vaddr, ph->p_filesz) != OK) {
276433d6423SLionel Sambuc 				if(execi->clearproc) execi->clearproc(execi);
277433d6423SLionel Sambuc 				return ENOMEM;
278433d6423SLionel Sambuc 			}
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc #if ELF_DEBUG
281*125832e3SLionel Sambuc 			printf("libexec: copied 0x%lx-0x%lx\n", p_vaddr, p_vaddr+ph->p_filesz);
282433d6423SLionel Sambuc #endif
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc 			/* Clear remaining bits */
285433d6423SLionel Sambuc 			vmemend = vaddr + seg_membytes;
286433d6423SLionel Sambuc 			if((chunk = p_vaddr - vaddr) > 0) {
287433d6423SLionel Sambuc #if ELF_DEBUG
288*125832e3SLionel Sambuc 				printf("libexec: start clearing 0x%lx-0x%lx\n", vaddr, vaddr+chunk);
289433d6423SLionel Sambuc #endif
290433d6423SLionel Sambuc 				execi->clearmem(execi, vaddr, chunk);
291433d6423SLionel Sambuc 			}
292433d6423SLionel Sambuc 
293433d6423SLionel Sambuc 			if((chunk = vmemend - vfileend) > 0) {
294433d6423SLionel Sambuc #if ELF_DEBUG
295*125832e3SLionel Sambuc 				printf("libexec: end clearing 0x%lx-0x%lx\n", vfileend, vfileend+chunk);
296433d6423SLionel Sambuc #endif
297433d6423SLionel Sambuc 				execi->clearmem(execi, vfileend, chunk);
298433d6423SLionel Sambuc 			}
299433d6423SLionel Sambuc 		}
300433d6423SLionel Sambuc 	}
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc 	/* Make it a stack */
303433d6423SLionel Sambuc 	if(execi->allocmem_ondemand(execi, stacklow, execi->stack_size) != OK) {
304433d6423SLionel Sambuc 		if(execi->clearproc) execi->clearproc(execi);
305433d6423SLionel Sambuc 		return ENOMEM;
306433d6423SLionel Sambuc 	}
307433d6423SLionel Sambuc 
308433d6423SLionel Sambuc #if ELF_DEBUG
309*125832e3SLionel Sambuc 	printf("libexec: stack mmapped 0x%lx-0x%lx\n", stacklow, stacklow+execi->stack_size);
310433d6423SLionel Sambuc #endif
311433d6423SLionel Sambuc 
312433d6423SLionel Sambuc 	/* record entry point and lowest load vaddr for caller */
313433d6423SLionel Sambuc 	execi->pc = hdr->e_entry + execi->load_offset;
314433d6423SLionel Sambuc 	execi->load_base = startv;
315433d6423SLionel Sambuc 
316433d6423SLionel Sambuc 	return OK;
317433d6423SLionel Sambuc }
318433d6423SLionel Sambuc 
319