xref: /netbsd-src/sys/compat/linux/common/linux_exec_elf32.c (revision b29180b2ffee5bef91ef5b8e400c91f31d8fb6d4)
1*b29180b2Smycroft /*	$NetBSD: linux_exec_elf32.c,v 1.50 2000/12/15 06:14:21 mycroft Exp $	*/
28fb507a3Schristos 
38fb507a3Schristos /*-
4*b29180b2Smycroft  * Copyright (c) 1995, 1998, 2000 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 <machine/cpu.h>
623bf459f3Sfvdl #include <machine/reg.h>
633bf459f3Sfvdl 
64908291d2Schristos #include <compat/linux/common/linux_types.h>
65908291d2Schristos #include <compat/linux/common/linux_signal.h>
66908291d2Schristos #include <compat/linux/common/linux_util.h>
67908291d2Schristos #include <compat/linux/common/linux_exec.h>
68908291d2Schristos #include <compat/linux/common/linux_machdep.h>
693bf459f3Sfvdl 
70908291d2Schristos #include <compat/linux/linux_syscallargs.h>
71908291d2Schristos #include <compat/linux/linux_syscall.h>
72fc7cfb5fSfvdl 
73f7ac1bd3Serh static int ELFNAME2(linux,signature) __P((struct proc *, struct exec_package *,
74*b29180b2Smycroft 	Elf_Ehdr *, char *));
75f7ac1bd3Serh #ifdef LINUX_GCC_SIGNATURE
76f7ac1bd3Serh static int ELFNAME2(linux,gcc_signature) __P((struct proc *p,
77f7ac1bd3Serh 	struct exec_package *, Elf_Ehdr *));
78f7ac1bd3Serh #endif
79f7ac1bd3Serh 
80f7ac1bd3Serh #ifdef LINUX_GCC_SIGNATURE
814d9a6e09Schristos /*
824d9a6e09Schristos  * Take advantage of the fact that all the linux binaries are compiled
834d9a6e09Schristos  * with gcc, and gcc sticks in the comment field a signature. Note that
844d9a6e09Schristos  * on SVR4 binaries, the gcc signature will follow the OS name signature,
854d9a6e09Schristos  * that will not be a problem. We don't bother to read in the string table,
864d9a6e09Schristos  * but we check all the progbits headers.
87f7ac1bd3Serh  *
88f7ac1bd3Serh  * XXX This only works in the i386.  On the alpha (at least)
89f7ac1bd3Serh  * XXX we have the same gcc signature which incorrectly identifies
90f7ac1bd3Serh  * XXX NetBSD binaries as Linux.
914d9a6e09Schristos  */
924d9a6e09Schristos static int
93f7ac1bd3Serh ELFNAME2(linux,gcc_signature)(p, epp, eh)
94fc7cfb5fSfvdl 	struct proc *p;
95fc7cfb5fSfvdl 	struct exec_package *epp;
96f7ac1bd3Serh 	Elf_Ehdr *eh;
974d9a6e09Schristos {
98*b29180b2Smycroft 	size_t shsize;
994d9a6e09Schristos 	size_t i;
1004d9a6e09Schristos 	static const char signature[] = "\0GCC: (GNU) ";
1014d9a6e09Schristos 	char buf[sizeof(signature) - 1];
102f7ac1bd3Serh 	Elf_Shdr *sh;
1034d9a6e09Schristos 	int error;
1044d9a6e09Schristos 
105*b29180b2Smycroft 	shsize = eh->e_shnum * sizeof(Elf_Shdr);
106f7ac1bd3Serh 	sh = (Elf_Shdr *) malloc(shsize, M_TEMP, M_WAITOK);
107*b29180b2Smycroft 	error = ELFNAME(read_from)(p, epp->ep_vp, eh->e_shoff, (caddr_t)sh,
108*b29180b2Smycroft 	    shsize);
109*b29180b2Smycroft 	if (error)
1104d9a6e09Schristos 		goto out;
1114d9a6e09Schristos 
1124d9a6e09Schristos 	for (i = 0; i < eh->e_shnum; i++) {
113f7ac1bd3Serh 		Elf_Shdr *s = &sh[i];
1144d9a6e09Schristos 
1154d9a6e09Schristos 		/*
1164d9a6e09Schristos 		 * Identify candidates for the comment header;
117d83602c1Schristos 		 * Header cannot have a load address, or flags and
1184d9a6e09Schristos 		 * it must be large enough.
1194d9a6e09Schristos 		 */
120522cbf02Skleink 		if (s->sh_type != SHT_PROGBITS ||
1214d9a6e09Schristos 		    s->sh_addr != 0 ||
1224d9a6e09Schristos 		    s->sh_flags != 0 ||
1234d9a6e09Schristos 		    s->sh_size < sizeof(signature) - 1)
1244d9a6e09Schristos 			continue;
1254d9a6e09Schristos 
126*b29180b2Smycroft 		error = ELFNAME(read_from)(p, epp->ep_vp, s->sh_offset,
127*b29180b2Smycroft 		    (caddr_t)buf, sizeof(signature) - 1);
128*b29180b2Smycroft 		if (error)
129*b29180b2Smycroft 			continue;
1304d9a6e09Schristos 
131d83602c1Schristos 		/*
132d83602c1Schristos 		 * error is 0, if the signatures match we are done.
133d83602c1Schristos 		 */
1348b351f01Serh #ifdef DEBUG_LINUX
1358b351f01Serh 		printf("linux_gcc_sig: sig=%s\n", buf);
1368b351f01Serh #endif
137*b29180b2Smycroft 		if (!memcmp(buf, signature, sizeof(signature) - 1)) {
138*b29180b2Smycroft 			error = 0;
1394d9a6e09Schristos 			goto out;
1404d9a6e09Schristos 		}
141*b29180b2Smycroft 	}
142*b29180b2Smycroft 	error = ENOEXEC;
1434d9a6e09Schristos 
1444d9a6e09Schristos out:
1454d9a6e09Schristos 	free(sh, M_TEMP);
146*b29180b2Smycroft 	return (error);
1474d9a6e09Schristos }
148f7ac1bd3Serh #endif
1494d9a6e09Schristos 
150f7ac1bd3Serh static int
151*b29180b2Smycroft ELFNAME2(linux,signature)(p, epp, eh, itp)
1524d9a6e09Schristos 	struct proc *p;
1534d9a6e09Schristos 	struct exec_package *epp;
154f7ac1bd3Serh 	Elf_Ehdr *eh;
155*b29180b2Smycroft 	char *itp;
156f7ac1bd3Serh {
157f7ac1bd3Serh 	size_t i;
158f7ac1bd3Serh 	Elf_Phdr *ph;
159f7ac1bd3Serh 	size_t phsize;
160*b29180b2Smycroft 	int error;
161f7ac1bd3Serh 
162f7ac1bd3Serh 	phsize = eh->e_phnum * sizeof(Elf_Phdr);
163f7ac1bd3Serh 	ph = (Elf_Phdr *)malloc(phsize, M_TEMP, M_WAITOK);
164*b29180b2Smycroft 	error = ELFNAME(read_from)(p, epp->ep_vp, eh->e_phoff, (caddr_t)ph,
165*b29180b2Smycroft 	    phsize);
166*b29180b2Smycroft 	if (error)
167*b29180b2Smycroft 		goto out;
168f7ac1bd3Serh 
169f7ac1bd3Serh 	for (i = 0; i < eh->e_phnum; i++) {
170f7ac1bd3Serh 		Elf_Phdr *ephp = &ph[i];
171*b29180b2Smycroft 		Elf_Nhdr *np;
172*b29180b2Smycroft 		u_int32_t *abi;
173f7ac1bd3Serh 
174*b29180b2Smycroft 		if (ephp->p_type != PT_NOTE ||
175*b29180b2Smycroft 		    ephp->p_filesz > 1024 ||
176*b29180b2Smycroft 		    ephp->p_filesz < sizeof(Elf_Nhdr) + 20)
177f7ac1bd3Serh 			continue;
178f7ac1bd3Serh 
179*b29180b2Smycroft 		np = (Elf_Nhdr *)malloc(ephp->p_filesz, M_TEMP, M_WAITOK);
180*b29180b2Smycroft 		error = ELFNAME(read_from)(p, epp->ep_vp, ephp->p_offset,
181*b29180b2Smycroft 		    (caddr_t)np, ephp->p_filesz);
182*b29180b2Smycroft 		if (error)
183*b29180b2Smycroft 			goto next;
184f7ac1bd3Serh 
185*b29180b2Smycroft 		if (np->n_type != ELF_NOTE_TYPE_ABI_TAG ||
186*b29180b2Smycroft 		    np->n_namesz != ELF_NOTE_ABI_NAMESZ ||
187*b29180b2Smycroft 		    np->n_descsz != ELF_NOTE_ABI_DESCSZ ||
188*b29180b2Smycroft 		    memcmp((caddr_t)(np + 1), ELF_NOTE_ABI_NAME,
189*b29180b2Smycroft 		    ELF_NOTE_ABI_NAMESZ))
190*b29180b2Smycroft 			goto next;
191*b29180b2Smycroft 
192*b29180b2Smycroft 		/* Make sure the OS is Linux. */
193*b29180b2Smycroft 		abi = (u_int32_t *)((caddr_t)np + sizeof(Elf_Nhdr) +
194*b29180b2Smycroft 		    np->n_namesz);
195*b29180b2Smycroft 		if (abi[0] == ELF_NOTE_ABI_OS_LINUX)
196f7ac1bd3Serh 			error = 0;
197*b29180b2Smycroft 		else
198*b29180b2Smycroft 			error = ENOEXEC;
199*b29180b2Smycroft 		free(np, M_TEMP);
200*b29180b2Smycroft 		goto out;
201f7ac1bd3Serh 
202*b29180b2Smycroft 	next:
203*b29180b2Smycroft 		free(np, M_TEMP);
204f7ac1bd3Serh 		continue;
205f7ac1bd3Serh 	}
206f7ac1bd3Serh 
207*b29180b2Smycroft 	/* Check for certain intepreter names. */
208*b29180b2Smycroft 	if (itp[0]) {
209*b29180b2Smycroft 		if (!strncmp(itp, "/lib/ld-linux", 13) ||
210*b29180b2Smycroft 		    !strncmp(itp, "/lib/ld.so.", 11))
211f7ac1bd3Serh 			error = 0;
212*b29180b2Smycroft 		else
213*b29180b2Smycroft 			error = ENOEXEC;
214*b29180b2Smycroft 		goto out;
215f7ac1bd3Serh 	}
216f7ac1bd3Serh 
217f7ac1bd3Serh 	error = ENOEXEC;
218*b29180b2Smycroft out:
219f7ac1bd3Serh 	free(ph, M_TEMP);
220*b29180b2Smycroft 	return (error);
221f7ac1bd3Serh }
222f7ac1bd3Serh 
223f7ac1bd3Serh int
224f7ac1bd3Serh ELFNAME2(linux,probe)(p, epp, eh, itp, pos)
225f7ac1bd3Serh 	struct proc *p;
226f7ac1bd3Serh 	struct exec_package *epp;
227baae0324Sjdolecek 	void *eh;
228c4aaa600Sfvdl 	char *itp;
229baae0324Sjdolecek 	vaddr_t *pos;
230fc7cfb5fSfvdl {
231c8216580Schristos 	const char *bp;
232fc7cfb5fSfvdl 	int error;
233c4aaa600Sfvdl 	size_t len;
234fc7cfb5fSfvdl 
235*b29180b2Smycroft 	if ((error = ELFNAME2(linux,signature)(p, epp, eh, itp)) != 0)
236f7ac1bd3Serh #ifdef LINUX_GCC_SIGNATURE
237f7ac1bd3Serh 		if ((error = ELFNAME2(linux,gcc_signature)(p, epp, eh)) != 0)
2384d9a6e09Schristos 			return error;
239f7ac1bd3Serh #else
240f7ac1bd3Serh 		return error;
241f7ac1bd3Serh #endif
2424d9a6e09Schristos 
243c4aaa600Sfvdl 	if (itp[0]) {
24401040d97Sjdolecek 		if ((error = emul_find(p, NULL, epp->ep_esch->es_emul->e_path,
24501040d97Sjdolecek 		    itp, &bp, 0)))
246c4aaa600Sfvdl 			return error;
247c4aaa600Sfvdl 		if ((error = copystr(bp, itp, MAXPATHLEN, &len)))
248c4aaa600Sfvdl 			return error;
249c8216580Schristos 		free((void *)bp, M_TEMP);
250fc7cfb5fSfvdl 	}
251f7ac1bd3Serh 	*pos = ELF_NO_ADDR;
2528b351f01Serh #ifdef DEBUG_LINUX
2538b351f01Serh 	printf("linux_probe: returning 0\n");
2548b351f01Serh #endif
255c4aaa600Sfvdl 	return 0;
256fc7cfb5fSfvdl }
257c4aaa600Sfvdl 
258