xref: /freebsd/sys/compat/linux/linux_elf.c (revision 66e8f1f7d37b5f657086437f5411a63c9080e25c)
1447636e4SEdward Tomasz Napierala /*-
2447636e4SEdward Tomasz Napierala  * SPDX-License-Identifier: BSD-3-Clause
3447636e4SEdward Tomasz Napierala  *
4447636e4SEdward Tomasz Napierala  * Copyright (c) 2021 Edward Tomasz Napierala <trasz@FreeBSD.org>
5447636e4SEdward Tomasz Napierala  * Copyright (c) 2018 Chuck Tuffli
6447636e4SEdward Tomasz Napierala  * Copyright (c) 2017 Dell EMC
7447636e4SEdward Tomasz Napierala  * Copyright (c) 2000 David O'Brien
8447636e4SEdward Tomasz Napierala  * Copyright (c) 1995-1996 Søren Schmidt
9447636e4SEdward Tomasz Napierala  * Copyright (c) 1996 Peter Wemm
10447636e4SEdward Tomasz Napierala  * All rights reserved.
11447636e4SEdward Tomasz Napierala  *
12447636e4SEdward Tomasz Napierala  * This software was developed by the University of Cambridge Computer
13447636e4SEdward Tomasz Napierala  * Laboratory as part of the CHERI for Hypervisors and Operating Systems
14447636e4SEdward Tomasz Napierala  * (CHaOS) project, funded by EPSRC grant EP/V000292/1.
15447636e4SEdward Tomasz Napierala  *
16447636e4SEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
17447636e4SEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
18447636e4SEdward Tomasz Napierala  * are met:
19447636e4SEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
20447636e4SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer
21447636e4SEdward Tomasz Napierala  *    in this position and unchanged.
22447636e4SEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
23447636e4SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
24447636e4SEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
25447636e4SEdward Tomasz Napierala  * 3. The name of the author may not be used to endorse or promote products
26447636e4SEdward Tomasz Napierala  *    derived from this software without specific prior written permission
27447636e4SEdward Tomasz Napierala  *
28447636e4SEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
29447636e4SEdward Tomasz Napierala  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30447636e4SEdward Tomasz Napierala  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31447636e4SEdward Tomasz Napierala  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
32447636e4SEdward Tomasz Napierala  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
33447636e4SEdward Tomasz Napierala  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34447636e4SEdward Tomasz Napierala  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35447636e4SEdward Tomasz Napierala  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36447636e4SEdward Tomasz Napierala  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37447636e4SEdward Tomasz Napierala  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38447636e4SEdward Tomasz Napierala  */
39447636e4SEdward Tomasz Napierala 
40447636e4SEdward Tomasz Napierala #include <sys/cdefs.h>
41447636e4SEdward Tomasz Napierala __FBSDID("$FreeBSD$");
42447636e4SEdward Tomasz Napierala 
43447636e4SEdward Tomasz Napierala #include <sys/param.h>
446039e966SDmitry Chagin #include <sys/exec.h>
45447636e4SEdward Tomasz Napierala #include <sys/imgact.h>
46447636e4SEdward Tomasz Napierala #include <sys/imgact_elf.h>
4706d5ef0aSEdward Tomasz Napierala #include <sys/lock.h>
4806d5ef0aSEdward Tomasz Napierala #include <sys/malloc.h>
4906d5ef0aSEdward Tomasz Napierala #include <sys/mutex.h>
50447636e4SEdward Tomasz Napierala #include <sys/proc.h>
51447636e4SEdward Tomasz Napierala #include <sys/procfs.h>
52b7924341SAndrew Turner #include <sys/reg.h>
53447636e4SEdward Tomasz Napierala #include <sys/sbuf.h>
546039e966SDmitry Chagin #include <sys/sysent.h>
556039e966SDmitry Chagin 
566039e966SDmitry Chagin #include <vm/vm.h>
576039e966SDmitry Chagin #include <vm/pmap.h>
586039e966SDmitry Chagin #include <vm/vm_map.h>
59447636e4SEdward Tomasz Napierala 
60447636e4SEdward Tomasz Napierala #include <machine/elf.h>
61447636e4SEdward Tomasz Napierala 
626f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
63447636e4SEdward Tomasz Napierala #define linux_pt_regset linux_pt_regset32
64447636e4SEdward Tomasz Napierala #define bsd_to_linux_regset bsd_to_linux_regset32
65447636e4SEdward Tomasz Napierala #include <machine/../linux32/linux.h>
66447636e4SEdward Tomasz Napierala #else
67447636e4SEdward Tomasz Napierala #include <machine/../linux/linux.h>
68447636e4SEdward Tomasz Napierala #endif
69447636e4SEdward Tomasz Napierala #include <compat/linux/linux_elf.h>
7095b86034SDmitry Chagin #include <compat/linux/linux_mib.h>
716039e966SDmitry Chagin #include <compat/linux/linux_misc.h>
72447636e4SEdward Tomasz Napierala 
7374465145SDmitry Chagin struct l_elf_siginfo {
7474465145SDmitry Chagin 	l_int		si_signo;
7574465145SDmitry Chagin 	l_int		si_code;
7674465145SDmitry Chagin 	l_int		si_errno;
7774465145SDmitry Chagin };
7874465145SDmitry Chagin 
7974465145SDmitry Chagin typedef struct linux_pt_regset l_elf_gregset_t;
8074465145SDmitry Chagin 
8174465145SDmitry Chagin struct linux_elf_prstatus {
8274465145SDmitry Chagin 	struct l_elf_siginfo pr_info;
8374465145SDmitry Chagin 	l_short		pr_cursig;
8474465145SDmitry Chagin 	l_ulong		pr_sigpend;
8574465145SDmitry Chagin 	l_ulong		pr_sighold;
8674465145SDmitry Chagin 	l_pid_t		pr_pid;
8774465145SDmitry Chagin 	l_pid_t		pr_ppid;
8874465145SDmitry Chagin 	l_pid_t		pr_pgrp;
8974465145SDmitry Chagin 	l_pid_t		pr_sid;
9074465145SDmitry Chagin 	l_timeval	pr_utime;
9174465145SDmitry Chagin 	l_timeval	pr_stime;
9274465145SDmitry Chagin 	l_timeval	pr_cutime;
9374465145SDmitry Chagin 	l_timeval	pr_cstime;
9474465145SDmitry Chagin 	l_elf_gregset_t	pr_reg;
9574465145SDmitry Chagin 	l_int		pr_fpvalid;
9674465145SDmitry Chagin };
97447636e4SEdward Tomasz Napierala 
98447636e4SEdward Tomasz Napierala #define	LINUX_NT_AUXV	6
99447636e4SEdward Tomasz Napierala 
100447636e4SEdward Tomasz Napierala static void __linuxN(note_fpregset)(void *, struct sbuf *, size_t *);
101447636e4SEdward Tomasz Napierala static void __linuxN(note_prpsinfo)(void *, struct sbuf *, size_t *);
102447636e4SEdward Tomasz Napierala static void __linuxN(note_prstatus)(void *, struct sbuf *, size_t *);
103447636e4SEdward Tomasz Napierala static void __linuxN(note_threadmd)(void *, struct sbuf *, size_t *);
104447636e4SEdward Tomasz Napierala static void __linuxN(note_nt_auxv)(void *, struct sbuf *, size_t *);
105447636e4SEdward Tomasz Napierala 
106447636e4SEdward Tomasz Napierala void
107447636e4SEdward Tomasz Napierala __linuxN(prepare_notes)(struct thread *td, struct note_info_list *list,
108447636e4SEdward Tomasz Napierala     size_t *sizep)
109447636e4SEdward Tomasz Napierala {
110447636e4SEdward Tomasz Napierala 	struct proc *p;
111447636e4SEdward Tomasz Napierala 	struct thread *thr;
112447636e4SEdward Tomasz Napierala 	size_t size;
113447636e4SEdward Tomasz Napierala 
114447636e4SEdward Tomasz Napierala 	p = td->td_proc;
115447636e4SEdward Tomasz Napierala 	size = 0;
116447636e4SEdward Tomasz Napierala 
117447636e4SEdward Tomasz Napierala 	/*
118447636e4SEdward Tomasz Napierala 	 * To have the debugger select the right thread (LWP) as the initial
119447636e4SEdward Tomasz Napierala 	 * thread, we dump the state of the thread passed to us in td first.
120447636e4SEdward Tomasz Napierala 	 * This is the thread that causes the core dump and thus likely to
121447636e4SEdward Tomasz Napierala 	 * be the right thread one wants to have selected in the debugger.
122447636e4SEdward Tomasz Napierala 	 */
123447636e4SEdward Tomasz Napierala 	thr = td;
124447636e4SEdward Tomasz Napierala 	while (thr != NULL) {
125447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
126447636e4SEdward Tomasz Napierala 		    NT_PRSTATUS, __linuxN(note_prstatus), thr);
127447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
128447636e4SEdward Tomasz Napierala 		    NT_PRPSINFO, __linuxN(note_prpsinfo), p);
129447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
130447636e4SEdward Tomasz Napierala 		    LINUX_NT_AUXV, __linuxN(note_nt_auxv), p);
131447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
132447636e4SEdward Tomasz Napierala 		    NT_FPREGSET, __linuxN(note_fpregset), thr);
133447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
134447636e4SEdward Tomasz Napierala 		    -1, __linuxN(note_threadmd), thr);
135447636e4SEdward Tomasz Napierala 
136447636e4SEdward Tomasz Napierala 		thr = thr == td ? TAILQ_FIRST(&p->p_threads) :
137447636e4SEdward Tomasz Napierala 		    TAILQ_NEXT(thr, td_plist);
138447636e4SEdward Tomasz Napierala 		if (thr == td)
139447636e4SEdward Tomasz Napierala 			thr = TAILQ_NEXT(thr, td_plist);
140447636e4SEdward Tomasz Napierala 	}
141447636e4SEdward Tomasz Napierala 
142447636e4SEdward Tomasz Napierala 	*sizep = size;
143447636e4SEdward Tomasz Napierala }
144447636e4SEdward Tomasz Napierala 
145447636e4SEdward Tomasz Napierala typedef struct linux_elf_prstatus linux_elf_prstatus_t;
1466f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
147447636e4SEdward Tomasz Napierala typedef struct prpsinfo32 linux_elf_prpsinfo_t;
148447636e4SEdward Tomasz Napierala typedef struct fpreg32 linux_elf_prfpregset_t;
149447636e4SEdward Tomasz Napierala #else
150447636e4SEdward Tomasz Napierala typedef prpsinfo_t linux_elf_prpsinfo_t;
151447636e4SEdward Tomasz Napierala typedef prfpregset_t linux_elf_prfpregset_t;
152447636e4SEdward Tomasz Napierala #endif
153447636e4SEdward Tomasz Napierala 
154447636e4SEdward Tomasz Napierala static void
155447636e4SEdward Tomasz Napierala __linuxN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep)
156447636e4SEdward Tomasz Napierala {
157447636e4SEdward Tomasz Napierala 	struct sbuf sbarg;
158447636e4SEdward Tomasz Napierala 	size_t len;
159447636e4SEdward Tomasz Napierala 	char *cp, *end;
160447636e4SEdward Tomasz Napierala 	struct proc *p;
161447636e4SEdward Tomasz Napierala 	linux_elf_prpsinfo_t *psinfo;
162447636e4SEdward Tomasz Napierala 	int error;
163447636e4SEdward Tomasz Napierala 
164447636e4SEdward Tomasz Napierala 	p = arg;
165447636e4SEdward Tomasz Napierala 	if (sb != NULL) {
166447636e4SEdward Tomasz Napierala 		KASSERT(*sizep == sizeof(*psinfo), ("invalid size"));
167447636e4SEdward Tomasz Napierala 		psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK);
168447636e4SEdward Tomasz Napierala 		psinfo->pr_version = PRPSINFO_VERSION;
169447636e4SEdward Tomasz Napierala 		psinfo->pr_psinfosz = sizeof(linux_elf_prpsinfo_t);
170447636e4SEdward Tomasz Napierala 		strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname));
171447636e4SEdward Tomasz Napierala 		PROC_LOCK(p);
172447636e4SEdward Tomasz Napierala 		if (p->p_args != NULL) {
173447636e4SEdward Tomasz Napierala 			len = sizeof(psinfo->pr_psargs) - 1;
174447636e4SEdward Tomasz Napierala 			if (len > p->p_args->ar_length)
175447636e4SEdward Tomasz Napierala 				len = p->p_args->ar_length;
176447636e4SEdward Tomasz Napierala 			memcpy(psinfo->pr_psargs, p->p_args->ar_args, len);
177447636e4SEdward Tomasz Napierala 			PROC_UNLOCK(p);
178447636e4SEdward Tomasz Napierala 			error = 0;
179447636e4SEdward Tomasz Napierala 		} else {
180447636e4SEdward Tomasz Napierala 			_PHOLD(p);
181447636e4SEdward Tomasz Napierala 			PROC_UNLOCK(p);
182447636e4SEdward Tomasz Napierala 			sbuf_new(&sbarg, psinfo->pr_psargs,
183447636e4SEdward Tomasz Napierala 			    sizeof(psinfo->pr_psargs), SBUF_FIXEDLEN);
184447636e4SEdward Tomasz Napierala 			error = proc_getargv(curthread, p, &sbarg);
185447636e4SEdward Tomasz Napierala 			PRELE(p);
18600d17cf3SKonstantin Belousov 			if (sbuf_finish(&sbarg) == 0) {
187447636e4SEdward Tomasz Napierala 				len = sbuf_len(&sbarg) - 1;
18800d17cf3SKonstantin Belousov 				if (len > 0)
18900d17cf3SKonstantin Belousov 					len--;
19000d17cf3SKonstantin Belousov 			} else {
191447636e4SEdward Tomasz Napierala 				len = sizeof(psinfo->pr_psargs) - 1;
19200d17cf3SKonstantin Belousov 			}
193447636e4SEdward Tomasz Napierala 			sbuf_delete(&sbarg);
194447636e4SEdward Tomasz Napierala 		}
19500d17cf3SKonstantin Belousov 		if (error != 0 || len == 0 || (ssize_t)len == -1)
196447636e4SEdward Tomasz Napierala 			strlcpy(psinfo->pr_psargs, p->p_comm,
197447636e4SEdward Tomasz Napierala 			    sizeof(psinfo->pr_psargs));
198447636e4SEdward Tomasz Napierala 		else {
199447636e4SEdward Tomasz Napierala 			KASSERT(len < sizeof(psinfo->pr_psargs),
200447636e4SEdward Tomasz Napierala 			    ("len is too long: %zu vs %zu", len,
201447636e4SEdward Tomasz Napierala 			    sizeof(psinfo->pr_psargs)));
202447636e4SEdward Tomasz Napierala 			cp = psinfo->pr_psargs;
203447636e4SEdward Tomasz Napierala 			end = cp + len - 1;
204447636e4SEdward Tomasz Napierala 			for (;;) {
205447636e4SEdward Tomasz Napierala 				cp = memchr(cp, '\0', end - cp);
206447636e4SEdward Tomasz Napierala 				if (cp == NULL)
207447636e4SEdward Tomasz Napierala 					break;
208447636e4SEdward Tomasz Napierala 				*cp = ' ';
209447636e4SEdward Tomasz Napierala 			}
210447636e4SEdward Tomasz Napierala 		}
211447636e4SEdward Tomasz Napierala 		psinfo->pr_pid = p->p_pid;
212447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, psinfo, sizeof(*psinfo));
213447636e4SEdward Tomasz Napierala 		free(psinfo, M_TEMP);
214447636e4SEdward Tomasz Napierala 	}
215447636e4SEdward Tomasz Napierala 	*sizep = sizeof(*psinfo);
216447636e4SEdward Tomasz Napierala }
217447636e4SEdward Tomasz Napierala 
218447636e4SEdward Tomasz Napierala static void
219447636e4SEdward Tomasz Napierala __linuxN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
220447636e4SEdward Tomasz Napierala {
221447636e4SEdward Tomasz Napierala 	struct thread *td;
222447636e4SEdward Tomasz Napierala 	linux_elf_prstatus_t *status;
2236f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
224447636e4SEdward Tomasz Napierala 	struct reg32 pr_reg;
225447636e4SEdward Tomasz Napierala #else
226447636e4SEdward Tomasz Napierala 	struct reg pr_reg;
227447636e4SEdward Tomasz Napierala #endif
228447636e4SEdward Tomasz Napierala 
229447636e4SEdward Tomasz Napierala 	td = arg;
230447636e4SEdward Tomasz Napierala 	if (sb != NULL) {
231447636e4SEdward Tomasz Napierala 		KASSERT(*sizep == sizeof(*status), ("invalid size"));
232447636e4SEdward Tomasz Napierala 		status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
233447636e4SEdward Tomasz Napierala 
234447636e4SEdward Tomasz Napierala 		/*
235447636e4SEdward Tomasz Napierala 		 * XXX: Some fields missing.
236447636e4SEdward Tomasz Napierala 		 */
237447636e4SEdward Tomasz Napierala 		status->pr_cursig = td->td_proc->p_sig;
238447636e4SEdward Tomasz Napierala 		status->pr_pid = td->td_tid;
239447636e4SEdward Tomasz Napierala 
2406f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
241447636e4SEdward Tomasz Napierala 		fill_regs32(td, &pr_reg);
242447636e4SEdward Tomasz Napierala #else
243447636e4SEdward Tomasz Napierala 		fill_regs(td, &pr_reg);
244447636e4SEdward Tomasz Napierala #endif
245447636e4SEdward Tomasz Napierala 		bsd_to_linux_regset(&pr_reg, &status->pr_reg);
246447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, status, sizeof(*status));
247447636e4SEdward Tomasz Napierala 		free(status, M_TEMP);
248447636e4SEdward Tomasz Napierala 	}
249447636e4SEdward Tomasz Napierala 	*sizep = sizeof(*status);
250447636e4SEdward Tomasz Napierala }
251447636e4SEdward Tomasz Napierala 
252447636e4SEdward Tomasz Napierala static void
253447636e4SEdward Tomasz Napierala __linuxN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep)
254447636e4SEdward Tomasz Napierala {
255447636e4SEdward Tomasz Napierala 	struct thread *td;
256447636e4SEdward Tomasz Napierala 	linux_elf_prfpregset_t *fpregset;
257447636e4SEdward Tomasz Napierala 
258447636e4SEdward Tomasz Napierala 	td = arg;
259447636e4SEdward Tomasz Napierala 	if (sb != NULL) {
260447636e4SEdward Tomasz Napierala 		KASSERT(*sizep == sizeof(*fpregset), ("invalid size"));
261447636e4SEdward Tomasz Napierala 		fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK);
2626f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
263447636e4SEdward Tomasz Napierala 		fill_fpregs32(td, fpregset);
264447636e4SEdward Tomasz Napierala #else
265447636e4SEdward Tomasz Napierala 		fill_fpregs(td, fpregset);
266447636e4SEdward Tomasz Napierala #endif
267447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, fpregset, sizeof(*fpregset));
268447636e4SEdward Tomasz Napierala 		free(fpregset, M_TEMP);
269447636e4SEdward Tomasz Napierala 	}
270447636e4SEdward Tomasz Napierala 	*sizep = sizeof(*fpregset);
271447636e4SEdward Tomasz Napierala }
272447636e4SEdward Tomasz Napierala 
273447636e4SEdward Tomasz Napierala /*
274447636e4SEdward Tomasz Napierala  * Allow for MD specific notes, as well as any MD
275447636e4SEdward Tomasz Napierala  * specific preparations for writing MI notes.
276447636e4SEdward Tomasz Napierala  */
277447636e4SEdward Tomasz Napierala static void
278447636e4SEdward Tomasz Napierala __linuxN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep)
279447636e4SEdward Tomasz Napierala {
280447636e4SEdward Tomasz Napierala 	struct thread *td;
281447636e4SEdward Tomasz Napierala 	void *buf;
282447636e4SEdward Tomasz Napierala 	size_t size;
283447636e4SEdward Tomasz Napierala 
284447636e4SEdward Tomasz Napierala 	td = arg;
285447636e4SEdward Tomasz Napierala 	size = *sizep;
286447636e4SEdward Tomasz Napierala 	if (size != 0 && sb != NULL)
287447636e4SEdward Tomasz Napierala 		buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK);
288447636e4SEdward Tomasz Napierala 	else
289447636e4SEdward Tomasz Napierala 		buf = NULL;
290447636e4SEdward Tomasz Napierala 	size = 0;
291447636e4SEdward Tomasz Napierala 	__elfN(dump_thread)(td, buf, &size);
292447636e4SEdward Tomasz Napierala 	KASSERT(sb == NULL || *sizep == size, ("invalid size"));
293447636e4SEdward Tomasz Napierala 	if (size != 0 && sb != NULL)
294447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, buf, size);
295447636e4SEdward Tomasz Napierala 	free(buf, M_TEMP);
296447636e4SEdward Tomasz Napierala 	*sizep = size;
297447636e4SEdward Tomasz Napierala }
298447636e4SEdward Tomasz Napierala 
299447636e4SEdward Tomasz Napierala static void
300447636e4SEdward Tomasz Napierala __linuxN(note_nt_auxv)(void *arg, struct sbuf *sb, size_t *sizep)
301447636e4SEdward Tomasz Napierala {
302447636e4SEdward Tomasz Napierala 	struct proc *p;
303447636e4SEdward Tomasz Napierala 	size_t size;
304447636e4SEdward Tomasz Napierala 
305447636e4SEdward Tomasz Napierala 	p = arg;
306447636e4SEdward Tomasz Napierala 	if (sb == NULL) {
307447636e4SEdward Tomasz Napierala 		size = 0;
308447636e4SEdward Tomasz Napierala 		sb = sbuf_new(NULL, NULL, LINUX_AT_COUNT * sizeof(Elf_Auxinfo),
309447636e4SEdward Tomasz Napierala 		    SBUF_FIXEDLEN);
310447636e4SEdward Tomasz Napierala 		sbuf_set_drain(sb, sbuf_count_drain, &size);
311447636e4SEdward Tomasz Napierala 		PHOLD(p);
312447636e4SEdward Tomasz Napierala 		proc_getauxv(curthread, p, sb);
313447636e4SEdward Tomasz Napierala 		PRELE(p);
314447636e4SEdward Tomasz Napierala 		sbuf_finish(sb);
315447636e4SEdward Tomasz Napierala 		sbuf_delete(sb);
316447636e4SEdward Tomasz Napierala 		*sizep = size;
317447636e4SEdward Tomasz Napierala 	} else {
318447636e4SEdward Tomasz Napierala 		PHOLD(p);
319447636e4SEdward Tomasz Napierala 		proc_getauxv(curthread, p, sb);
320447636e4SEdward Tomasz Napierala 		PRELE(p);
321447636e4SEdward Tomasz Napierala 	}
322447636e4SEdward Tomasz Napierala }
3236039e966SDmitry Chagin 
3246039e966SDmitry Chagin /*
3256039e966SDmitry Chagin  * Copy strings out to the new process address space, constructing new arg
3266039e966SDmitry Chagin  * and env vector tables. Return a pointer to the base so that it can be used
3276039e966SDmitry Chagin  * as the initial stack pointer.
3286039e966SDmitry Chagin  */
3296039e966SDmitry Chagin int
3306039e966SDmitry Chagin __linuxN(copyout_strings)(struct image_params *imgp, uintptr_t *stack_base)
3316039e966SDmitry Chagin {
3326039e966SDmitry Chagin 	char canary[LINUX_AT_RANDOM_LEN];
3336039e966SDmitry Chagin 	char **vectp;
3346039e966SDmitry Chagin 	char *stringp;
3356039e966SDmitry Chagin 	uintptr_t destp, ustringp;
3366039e966SDmitry Chagin 	struct ps_strings *arginfo;
3376039e966SDmitry Chagin 	struct proc *p;
3386039e966SDmitry Chagin 	size_t execpath_len;
3396039e966SDmitry Chagin 	int argc, envc;
3406039e966SDmitry Chagin 	int error;
3416039e966SDmitry Chagin 
3426039e966SDmitry Chagin 	p = imgp->proc;
3436039e966SDmitry Chagin 	destp =	PROC_PS_STRINGS(p);
3446039e966SDmitry Chagin 	arginfo = imgp->ps_strings = (void *)destp;
3456039e966SDmitry Chagin 
3466039e966SDmitry Chagin 	/*
3476039e966SDmitry Chagin 	 * Copy the image path for the rtld.
3486039e966SDmitry Chagin 	 */
3496039e966SDmitry Chagin 	if (imgp->execpath != NULL && imgp->auxargs != NULL) {
3506039e966SDmitry Chagin 		execpath_len = strlen(imgp->execpath) + 1;
3516039e966SDmitry Chagin 		destp -= execpath_len;
3526039e966SDmitry Chagin 		destp = rounddown2(destp, sizeof(void *));
3536039e966SDmitry Chagin 		imgp->execpathp = (void *)destp;
3546039e966SDmitry Chagin 		error = copyout(imgp->execpath, imgp->execpathp, execpath_len);
3556039e966SDmitry Chagin 		if (error != 0)
3566039e966SDmitry Chagin 			return (error);
3576039e966SDmitry Chagin 	}
3586039e966SDmitry Chagin 
3596039e966SDmitry Chagin 	/*
3606039e966SDmitry Chagin 	 * Prepare the canary for SSP.
3616039e966SDmitry Chagin 	 */
3626039e966SDmitry Chagin 	arc4rand(canary, sizeof(canary), 0);
3636039e966SDmitry Chagin 	destp -= sizeof(canary);
3646039e966SDmitry Chagin 	imgp->canary = (void *)destp;
3656039e966SDmitry Chagin 	error = copyout(canary, imgp->canary, sizeof(canary));
3666039e966SDmitry Chagin 	if (error != 0)
3676039e966SDmitry Chagin 		return (error);
3686039e966SDmitry Chagin 	imgp->canarylen = sizeof(canary);
3696039e966SDmitry Chagin 
3706039e966SDmitry Chagin 	/*
3716039e966SDmitry Chagin 	 * Allocate room for the argument and environment strings.
3726039e966SDmitry Chagin 	 */
3736039e966SDmitry Chagin 	destp -= ARG_MAX - imgp->args->stringspace;
3746039e966SDmitry Chagin 	destp = rounddown2(destp, sizeof(void *));
3756039e966SDmitry Chagin 	ustringp = destp;
3766039e966SDmitry Chagin 
3776039e966SDmitry Chagin 	if (imgp->auxargs) {
3786039e966SDmitry Chagin 		/*
3796039e966SDmitry Chagin 		 * Allocate room on the stack for the ELF auxargs
3806039e966SDmitry Chagin 		 * array.  It has up to LINUX_AT_COUNT entries.
3816039e966SDmitry Chagin 		 */
3826039e966SDmitry Chagin 		destp -= LINUX_AT_COUNT * sizeof(Elf_Auxinfo);
3836039e966SDmitry Chagin 		destp = rounddown2(destp, sizeof(void *));
3846039e966SDmitry Chagin 	}
3856039e966SDmitry Chagin 
3866039e966SDmitry Chagin 	vectp = (char **)destp;
3876039e966SDmitry Chagin 
3886039e966SDmitry Chagin 	/*
3896039e966SDmitry Chagin 	 * Allocate room for the argv[] and env vectors including the
3906039e966SDmitry Chagin 	 * terminating NULL pointers.
3916039e966SDmitry Chagin 	 */
3926039e966SDmitry Chagin 	vectp -= imgp->args->argc + 1 + imgp->args->envc + 1;
3936039e966SDmitry Chagin 
3946039e966SDmitry Chagin 	/*
3956039e966SDmitry Chagin 	 * Starting with 2.24, glibc depends on a 16-byte stack alignment.
3966039e966SDmitry Chagin 	 */
3976039e966SDmitry Chagin 	vectp = (char **)((((uintptr_t)vectp + 8) & ~0xF) - 8);
3986039e966SDmitry Chagin 
3996039e966SDmitry Chagin 	/*
4006039e966SDmitry Chagin 	 * vectp also becomes our initial stack base
4016039e966SDmitry Chagin 	 */
4026039e966SDmitry Chagin 	*stack_base = (uintptr_t)vectp;
4036039e966SDmitry Chagin 
4046039e966SDmitry Chagin 	stringp = imgp->args->begin_argv;
4056039e966SDmitry Chagin 	argc = imgp->args->argc;
4066039e966SDmitry Chagin 	envc = imgp->args->envc;
4076039e966SDmitry Chagin 
4086039e966SDmitry Chagin 	/*
4096039e966SDmitry Chagin 	 * Copy out strings - arguments and environment.
4106039e966SDmitry Chagin 	 */
4116039e966SDmitry Chagin 	error = copyout(stringp, (void *)ustringp,
4126039e966SDmitry Chagin 	    ARG_MAX - imgp->args->stringspace);
4136039e966SDmitry Chagin 	if (error != 0)
4146039e966SDmitry Chagin 		return (error);
4156039e966SDmitry Chagin 
4166039e966SDmitry Chagin 	/*
4176039e966SDmitry Chagin 	 * Fill in "ps_strings" struct for ps, w, etc.
4186039e966SDmitry Chagin 	 */
4196039e966SDmitry Chagin 	imgp->argv = vectp;
4206039e966SDmitry Chagin 	if (suword(&arginfo->ps_argvstr, (long)(intptr_t)vectp) != 0 ||
4216039e966SDmitry Chagin 	    suword32(&arginfo->ps_nargvstr, argc) != 0)
4226039e966SDmitry Chagin 		return (EFAULT);
4236039e966SDmitry Chagin 
4246039e966SDmitry Chagin 	/*
4256039e966SDmitry Chagin 	 * Fill in argument portion of vector table.
4266039e966SDmitry Chagin 	 */
4276039e966SDmitry Chagin 	for (; argc > 0; --argc) {
4286039e966SDmitry Chagin 		if (suword(vectp++, ustringp) != 0)
4296039e966SDmitry Chagin 			return (EFAULT);
4306039e966SDmitry Chagin 		while (*stringp++ != 0)
4316039e966SDmitry Chagin 			ustringp++;
4326039e966SDmitry Chagin 		ustringp++;
4336039e966SDmitry Chagin 	}
4346039e966SDmitry Chagin 
4356039e966SDmitry Chagin 	/* a null vector table pointer separates the argp's from the envp's */
4366039e966SDmitry Chagin 	if (suword(vectp++, 0) != 0)
4376039e966SDmitry Chagin 		return (EFAULT);
4386039e966SDmitry Chagin 
4396039e966SDmitry Chagin 	imgp->envv = vectp;
4406039e966SDmitry Chagin 	if (suword(&arginfo->ps_envstr, (long)(intptr_t)vectp) != 0 ||
4416039e966SDmitry Chagin 	    suword32(&arginfo->ps_nenvstr, envc) != 0)
4426039e966SDmitry Chagin 		return (EFAULT);
4436039e966SDmitry Chagin 
4446039e966SDmitry Chagin 	/*
4456039e966SDmitry Chagin 	 * Fill in environment portion of vector table.
4466039e966SDmitry Chagin 	 */
4476039e966SDmitry Chagin 	for (; envc > 0; --envc) {
4486039e966SDmitry Chagin 		if (suword(vectp++, ustringp) != 0)
4496039e966SDmitry Chagin 			return (EFAULT);
4506039e966SDmitry Chagin 		while (*stringp++ != 0)
4516039e966SDmitry Chagin 			ustringp++;
4526039e966SDmitry Chagin 		ustringp++;
4536039e966SDmitry Chagin 	}
4546039e966SDmitry Chagin 
4556039e966SDmitry Chagin 	/* end of vector table is a null pointer */
4566039e966SDmitry Chagin 	if (suword(vectp, 0) != 0)
4576039e966SDmitry Chagin 		return (EFAULT);
4586039e966SDmitry Chagin 
4596039e966SDmitry Chagin 	if (imgp->auxargs) {
4606039e966SDmitry Chagin 		vectp++;
4616039e966SDmitry Chagin 		error = imgp->sysent->sv_copyout_auxargs(imgp,
4626039e966SDmitry Chagin 		    (uintptr_t)vectp);
4636039e966SDmitry Chagin 		if (error != 0)
4646039e966SDmitry Chagin 			return (error);
4656039e966SDmitry Chagin 	}
4666039e966SDmitry Chagin 
4676039e966SDmitry Chagin 	return (0);
4686039e966SDmitry Chagin }
46995b86034SDmitry Chagin 
47095b86034SDmitry Chagin bool
47195b86034SDmitry Chagin linux_trans_osrel(const Elf_Note *note, int32_t *osrel)
47295b86034SDmitry Chagin {
47395b86034SDmitry Chagin 	const Elf32_Word *desc;
47495b86034SDmitry Chagin 	uintptr_t p;
47595b86034SDmitry Chagin 
47695b86034SDmitry Chagin 	p = (uintptr_t)(note + 1);
47795b86034SDmitry Chagin 	p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
47895b86034SDmitry Chagin 
47995b86034SDmitry Chagin 	desc = (const Elf32_Word *)p;
48095b86034SDmitry Chagin 	if (desc[0] != GNU_ABI_LINUX)
48195b86034SDmitry Chagin 		return (false);
48295b86034SDmitry Chagin 	/*
48395b86034SDmitry Chagin 	 * For Linux we encode osrel using the Linux convention of
48495b86034SDmitry Chagin 	 * 	(version << 16) | (major << 8) | (minor)
48595b86034SDmitry Chagin 	 * See macro in linux_mib.h
48695b86034SDmitry Chagin 	 */
48795b86034SDmitry Chagin 	*osrel = LINUX_KERNVER(desc[1], desc[2], desc[3]);
48895b86034SDmitry Chagin 
48995b86034SDmitry Chagin 	return (true);
49095b86034SDmitry Chagin }
4917d8c9839SDmitry Chagin 
4927d8c9839SDmitry Chagin int
4937d8c9839SDmitry Chagin __linuxN(copyout_auxargs)(struct image_params *imgp, uintptr_t base)
4947d8c9839SDmitry Chagin {
4957d8c9839SDmitry Chagin 	Elf_Auxargs *args;
4967d8c9839SDmitry Chagin 	Elf_Auxinfo *aarray, *pos;
4977d8c9839SDmitry Chagin 	struct proc *p;
4987d8c9839SDmitry Chagin 	int error, issetugid;
4997d8c9839SDmitry Chagin 
5007d8c9839SDmitry Chagin 	p = imgp->proc;
5017d8c9839SDmitry Chagin 	issetugid = p->p_flag & P_SUGID ? 1 : 0;
5027d8c9839SDmitry Chagin 	args = imgp->auxargs;
5037d8c9839SDmitry Chagin 	aarray = pos = malloc(LINUX_AT_COUNT * sizeof(*pos), M_TEMP,
5047d8c9839SDmitry Chagin 	    M_WAITOK | M_ZERO);
5057d8c9839SDmitry Chagin 
5067d8c9839SDmitry Chagin 	__linuxN(arch_copyout_auxargs)(imgp, &pos);
5077d8c9839SDmitry Chagin 	/*
5087d8c9839SDmitry Chagin 	 * Do not export AT_CLKTCK when emulating Linux kernel prior to 2.4.0,
5097d8c9839SDmitry Chagin 	 * as it has appeared in the 2.4.0-rc7 first time.
5107d8c9839SDmitry Chagin 	 * Being exported, AT_CLKTCK is returned by sysconf(_SC_CLK_TCK),
5117d8c9839SDmitry Chagin 	 * glibc falls back to the hard-coded CLK_TCK value when aux entry
5127d8c9839SDmitry Chagin 	 * is not present.
5137d8c9839SDmitry Chagin 	 * Also see linux_times() implementation.
5147d8c9839SDmitry Chagin 	 */
5157d8c9839SDmitry Chagin 	if (linux_kernver(curthread) >= LINUX_KERNVER_2004000)
5167d8c9839SDmitry Chagin 		AUXARGS_ENTRY(pos, LINUX_AT_CLKTCK, stclohz);
5177d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz);
5187d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_PHDR, args->phdr);
5197d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_PHENT, args->phent);
5207d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum);
5217d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_BASE, args->base);
5227d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_FLAGS, args->flags);
5237d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_ENTRY, args->entry);
5247d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_UID, imgp->proc->p_ucred->cr_ruid);
5257d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_EUID, imgp->proc->p_ucred->cr_svuid);
5267d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_GID, imgp->proc->p_ucred->cr_rgid);
5277d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_EGID, imgp->proc->p_ucred->cr_svgid);
5287d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, LINUX_AT_SECURE, issetugid);
52940c36c46SDmitry Chagin 	if (p->p_osrel >= LINUX_KERNVER_2006030 || p->p_osrel == 0)
5307d8c9839SDmitry Chagin 		AUXARGS_ENTRY_PTR(pos, LINUX_AT_RANDOM, imgp->canary);
53170eab81dSDmitry Chagin 	if ((p->p_osrel >= LINUX_KERNVER_2006026 || p->p_osrel == 0) &&
53270eab81dSDmitry Chagin 	    imgp->execpathp != 0)
5337d8c9839SDmitry Chagin 		AUXARGS_ENTRY(pos, LINUX_AT_EXECFN, PTROUT(imgp->execpathp));
5347d8c9839SDmitry Chagin 	if (args->execfd != -1)
5357d8c9839SDmitry Chagin 		AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd);
536b7a6bcddSDmitry Chagin 	if (p->p_osrel >= LINUX_KERNVER_5013000 || p->p_osrel == 0)
537*66e8f1f7SDmitry Chagin 		AUXARGS_ENTRY(pos, LINUX_AT_MINSIGSTKSZ,
538*66e8f1f7SDmitry Chagin 		    imgp->sysent->sv_minsigstksz);
5397d8c9839SDmitry Chagin 	AUXARGS_ENTRY(pos, AT_NULL, 0);
5407d8c9839SDmitry Chagin 
5417d8c9839SDmitry Chagin 	free(imgp->auxargs, M_TEMP);
5427d8c9839SDmitry Chagin 	imgp->auxargs = NULL;
5437d8c9839SDmitry Chagin 	KASSERT(pos - aarray <= LINUX_AT_COUNT, ("Too many auxargs"));
5447d8c9839SDmitry Chagin 
5457d8c9839SDmitry Chagin 	error = copyout(aarray, PTRIN(base), sizeof(*aarray) * LINUX_AT_COUNT);
5467d8c9839SDmitry Chagin 	free(aarray, M_TEMP);
5477d8c9839SDmitry Chagin 	return (error);
5487d8c9839SDmitry Chagin }
549