imgact_coff.c revision 27537
160484Sobrien/*-
2218822Sdim * Copyright (c) 1994 Sean Eric Fagan
3218822Sdim * Copyright (c) 1994 S�ren Schmidt
460484Sobrien * All rights reserved.
560484Sobrien *
660484Sobrien * Redistribution and use in source and binary forms, with or without
760484Sobrien * modification, are permitted provided that the following conditions
860484Sobrien * are met:
960484Sobrien * 1. Redistributions of source code must retain the above copyright
1060484Sobrien *    notice, this list of conditions and the following disclaimer
1160484Sobrien *    in this position and unchanged.
1260484Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1360484Sobrien *    notice, this list of conditions and the following disclaimer in the
1460484Sobrien *    documentation and/or other materials provided with the distribution.
1560484Sobrien * 3. The name of the author may not be used to endorse or promote products
1660484Sobrien *    derived from this software withough specific prior written permission
1760484Sobrien *
1860484Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19218822Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2060484Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2160484Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2260484Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2360484Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2460484Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2560484Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2660484Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2760484Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2860484Sobrien *
2989857Sobrien *	$Id: imgact_coff.c,v 1.24 1997/04/13 01:48:13 dyson Exp $
3089857Sobrien */
3189857Sobrien
3289857Sobrien#include <sys/param.h>
3389857Sobrien#include <sys/systm.h>
3489857Sobrien#include <sys/mman.h>
3589857Sobrien#include <sys/imgact.h>
3689857Sobrien#include <sys/kernel.h>
3789857Sobrien#include <sys/fcntl.h>
38130561Sobrien#include <sys/malloc.h>
3960484Sobrien#include <sys/mount.h>
40218822Sdim#include <sys/namei.h>
41218822Sdim#include <sys/vnode.h>
42218822Sdim
4389857Sobrien#include <vm/vm.h>
4477298Sobrien#include <vm/vm_prot.h>
4589857Sobrien#include <vm/pmap.h>
4689857Sobrien#include <vm/vm_map.h>
4777298Sobrien#include <vm/vm_kern.h>
4877298Sobrien#include <vm/vm_extern.h>
49218822Sdim
50218822Sdim#include <i386/ibcs2/coff.h>
51218822Sdim#include <i386/ibcs2/ibcs2_util.h>
52218822Sdim
5377298Sobrienextern struct sysentvec ibcs2_svr3_sysvec;
5477298Sobrien
5577298Sobrienextern int coff_load_file __P((struct proc *p, char *name));
5689857Sobrienextern int exec_coff_imgact __P((struct image_params *imgp));
57218822Sdim
58218822Sdimstatic int load_coff_section __P((struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot));
59218822Sdim
6077298Sobrienstatic int
6160484Sobrienload_coff_section(vmspace, vp, offset, vmaddr, memsz, filsz, prot)
6289857Sobrien	struct vmspace *vmspace;
6389857Sobrien	struct vnode *vp;
6489857Sobrien	vm_offset_t offset;
6589857Sobrien	caddr_t vmaddr;
6689857Sobrien	size_t memsz, filsz;
6789857Sobrien	vm_prot_t prot;
6860484Sobrien{
6960484Sobrien	size_t map_len;
7060484Sobrien	vm_offset_t map_offset;
7160484Sobrien	vm_offset_t map_addr;
7260484Sobrien	int error;
73218822Sdim	unsigned char *data_buf = 0;
74218822Sdim	size_t copy_len;
75218822Sdim
76218822Sdim	map_offset = trunc_page(offset);
77218822Sdim	map_addr = trunc_page(vmaddr);
7860484Sobrien
7960484Sobrien	if (memsz > filsz) {
8060484Sobrien		/*
8160484Sobrien		 * We have the stupid situation that
8260484Sobrien		 * the section is longer than it is on file,
8360484Sobrien		 * which means it has zero-filled areas, and
8477298Sobrien		 * we have to work for it.  Stupid iBCS!
8577298Sobrien		 */
8660484Sobrien		map_len = trunc_page(offset + filsz) - trunc_page(map_offset);
87218822Sdim	} else {
88218822Sdim		/*
89218822Sdim		 * The only stuff we care about is on disk, and we
90218822Sdim		 * don't care if we map in more than is really there.
91218822Sdim		 */
92218822Sdim		map_len = round_page(offset + filsz) - trunc_page(map_offset);
93218822Sdim	}
94218822Sdim
95218822Sdim	DPRINTF(("%s(%d):  vm_mmap(&vmspace->vm_map, &0x%08lx, 0x%x, 0x%x, "
96218822Sdim		"VM_PROT_ALL, MAP_PRIVATE | MAP_FIXED, vp, 0x%x)\n",
97218822Sdim		__FILE__, __LINE__, map_addr, map_len, prot, map_offset));
98218822Sdim
99218822Sdim	if (error = vm_mmap(&vmspace->vm_map,
10060484Sobrien			     &map_addr,
10177298Sobrien			     map_len,
10260484Sobrien			     prot,
103218822Sdim			     VM_PROT_ALL,
104218822Sdim			     MAP_PRIVATE | MAP_FIXED,
105218822Sdim			     (caddr_t) vp,
106218822Sdim			     map_offset))
107218822Sdim		return error;
108218822Sdim
109218822Sdim	if (memsz == filsz) {
110218822Sdim		/* We're done! */
111218822Sdim		return 0;
112218822Sdim	}
113218822Sdim
114218822Sdim	/*
115218822Sdim	 * Now we have screwball stuff, to accomodate stupid COFF.
116218822Sdim	 * We have to map the remaining bit of the file into the kernel's
117218822Sdim	 * memory map, allocate some anonymous memory, copy that last
118218822Sdim	 * bit into it, and then we're done. *sigh*
119218822Sdim	 * For clean-up reasons, we actally map in the file last.
120218822Sdim	 */
121218822Sdim
122218822Sdim	copy_len = (offset + filsz) - trunc_page(offset + filsz);
123218822Sdim	map_addr = trunc_page(vmaddr + filsz);
124218822Sdim	map_len = round_page(vmaddr + memsz) - map_addr;
125218822Sdim
126218822Sdim	DPRINTF(("%s(%d): vm_map_find(&vmspace->vm_map, NULL, 0, &0x%08lx,0x%x, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0)\n", __FILE__, __LINE__, map_addr, map_len));
127218822Sdim
128218822Sdim	if (map_len != 0) {
129218822Sdim		error = vm_map_find(&vmspace->vm_map, NULL, 0, &map_addr,
130218822Sdim				    map_len, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0);
131218822Sdim		if (error)
132218822Sdim			return error;
133218822Sdim	}
134218822Sdim
135218822Sdim	if (error = vm_mmap(kernel_map,
136218822Sdim			    (vm_offset_t *) &data_buf,
137218822Sdim			    PAGE_SIZE,
138218822Sdim			    VM_PROT_READ,
139218822Sdim			    VM_PROT_READ,
140218822Sdim			    0,
141218822Sdim			    (caddr_t) vp,
142218822Sdim			    trunc_page(offset + filsz)))
143218822Sdim		return error;
144218822Sdim
145218822Sdim	error = copyout(data_buf, (caddr_t) map_addr, copy_len);
146218822Sdim
147218822Sdim	if (vm_map_remove(kernel_map,
148218822Sdim			  (vm_offset_t) data_buf,
149218822Sdim			  (vm_offset_t) data_buf + PAGE_SIZE))
150218822Sdim		panic("load_coff_section vm_map_remove failed");
151218822Sdim
152218822Sdim	return error;
153218822Sdim}
154218822Sdim
155218822Sdimint
156218822Sdimcoff_load_file(struct proc *p, char *name)
157218822Sdim{
158218822Sdim  	struct vmspace *vmspace = p->p_vmspace;
159218822Sdim  	int error;
160218822Sdim  	struct nameidata nd;
161218822Sdim  	struct vnode *vp;
162218822Sdim  	struct vattr attr;
163218822Sdim  	struct filehdr *fhdr;
164218822Sdim  	struct aouthdr *ahdr;
165218822Sdim  	struct scnhdr *scns;
166218822Sdim  	char *ptr = 0;
167218822Sdim  	int nscns;
168218822Sdim  	unsigned long text_offset = 0, text_address = 0, text_size = 0;
169218822Sdim  	unsigned long data_offset = 0, data_address = 0, data_size = 0;
170218822Sdim  	unsigned long bss_size = 0;
171218822Sdim  	int i;
172218822Sdim
173218822Sdim	/* XXX use of 'curproc' should be 'p'?*/
174218822Sdim	NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW | SAVENAME, UIO_SYSSPACE, name, curproc);
175218822Sdim
176218822Sdim  	error = namei(&nd);
177218822Sdim  	if (error)
178218822Sdim    		return error;
179218822Sdim
180218822Sdim  	vp = nd.ni_vp;
181218822Sdim  	if (vp == NULL)
182218822Sdim    		return ENOEXEC;
183218822Sdim
184218822Sdim  	if (vp->v_writecount) {
185218822Sdim    		error = ETXTBSY;
186218822Sdim    		goto fail;
187218822Sdim  	}
188218822Sdim
189218822Sdim  	if (error = VOP_GETATTR(vp, &attr, p->p_ucred, p))
190218822Sdim    		goto fail;
191218822Sdim
192218822Sdim  	if ((vp->v_mount->mnt_flag & MNT_NOEXEC)
193218822Sdim	    || ((attr.va_mode & 0111) == 0)
194218822Sdim	    || (attr.va_type != VREG))
195218822Sdim    		goto fail;
196218822Sdim
197218822Sdim  	if (attr.va_size == 0) {
198218822Sdim    		error = ENOEXEC;
199218822Sdim    		goto fail;
200218822Sdim  	}
201218822Sdim
202218822Sdim  	if (error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p))
203218822Sdim    		goto fail;
204218822Sdim
205218822Sdim  	if (error = VOP_OPEN(vp, FREAD, p->p_ucred, p))
206218822Sdim    		goto fail;
207218822Sdim
208218822Sdim	/*
209218822Sdim	 * Lose the lock on the vnode. It's no longer needed, and must not
210218822Sdim	 * exist for the pagefault paging to work below.
211218822Sdim	 */
212218822Sdim	VOP_UNLOCK(vp, 0, p);
213218822Sdim
214218822Sdim  	if (error = vm_mmap(kernel_map,
215218822Sdim			    (vm_offset_t *) &ptr,
216218822Sdim			    PAGE_SIZE,
217218822Sdim			    VM_PROT_READ,
218218822Sdim		       	    VM_PROT_READ,
219218822Sdim			    0,
220218822Sdim			    (caddr_t) vp,
221218822Sdim			    0))
222218822Sdim    	goto fail;
223218822Sdim
224218822Sdim  	fhdr = (struct filehdr *)ptr;
225218822Sdim
226218822Sdim  	if (fhdr->f_magic != I386_COFF) {
227218822Sdim    		error = ENOEXEC;
228218822Sdim    		goto dealloc_and_fail;
229218822Sdim  	}
230218822Sdim
231218822Sdim  	nscns = fhdr->f_nscns;
232218822Sdim
233218822Sdim  	if ((nscns * sizeof(struct scnhdr)) > PAGE_SIZE) {
234218822Sdim    		/*
235218822Sdim     		 * XXX -- just fail.  I'm so lazy.
236218822Sdim     		 */
23777298Sobrien    		error = ENOEXEC;
23860484Sobrien    		goto dealloc_and_fail;
239218822Sdim  	}
240218822Sdim
241218822Sdim  	ahdr = (struct aouthdr*)(ptr + sizeof(struct filehdr));
242218822Sdim
243218822Sdim  	scns = (struct scnhdr*)(ptr + sizeof(struct filehdr)
244218822Sdim			  + sizeof(struct aouthdr));
245218822Sdim
246218822Sdim  	for (i = 0; i < nscns; i++) {
247218822Sdim    		if (scns[i].s_flags & STYP_NOLOAD)
248218822Sdim      			continue;
249218822Sdim    		else if (scns[i].s_flags & STYP_TEXT) {
250218822Sdim      			text_address = scns[i].s_vaddr;
251218822Sdim      			text_size = scns[i].s_size;
252218822Sdim      			text_offset = scns[i].s_scnptr;
253218822Sdim    		}
254218822Sdim		else if (scns[i].s_flags & STYP_DATA) {
255218822Sdim      			data_address = scns[i].s_vaddr;
256218822Sdim      			data_size = scns[i].s_size;
257218822Sdim      			data_offset = scns[i].s_scnptr;
258218822Sdim    		} else if (scns[i].s_flags & STYP_BSS) {
259218822Sdim      			bss_size = scns[i].s_size;
260218822Sdim    		}
261218822Sdim  	}
262218822Sdim
263218822Sdim  	if (error = load_coff_section(vmspace, vp, text_offset,
264218822Sdim				      (caddr_t)text_address,
265218822Sdim				      text_size, text_size,
266218822Sdim				      VM_PROT_READ | VM_PROT_EXECUTE)) {
267218822Sdim    		goto dealloc_and_fail;
268218822Sdim  	}
269218822Sdim  	if (error = load_coff_section(vmspace, vp, data_offset,
270218822Sdim				      (caddr_t)data_address,
271218822Sdim				      data_size + bss_size, data_size,
272218822Sdim				      VM_PROT_ALL)) {
273218822Sdim    		goto dealloc_and_fail;
274218822Sdim  	}
275218822Sdim
276218822Sdim  	error = 0;
277218822Sdim
278130561Sobrien 	dealloc_and_fail:
279130561Sobrien	if (vm_map_remove(kernel_map,
280218822Sdim			  (vm_offset_t) ptr,
281218822Sdim			  (vm_offset_t) ptr + PAGE_SIZE))
282218822Sdim    		panic(__FUNCTION__ " vm_map_remove failed");
283218822Sdim
284218822Sdim fail:
285218822Sdim  	vput(nd.ni_vp);
286218822Sdim  	FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
28777298Sobrien  	return error;
288}
289
290int
291exec_coff_imgact(imgp)
292	struct image_params *imgp;
293{
294	const struct filehdr *fhdr = (const struct filehdr*)imgp->image_header;
295	const struct aouthdr *ahdr;
296	const struct scnhdr *scns;
297	int i;
298	struct vmspace *vmspace;
299	int nscns;
300	int error;
301	unsigned long text_offset = 0, text_address = 0, text_size = 0;
302	unsigned long data_offset = 0, data_address = 0, data_size = 0;
303	unsigned long bss_size = 0;
304	caddr_t hole;
305
306	if (fhdr->f_magic != I386_COFF ||
307	    !(fhdr->f_flags & F_EXEC)) {
308
309		 DPRINTF(("%s(%d): return -1\n", __FILE__, __LINE__));
310		 return -1;
311	}
312
313	nscns = fhdr->f_nscns;
314	if ((nscns * sizeof(struct scnhdr)) > PAGE_SIZE) {
315	  	/*
316	   	 * For now, return an error -- need to be able to
317	   	 * read in all of the section structures.
318	   	 */
319
320		DPRINTF(("%s(%d): return -1\n", __FILE__, __LINE__));
321		return -1;
322	}
323
324	ahdr = (const struct aouthdr*)
325	       ((const char*)(imgp->image_header) + sizeof(struct filehdr));
326	imgp->entry_addr = ahdr->entry;
327
328	scns = (const struct scnhdr*)
329	       ((const char*)(imgp->image_header) + sizeof(struct filehdr) +
330		sizeof(struct aouthdr));
331
332	if (error = exec_extract_strings(imgp)) {
333		DPRINTF(("%s(%d):  return %d\n", __FILE__, __LINE__, error));
334		return error;
335	}
336
337	exec_new_vmspace(imgp);
338	vmspace = imgp->proc->p_vmspace;
339
340	for (i = 0; i < nscns; i++) {
341
342	  DPRINTF(("i = %d, scns[i].s_name = %s, scns[i].s_vaddr = %08lx, "
343		   "scns[i].s_scnptr = %d\n", i, scns[i].s_name,
344		   scns[i].s_vaddr, scns[i].s_scnptr));
345	  if (scns[i].s_flags & STYP_NOLOAD) {
346	    	/*
347	     	 * A section that is not loaded, for whatever
348	     	 * reason.  It takes precedance over other flag
349	     	 * bits...
350	     	 */
351	    	continue;
352	  } else if (scns[i].s_flags & STYP_TEXT) {
353	    	text_address = scns[i].s_vaddr;
354	    	text_size = scns[i].s_size;
355	    	text_offset = scns[i].s_scnptr;
356	  } else if (scns[i].s_flags & STYP_DATA) {
357	    	/* .data section */
358	    	data_address = scns[i].s_vaddr;
359	    	data_size = scns[i].s_size;
360	    	data_offset = scns[i].s_scnptr;
361	  } else if (scns[i].s_flags & STYP_BSS) {
362	    	/* .bss section */
363	    	bss_size = scns[i].s_size;
364	  } else if (scns[i].s_flags & STYP_LIB) {
365	    	char *buf = 0;
366	    	int foff = trunc_page(scns[i].s_scnptr);
367	    	int off = scns[i].s_scnptr - foff;
368	    	int len = round_page(scns[i].s_size + PAGE_SIZE);
369	    	int j;
370
371	    	if (error = vm_mmap(kernel_map,
372				    (vm_offset_t *) &buf,
373				    len,
374				    VM_PROT_READ,
375				    VM_PROT_READ,
376				    0,
377				    (caddr_t) imgp->vp,
378				    foff)) {
379	      		return ENOEXEC;
380	    	}
381		if(scns[i].s_size) {
382			char *libbuf;
383			int emul_path_len = strlen(ibcs2_emul_path);
384
385			libbuf = malloc(MAXPATHLEN + emul_path_len,
386					M_TEMP, M_WAITOK);
387			strcpy(libbuf, ibcs2_emul_path);
388
389		    	for (j = off; j < scns[i].s_size + off; j++) {
390	      			char *libname;
391
392		      		libname = buf + j + 4 * *(long*)(buf + j + 4);
393		      		j += 4* *(long*)(buf + j);
394
395				DPRINTF(("%s(%d):  shared library %s\n",
396					 __FILE__, __LINE__, libname));
397				strcpy(&libbuf[emul_path_len], libname);
398		      		error = coff_load_file(imgp->proc, libbuf);
399		      		if (error)
400	      				error = coff_load_file(imgp->proc,
401							       libname);
402		      		if (error)
403					break;
404		    	}
405			free(libbuf, M_TEMP);
406		}
407		if (vm_map_remove(kernel_map,
408				  (vm_offset_t) buf,
409				  (vm_offset_t) buf + len))
410	      		panic("exec_coff_imgact vm_map_remove failed");
411	    	if (error)
412	      		return error;
413	  	}
414	}
415	/*
416	 * Map in .text now
417	 */
418
419	DPRINTF(("%s(%d):  load_coff_section(vmspace, "
420		"imgp->vp, %08lx, %08lx, 0x%x, 0x%x, 0x%x)\n",
421		__FILE__, __LINE__, text_offset, text_address,
422		text_size, text_size, VM_PROT_READ | VM_PROT_EXECUTE));
423	if (error = load_coff_section(vmspace, imgp->vp,
424				      text_offset, (caddr_t)text_address,
425				      text_size, text_size,
426				      VM_PROT_READ | VM_PROT_EXECUTE)) {
427		DPRINTF(("%s(%d): error = %d\n", __FILE__, __LINE__, error));
428		return error;
429       	}
430	/*
431	 * Map in .data and .bss now
432	 */
433
434
435	DPRINTF(("%s(%d): load_coff_section(vmspace, "
436		"imgp->vp, 0x%08lx, 0x%08lx, 0x%x, 0x%x, 0x%x)\n",
437		__FILE__, __LINE__, data_offset, data_address,
438		data_size + bss_size, data_size, VM_PROT_ALL));
439	if (error = load_coff_section(vmspace, imgp->vp,
440				      data_offset, (caddr_t)data_address,
441				      data_size + bss_size, data_size,
442				      VM_PROT_ALL)) {
443
444		DPRINTF(("%s(%d): error = %d\n", __FILE__, __LINE__, error));
445		return error;
446	}
447
448	imgp->interpreted = 0;
449	imgp->proc->p_sysent = &ibcs2_svr3_sysvec;
450
451	vmspace->vm_tsize = round_page(text_size) >> PAGE_SHIFT;
452	vmspace->vm_dsize = round_page(data_size + bss_size) >> PAGE_SHIFT;
453	vmspace->vm_taddr = (caddr_t)text_address;
454	vmspace->vm_daddr = (caddr_t)data_address;
455
456	hole = (caddr_t)trunc_page(vmspace->vm_daddr) + ctob(vmspace->vm_dsize);
457
458
459	DPRINTF(("%s(%d): vm_map_find(&vmspace->vm_map, NULL, 0, &0x%08lx, PAGE_SIZE, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0)\n",
460		__FILE__, __LINE__, hole));
461        DPRINTF(("imgact: error = %d\n", error));
462
463	error = vm_map_find(&vmspace->vm_map, NULL, 0,
464			    (vm_offset_t *) &hole, PAGE_SIZE, FALSE,
465				VM_PROT_ALL, VM_PROT_ALL, 0);
466
467	DPRINTF(("IBCS2: start vm_dsize = 0x%x, vm_daddr = 0x%x end = 0x%x\n",
468		ctob(vmspace->vm_dsize), vmspace->vm_daddr,
469		ctob(vmspace->vm_dsize) + vmspace->vm_daddr ));
470	DPRINTF(("%s(%d):  returning successfully!\n", __FILE__, __LINE__));
471
472	/* Indicate that this file should not be modified */
473	imgp->vp->v_flag |= VTEXT;
474	return 0;
475}
476
477/*
478 * Tell kern_execve.c about it, with a little help from the linker.
479 * Since `const' objects end up in the text segment, TEXT_SET is the
480 * correct directive to use.
481 */
482const struct execsw coff_execsw = { exec_coff_imgact, "coff" };
483TEXT_SET(execsw_set, coff_execsw);
484