xref: /netbsd-src/sys/compat/linux/common/linux_exec_elf32.c (revision c8216580df7d3e6b76aad0c2e4ab253e8c0b0c18)
1*c8216580Schristos /*	$NetBSD: linux_exec_elf32.c,v 1.41 1999/02/09 20:37:19 christos Exp $	*/
28fb507a3Schristos 
38fb507a3Schristos /*-
48096c25aSfvdl  * Copyright (c) 1995, 1998 The NetBSD Foundation, Inc.
58fb507a3Schristos  * All rights reserved.
68fb507a3Schristos  *
78fb507a3Schristos  * This code is derived from software contributed to The NetBSD Foundation
88096c25aSfvdl  * by Christos Zoulas, Frank van der Linden and Eric Haszlakiewicz.
98fb507a3Schristos  *
108fb507a3Schristos  * Redistribution and use in source and binary forms, with or without
118fb507a3Schristos  * modification, are permitted provided that the following conditions
128fb507a3Schristos  * are met:
138fb507a3Schristos  * 1. Redistributions of source code must retain the above copyright
148fb507a3Schristos  *    notice, this list of conditions and the following disclaimer.
158fb507a3Schristos  * 2. Redistributions in binary form must reproduce the above copyright
168fb507a3Schristos  *    notice, this list of conditions and the following disclaimer in the
178fb507a3Schristos  *    documentation and/or other materials provided with the distribution.
188fb507a3Schristos  * 3. All advertising materials mentioning features or use of this software
198fb507a3Schristos  *    must display the following acknowledgement:
208fb507a3Schristos  *	This product includes software developed by the NetBSD
218fb507a3Schristos  *	Foundation, Inc. and its contributors.
228fb507a3Schristos  * 4. Neither the name of The NetBSD Foundation nor the names of its
238fb507a3Schristos  *    contributors may be used to endorse or promote products derived
248fb507a3Schristos  *    from this software without specific prior written permission.
258fb507a3Schristos  *
268fb507a3Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
278fb507a3Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
288fb507a3Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
298fb507a3Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
308fb507a3Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
318fb507a3Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
328fb507a3Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
338fb507a3Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
348fb507a3Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
358fb507a3Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
368fb507a3Schristos  * POSSIBILITY OF SUCH DAMAGE.
378fb507a3Schristos  */
383bf459f3Sfvdl 
393bf459f3Sfvdl /*
40fb777788Sfvdl  * based on exec_aout.c, sunos_exec.c and svr4_exec.c
413bf459f3Sfvdl  */
423bf459f3Sfvdl 
43f7ac1bd3Serh #ifndef ELFSIZE
447c325577Scgd #define	ELFSIZE		32				/* XXX should die */
45f7ac1bd3Serh #endif
469c3e274cScgd 
473bf459f3Sfvdl #include <sys/param.h>
483bf459f3Sfvdl #include <sys/systm.h>
493bf459f3Sfvdl #include <sys/kernel.h>
503bf459f3Sfvdl #include <sys/proc.h>
513bf459f3Sfvdl #include <sys/malloc.h>
523bf459f3Sfvdl #include <sys/namei.h>
533bf459f3Sfvdl #include <sys/vnode.h>
54151fa70fSchristos #include <sys/mount.h>
55d551a4edSchristos #include <sys/exec.h>
56c4aaa600Sfvdl #include <sys/exec_elf.h>
573bf459f3Sfvdl 
583bf459f3Sfvdl #include <sys/mman.h>
59151fa70fSchristos #include <sys/syscallargs.h>
60151fa70fSchristos 
613bf459f3Sfvdl #include <vm/vm.h>
623bf459f3Sfvdl #include <vm/vm_param.h>
633bf459f3Sfvdl #include <vm/vm_map.h>
643bf459f3Sfvdl 
653bf459f3Sfvdl #include <machine/cpu.h>
663bf459f3Sfvdl #include <machine/reg.h>
673bf459f3Sfvdl 
68908291d2Schristos #include <compat/linux/common/linux_types.h>
69908291d2Schristos #include <compat/linux/common/linux_signal.h>
70908291d2Schristos #include <compat/linux/common/linux_util.h>
71908291d2Schristos #include <compat/linux/common/linux_exec.h>
72908291d2Schristos #include <compat/linux/common/linux_machdep.h>
734c127bdcSveego #include <compat/linux/common/linux_errno.h>
743bf459f3Sfvdl 
75908291d2Schristos #include <compat/linux/linux_syscallargs.h>
76908291d2Schristos #include <compat/linux/linux_syscall.h>
77fc7cfb5fSfvdl 
78f7ac1bd3Serh static int ELFNAME2(linux,signature) __P((struct proc *, struct exec_package *,
79f7ac1bd3Serh 	Elf_Ehdr *));
80f7ac1bd3Serh #ifdef LINUX_GCC_SIGNATURE
81f7ac1bd3Serh static int ELFNAME2(linux,gcc_signature) __P((struct proc *p,
82f7ac1bd3Serh 	struct exec_package *, Elf_Ehdr *));
83f7ac1bd3Serh #endif
84f7ac1bd3Serh 
85c4aaa600Sfvdl #define LINUX_ELF_AUX_ARGSIZ (sizeof(AuxInfo) * 8 / sizeof(char *))
86c4aaa600Sfvdl 
8744eef7c2Schristos 
88245f292fSmycroft extern char linux_sigcode[], linux_esigcode[];
8944eef7c2Schristos extern struct sysent linux_sysent[];
9044eef7c2Schristos extern char *linux_syscallnames[];
9144eef7c2Schristos 
92f7ac1bd3Serh struct emul ELFNAMEEND(emul_linux) = {
93fc7cfb5fSfvdl 	"linux",
948b351f01Serh 	native_to_linux_errno,
95fc7cfb5fSfvdl 	linux_sendsig,
96fc7cfb5fSfvdl 	LINUX_SYS_syscall,
97fc7cfb5fSfvdl 	LINUX_SYS_MAXSYSCALL,
98fc7cfb5fSfvdl 	linux_sysent,
99fc7cfb5fSfvdl 	linux_syscallnames,
100fc7cfb5fSfvdl 	LINUX_ELF_AUX_ARGSIZ,
101f7ac1bd3Serh 	ELFNAME(copyargs),
10272623d84Smycroft 	linux_setregs,
10344eef7c2Schristos 	linux_sigcode,
10444eef7c2Schristos 	linux_esigcode,
10544eef7c2Schristos };
10644eef7c2Schristos 
10744eef7c2Schristos 
108f7ac1bd3Serh #ifdef LINUX_GCC_SIGNATURE
1094d9a6e09Schristos /*
1104d9a6e09Schristos  * Take advantage of the fact that all the linux binaries are compiled
1114d9a6e09Schristos  * with gcc, and gcc sticks in the comment field a signature. Note that
1124d9a6e09Schristos  * on SVR4 binaries, the gcc signature will follow the OS name signature,
1134d9a6e09Schristos  * that will not be a problem. We don't bother to read in the string table,
1144d9a6e09Schristos  * but we check all the progbits headers.
115f7ac1bd3Serh  *
116f7ac1bd3Serh  * XXX This only works in the i386.  On the alpha (at least)
117f7ac1bd3Serh  * XXX we have the same gcc signature which incorrectly identifies
118f7ac1bd3Serh  * XXX NetBSD binaries as Linux.
1194d9a6e09Schristos  */
1204d9a6e09Schristos static int
121f7ac1bd3Serh ELFNAME2(linux,gcc_signature)(p, epp, eh)
122fc7cfb5fSfvdl 	struct proc *p;
123fc7cfb5fSfvdl 	struct exec_package *epp;
124f7ac1bd3Serh 	Elf_Ehdr *eh;
1254d9a6e09Schristos {
126f7ac1bd3Serh 	size_t shsize = sizeof(Elf_Shdr) * eh->e_shnum;
1274d9a6e09Schristos 	size_t i;
1284d9a6e09Schristos 	static const char signature[] = "\0GCC: (GNU) ";
1294d9a6e09Schristos 	char buf[sizeof(signature) - 1];
130f7ac1bd3Serh 	Elf_Shdr *sh;
1314d9a6e09Schristos 	int error;
1324d9a6e09Schristos 
133f7ac1bd3Serh 	error = ENOEXEC;
134f7ac1bd3Serh 	sh = (Elf_Shdr *) malloc(shsize, M_TEMP, M_WAITOK);
1354d9a6e09Schristos 
136f7ac1bd3Serh 	if ((error = ELFNAME(read_from)(p, epp->ep_vp, eh->e_shoff,
1374d9a6e09Schristos 	    (caddr_t) sh, shsize)) != 0)
1384d9a6e09Schristos 		goto out;
1394d9a6e09Schristos 
1404d9a6e09Schristos 	for (i = 0; i < eh->e_shnum; i++) {
141f7ac1bd3Serh 		Elf_Shdr *s = &sh[i];
1424d9a6e09Schristos 
1434d9a6e09Schristos 		/*
1444d9a6e09Schristos 		 * Identify candidates for the comment header;
145d83602c1Schristos 		 * Header cannot have a load address, or flags and
1464d9a6e09Schristos 		 * it must be large enough.
1474d9a6e09Schristos 		 */
148fb2727b7Sjtk 		if (s->sh_type != Elf_sht_progbits ||
1494d9a6e09Schristos 		    s->sh_addr != 0 ||
1504d9a6e09Schristos 		    s->sh_flags != 0 ||
1514d9a6e09Schristos 		    s->sh_size < sizeof(signature) - 1)
1524d9a6e09Schristos 			continue;
1534d9a6e09Schristos 
154f7ac1bd3Serh 		if ((error = ELFNAME(read_from)(p, epp->ep_vp, s->sh_offset,
155d83602c1Schristos 		    (caddr_t) buf, sizeof(signature) - 1)) != 0)
1564d9a6e09Schristos 			goto out;
1574d9a6e09Schristos 
158d83602c1Schristos 		/*
159d83602c1Schristos 		 * error is 0, if the signatures match we are done.
160d83602c1Schristos 		 */
1618b351f01Serh #ifdef DEBUG_LINUX
1628b351f01Serh 		printf("linux_gcc_sig: sig=%s\n", buf);
1638b351f01Serh #endif
164e1601dc2Sperry 		if (memcmp(buf, signature, sizeof(signature) - 1) == 0)
1654d9a6e09Schristos 			goto out;
1664d9a6e09Schristos 	}
1674d9a6e09Schristos 
1684d9a6e09Schristos out:
1694d9a6e09Schristos 	free(sh, M_TEMP);
1708b351f01Serh #ifdef DEBUG_LINUX
1718b351f01Serh 	printf("linux_gcc_sig: returning %d\n", error);
1728b351f01Serh #endif
1734d9a6e09Schristos 	return error;
1744d9a6e09Schristos }
175f7ac1bd3Serh #endif
1764d9a6e09Schristos 
177f7ac1bd3Serh static int
178f7ac1bd3Serh ELFNAME2(linux,signature)(p, epp, eh)
1794d9a6e09Schristos 	struct proc *p;
1804d9a6e09Schristos 	struct exec_package *epp;
181f7ac1bd3Serh 	Elf_Ehdr *eh;
182f7ac1bd3Serh {
183f7ac1bd3Serh 	size_t i;
184f7ac1bd3Serh 	Elf_Phdr *ph;
185f7ac1bd3Serh 	Elf_Note *notep;
186f7ac1bd3Serh 	size_t phsize;
187f7ac1bd3Serh 	int error = ENOEXEC;
188f7ac1bd3Serh 
189f7ac1bd3Serh 	phsize = eh->e_phnum * sizeof(Elf_Phdr);
190f7ac1bd3Serh 	ph = (Elf_Phdr *)malloc(phsize, M_TEMP, M_WAITOK);
191f7ac1bd3Serh 	if ((error = ELFNAME(read_from)(p, epp->ep_vp, eh->e_phoff,
192f7ac1bd3Serh 					(caddr_t) ph, phsize)) != 0)
193f7ac1bd3Serh 		goto out1;
194f7ac1bd3Serh 
195f7ac1bd3Serh 	for (i = 0; i < eh->e_phnum; i++) {
196f7ac1bd3Serh 		Elf_Phdr *ephp = &ph[i];
197f7ac1bd3Serh 		u_int32_t ostype;
198f7ac1bd3Serh 
199f7ac1bd3Serh 		if (ephp->p_type != Elf_pt_interp /* XAX pt_note */
20096dc2f3cSchristos #if 0
20196dc2f3cSchristos 		    || ephp->p_flags != 0
20296dc2f3cSchristos 		    || ephp->p_filesz < sizeof(Elf_Note))
20396dc2f3cSchristos #endif
20496dc2f3cSchristos 		    )
205f7ac1bd3Serh 			continue;
206f7ac1bd3Serh 
207bda91330Serh 		notep = (Elf_Note *)malloc(ephp->p_filesz+1, M_TEMP, M_WAITOK);
208f7ac1bd3Serh 		if ((error = ELFNAME(read_from)(p, epp->ep_vp, ephp->p_offset,
209f7ac1bd3Serh 					(caddr_t)notep, ephp->p_filesz)) != 0)
210f7ac1bd3Serh 			goto out3;
211f7ac1bd3Serh 
212bda91330Serh 		/* Check for "linux" in the intepreter name. */
213bda91330Serh 		if (ephp->p_filesz < 8 + 5) {
214bda91330Serh 			error = ENOEXEC;
215bda91330Serh 			goto out3;
216bda91330Serh 		}
2178b351f01Serh #ifdef DEBUG_LINUX
218bda91330Serh 		printf("linux_signature: interp=%s\n", notep);
2198b351f01Serh #endif
220bda91330Serh 		if (strncmp(&((char *)notep)[8], "linux", 5) == 0 ||
221bda91330Serh 		    strncmp((char *)notep, "/lib/ld.so.", 11) == 0) {
222f7ac1bd3Serh 			error = 0;
223f7ac1bd3Serh 			goto out3;
224f7ac1bd3Serh 		}
225f7ac1bd3Serh 
226f7ac1bd3Serh 		goto out2;
227f7ac1bd3Serh 
228f7ac1bd3Serh 		/* XXX XAX Should handle NETBSD_TYPE_EMULNAME */
229f7ac1bd3Serh 		if (notep->type != ELF_NOTE_TYPE_OSVERSION) {
230f7ac1bd3Serh 			free(notep, M_TEMP);
231f7ac1bd3Serh 			continue;
232f7ac1bd3Serh 		}
233f7ac1bd3Serh 
234f7ac1bd3Serh 		/* Check the name and description sizes. */
235f7ac1bd3Serh 		if (notep->namesz != ELF_NOTE_GNU_NAMESZ ||
236f7ac1bd3Serh 		    notep->descsz != ELF_NOTE_GNU_DESCSZ)
237f7ac1bd3Serh 			goto out2;
238f7ac1bd3Serh 
239f7ac1bd3Serh 		/* Is the name "GNU\0"? */
240f7ac1bd3Serh 		if (memcmp((notep + sizeof(Elf_Note)),
241f7ac1bd3Serh 			   ELF_NOTE_GNU_NAME, ELF_NOTE_GNU_NAMESZ))
242f7ac1bd3Serh 			goto out2;
243f7ac1bd3Serh 
244f7ac1bd3Serh 		/* Make sure the OS is Linux */
24596dc2f3cSchristos 		ostype = (u_int32_t)(*((u_int32_t *)notep + sizeof(Elf_Note) +
24696dc2f3cSchristos 		    notep->namesz)) & ELF_NOTE_GNU_OSMASK;
247f7ac1bd3Serh 		if (ostype != ELF_NOTE_GNU_OSLINUX)
248f7ac1bd3Serh 			goto out2;
249f7ac1bd3Serh 
250f7ac1bd3Serh 		/* All checks succeeded. */
251f7ac1bd3Serh 		error = 0;
252f7ac1bd3Serh 		goto out3;
253f7ac1bd3Serh 	}
254f7ac1bd3Serh 
255f7ac1bd3Serh 	error = ENOEXEC;
256f7ac1bd3Serh 
257f7ac1bd3Serh out1:
258f7ac1bd3Serh 	free(ph, M_TEMP);
2598b351f01Serh #ifdef DEBUG_LINUX
2608b351f01Serh 	printf("linux_signature: out1=%d\n", error);
2618b351f01Serh #endif
262f7ac1bd3Serh 	return error;
263f7ac1bd3Serh 
264f7ac1bd3Serh out2:
265f7ac1bd3Serh 	error = ENOEXEC;
266f7ac1bd3Serh out3:
267f7ac1bd3Serh 	free(notep, M_TEMP);
268f7ac1bd3Serh 	free(ph, M_TEMP);
2698b351f01Serh #ifdef DEBUG_LINUX
2708b351f01Serh 	printf("linux_signature: out2,3=%d\n", error);
2718b351f01Serh #endif
272f7ac1bd3Serh 	return error;
273f7ac1bd3Serh }
274f7ac1bd3Serh 
275f7ac1bd3Serh int
276f7ac1bd3Serh ELFNAME2(linux,probe)(p, epp, eh, itp, pos)
277f7ac1bd3Serh 	struct proc *p;
278f7ac1bd3Serh 	struct exec_package *epp;
279f7ac1bd3Serh 	Elf_Ehdr *eh;
280c4aaa600Sfvdl 	char *itp;
281f7ac1bd3Serh 	Elf_Addr *pos;
282fc7cfb5fSfvdl {
283*c8216580Schristos 	const char *bp;
284fc7cfb5fSfvdl 	int error;
285c4aaa600Sfvdl 	size_t len;
286fc7cfb5fSfvdl 
287f7ac1bd3Serh 	if ((error = ELFNAME2(linux,signature)(p, epp, eh)) != 0)
288f7ac1bd3Serh #ifdef LINUX_GCC_SIGNATURE
289f7ac1bd3Serh 		if ((error = ELFNAME2(linux,gcc_signature)(p, epp, eh)) != 0)
2904d9a6e09Schristos 			return error;
291f7ac1bd3Serh #else
292f7ac1bd3Serh 		return error;
293f7ac1bd3Serh #endif
2944d9a6e09Schristos 
295c4aaa600Sfvdl 	if (itp[0]) {
2966b95b513Schristos 		if ((error = emul_find(p, NULL, linux_emul_path, itp, &bp, 0)))
297c4aaa600Sfvdl 			return error;
298c4aaa600Sfvdl 		if ((error = copystr(bp, itp, MAXPATHLEN, &len)))
299c4aaa600Sfvdl 			return error;
300*c8216580Schristos 		free((void *)bp, M_TEMP);
301fc7cfb5fSfvdl 	}
302f7ac1bd3Serh 	epp->ep_emul = &ELFNAMEEND(emul_linux);
303f7ac1bd3Serh 	*pos = ELF_NO_ADDR;
3048b351f01Serh #ifdef DEBUG_LINUX
3058b351f01Serh 	printf("linux_probe: returning 0\n");
3068b351f01Serh #endif
307c4aaa600Sfvdl 	return 0;
308fc7cfb5fSfvdl }
309c4aaa600Sfvdl 
310