xref: /freebsd/sys/compat/linux/linux_elf.c (revision 6039e966ff276ff6bcb57f2f70e7d8ff376b24fd)
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>
44*6039e966SDmitry 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>
54*6039e966SDmitry Chagin #include <sys/sysent.h>
55*6039e966SDmitry Chagin 
56*6039e966SDmitry Chagin #include <vm/vm.h>
57*6039e966SDmitry Chagin #include <vm/pmap.h>
58*6039e966SDmitry 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>
70*6039e966SDmitry Chagin #include <compat/linux/linux_misc.h>
71447636e4SEdward Tomasz Napierala 
7274465145SDmitry Chagin struct l_elf_siginfo {
7374465145SDmitry Chagin 	l_int		si_signo;
7474465145SDmitry Chagin 	l_int		si_code;
7574465145SDmitry Chagin 	l_int		si_errno;
7674465145SDmitry Chagin };
7774465145SDmitry Chagin 
7874465145SDmitry Chagin typedef struct linux_pt_regset l_elf_gregset_t;
7974465145SDmitry Chagin 
8074465145SDmitry Chagin struct linux_elf_prstatus {
8174465145SDmitry Chagin 	struct l_elf_siginfo pr_info;
8274465145SDmitry Chagin 	l_short		pr_cursig;
8374465145SDmitry Chagin 	l_ulong		pr_sigpend;
8474465145SDmitry Chagin 	l_ulong		pr_sighold;
8574465145SDmitry Chagin 	l_pid_t		pr_pid;
8674465145SDmitry Chagin 	l_pid_t		pr_ppid;
8774465145SDmitry Chagin 	l_pid_t		pr_pgrp;
8874465145SDmitry Chagin 	l_pid_t		pr_sid;
8974465145SDmitry Chagin 	l_timeval	pr_utime;
9074465145SDmitry Chagin 	l_timeval	pr_stime;
9174465145SDmitry Chagin 	l_timeval	pr_cutime;
9274465145SDmitry Chagin 	l_timeval	pr_cstime;
9374465145SDmitry Chagin 	l_elf_gregset_t	pr_reg;
9474465145SDmitry Chagin 	l_int		pr_fpvalid;
9574465145SDmitry Chagin };
96447636e4SEdward Tomasz Napierala 
97447636e4SEdward Tomasz Napierala #define	LINUX_NT_AUXV	6
98447636e4SEdward Tomasz Napierala 
99447636e4SEdward Tomasz Napierala static void __linuxN(note_fpregset)(void *, struct sbuf *, size_t *);
100447636e4SEdward Tomasz Napierala static void __linuxN(note_prpsinfo)(void *, struct sbuf *, size_t *);
101447636e4SEdward Tomasz Napierala static void __linuxN(note_prstatus)(void *, struct sbuf *, size_t *);
102447636e4SEdward Tomasz Napierala static void __linuxN(note_threadmd)(void *, struct sbuf *, size_t *);
103447636e4SEdward Tomasz Napierala static void __linuxN(note_nt_auxv)(void *, struct sbuf *, size_t *);
104447636e4SEdward Tomasz Napierala 
105447636e4SEdward Tomasz Napierala void
106447636e4SEdward Tomasz Napierala __linuxN(prepare_notes)(struct thread *td, struct note_info_list *list,
107447636e4SEdward Tomasz Napierala     size_t *sizep)
108447636e4SEdward Tomasz Napierala {
109447636e4SEdward Tomasz Napierala 	struct proc *p;
110447636e4SEdward Tomasz Napierala 	struct thread *thr;
111447636e4SEdward Tomasz Napierala 	size_t size;
112447636e4SEdward Tomasz Napierala 
113447636e4SEdward Tomasz Napierala 	p = td->td_proc;
114447636e4SEdward Tomasz Napierala 	size = 0;
115447636e4SEdward Tomasz Napierala 
116447636e4SEdward Tomasz Napierala 	/*
117447636e4SEdward Tomasz Napierala 	 * To have the debugger select the right thread (LWP) as the initial
118447636e4SEdward Tomasz Napierala 	 * thread, we dump the state of the thread passed to us in td first.
119447636e4SEdward Tomasz Napierala 	 * This is the thread that causes the core dump and thus likely to
120447636e4SEdward Tomasz Napierala 	 * be the right thread one wants to have selected in the debugger.
121447636e4SEdward Tomasz Napierala 	 */
122447636e4SEdward Tomasz Napierala 	thr = td;
123447636e4SEdward Tomasz Napierala 	while (thr != NULL) {
124447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
125447636e4SEdward Tomasz Napierala 		    NT_PRSTATUS, __linuxN(note_prstatus), thr);
126447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
127447636e4SEdward Tomasz Napierala 		    NT_PRPSINFO, __linuxN(note_prpsinfo), p);
128447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
129447636e4SEdward Tomasz Napierala 		    LINUX_NT_AUXV, __linuxN(note_nt_auxv), p);
130447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
131447636e4SEdward Tomasz Napierala 		    NT_FPREGSET, __linuxN(note_fpregset), thr);
132447636e4SEdward Tomasz Napierala 		size += __elfN(register_note)(td, list,
133447636e4SEdward Tomasz Napierala 		    -1, __linuxN(note_threadmd), thr);
134447636e4SEdward Tomasz Napierala 
135447636e4SEdward Tomasz Napierala 		thr = thr == td ? TAILQ_FIRST(&p->p_threads) :
136447636e4SEdward Tomasz Napierala 		    TAILQ_NEXT(thr, td_plist);
137447636e4SEdward Tomasz Napierala 		if (thr == td)
138447636e4SEdward Tomasz Napierala 			thr = TAILQ_NEXT(thr, td_plist);
139447636e4SEdward Tomasz Napierala 	}
140447636e4SEdward Tomasz Napierala 
141447636e4SEdward Tomasz Napierala 	*sizep = size;
142447636e4SEdward Tomasz Napierala }
143447636e4SEdward Tomasz Napierala 
144447636e4SEdward Tomasz Napierala typedef struct linux_elf_prstatus linux_elf_prstatus_t;
1456f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
146447636e4SEdward Tomasz Napierala typedef struct prpsinfo32 linux_elf_prpsinfo_t;
147447636e4SEdward Tomasz Napierala typedef struct fpreg32 linux_elf_prfpregset_t;
148447636e4SEdward Tomasz Napierala #else
149447636e4SEdward Tomasz Napierala typedef prpsinfo_t linux_elf_prpsinfo_t;
150447636e4SEdward Tomasz Napierala typedef prfpregset_t linux_elf_prfpregset_t;
151447636e4SEdward Tomasz Napierala #endif
152447636e4SEdward Tomasz Napierala 
153447636e4SEdward Tomasz Napierala static void
154447636e4SEdward Tomasz Napierala __linuxN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep)
155447636e4SEdward Tomasz Napierala {
156447636e4SEdward Tomasz Napierala 	struct sbuf sbarg;
157447636e4SEdward Tomasz Napierala 	size_t len;
158447636e4SEdward Tomasz Napierala 	char *cp, *end;
159447636e4SEdward Tomasz Napierala 	struct proc *p;
160447636e4SEdward Tomasz Napierala 	linux_elf_prpsinfo_t *psinfo;
161447636e4SEdward Tomasz Napierala 	int error;
162447636e4SEdward Tomasz Napierala 
163447636e4SEdward Tomasz Napierala 	p = arg;
164447636e4SEdward Tomasz Napierala 	if (sb != NULL) {
165447636e4SEdward Tomasz Napierala 		KASSERT(*sizep == sizeof(*psinfo), ("invalid size"));
166447636e4SEdward Tomasz Napierala 		psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK);
167447636e4SEdward Tomasz Napierala 		psinfo->pr_version = PRPSINFO_VERSION;
168447636e4SEdward Tomasz Napierala 		psinfo->pr_psinfosz = sizeof(linux_elf_prpsinfo_t);
169447636e4SEdward Tomasz Napierala 		strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname));
170447636e4SEdward Tomasz Napierala 		PROC_LOCK(p);
171447636e4SEdward Tomasz Napierala 		if (p->p_args != NULL) {
172447636e4SEdward Tomasz Napierala 			len = sizeof(psinfo->pr_psargs) - 1;
173447636e4SEdward Tomasz Napierala 			if (len > p->p_args->ar_length)
174447636e4SEdward Tomasz Napierala 				len = p->p_args->ar_length;
175447636e4SEdward Tomasz Napierala 			memcpy(psinfo->pr_psargs, p->p_args->ar_args, len);
176447636e4SEdward Tomasz Napierala 			PROC_UNLOCK(p);
177447636e4SEdward Tomasz Napierala 			error = 0;
178447636e4SEdward Tomasz Napierala 		} else {
179447636e4SEdward Tomasz Napierala 			_PHOLD(p);
180447636e4SEdward Tomasz Napierala 			PROC_UNLOCK(p);
181447636e4SEdward Tomasz Napierala 			sbuf_new(&sbarg, psinfo->pr_psargs,
182447636e4SEdward Tomasz Napierala 			    sizeof(psinfo->pr_psargs), SBUF_FIXEDLEN);
183447636e4SEdward Tomasz Napierala 			error = proc_getargv(curthread, p, &sbarg);
184447636e4SEdward Tomasz Napierala 			PRELE(p);
18500d17cf3SKonstantin Belousov 			if (sbuf_finish(&sbarg) == 0) {
186447636e4SEdward Tomasz Napierala 				len = sbuf_len(&sbarg) - 1;
18700d17cf3SKonstantin Belousov 				if (len > 0)
18800d17cf3SKonstantin Belousov 					len--;
18900d17cf3SKonstantin Belousov 			} else {
190447636e4SEdward Tomasz Napierala 				len = sizeof(psinfo->pr_psargs) - 1;
19100d17cf3SKonstantin Belousov 			}
192447636e4SEdward Tomasz Napierala 			sbuf_delete(&sbarg);
193447636e4SEdward Tomasz Napierala 		}
19400d17cf3SKonstantin Belousov 		if (error != 0 || len == 0 || (ssize_t)len == -1)
195447636e4SEdward Tomasz Napierala 			strlcpy(psinfo->pr_psargs, p->p_comm,
196447636e4SEdward Tomasz Napierala 			    sizeof(psinfo->pr_psargs));
197447636e4SEdward Tomasz Napierala 		else {
198447636e4SEdward Tomasz Napierala 			KASSERT(len < sizeof(psinfo->pr_psargs),
199447636e4SEdward Tomasz Napierala 			    ("len is too long: %zu vs %zu", len,
200447636e4SEdward Tomasz Napierala 			    sizeof(psinfo->pr_psargs)));
201447636e4SEdward Tomasz Napierala 			cp = psinfo->pr_psargs;
202447636e4SEdward Tomasz Napierala 			end = cp + len - 1;
203447636e4SEdward Tomasz Napierala 			for (;;) {
204447636e4SEdward Tomasz Napierala 				cp = memchr(cp, '\0', end - cp);
205447636e4SEdward Tomasz Napierala 				if (cp == NULL)
206447636e4SEdward Tomasz Napierala 					break;
207447636e4SEdward Tomasz Napierala 				*cp = ' ';
208447636e4SEdward Tomasz Napierala 			}
209447636e4SEdward Tomasz Napierala 		}
210447636e4SEdward Tomasz Napierala 		psinfo->pr_pid = p->p_pid;
211447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, psinfo, sizeof(*psinfo));
212447636e4SEdward Tomasz Napierala 		free(psinfo, M_TEMP);
213447636e4SEdward Tomasz Napierala 	}
214447636e4SEdward Tomasz Napierala 	*sizep = sizeof(*psinfo);
215447636e4SEdward Tomasz Napierala }
216447636e4SEdward Tomasz Napierala 
217447636e4SEdward Tomasz Napierala static void
218447636e4SEdward Tomasz Napierala __linuxN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
219447636e4SEdward Tomasz Napierala {
220447636e4SEdward Tomasz Napierala 	struct thread *td;
221447636e4SEdward Tomasz Napierala 	linux_elf_prstatus_t *status;
2226f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
223447636e4SEdward Tomasz Napierala 	struct reg32 pr_reg;
224447636e4SEdward Tomasz Napierala #else
225447636e4SEdward Tomasz Napierala 	struct reg pr_reg;
226447636e4SEdward Tomasz Napierala #endif
227447636e4SEdward Tomasz Napierala 
228447636e4SEdward Tomasz Napierala 	td = arg;
229447636e4SEdward Tomasz Napierala 	if (sb != NULL) {
230447636e4SEdward Tomasz Napierala 		KASSERT(*sizep == sizeof(*status), ("invalid size"));
231447636e4SEdward Tomasz Napierala 		status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
232447636e4SEdward Tomasz Napierala 
233447636e4SEdward Tomasz Napierala 		/*
234447636e4SEdward Tomasz Napierala 		 * XXX: Some fields missing.
235447636e4SEdward Tomasz Napierala 		 */
236447636e4SEdward Tomasz Napierala 		status->pr_cursig = td->td_proc->p_sig;
237447636e4SEdward Tomasz Napierala 		status->pr_pid = td->td_tid;
238447636e4SEdward Tomasz Napierala 
2396f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
240447636e4SEdward Tomasz Napierala 		fill_regs32(td, &pr_reg);
241447636e4SEdward Tomasz Napierala #else
242447636e4SEdward Tomasz Napierala 		fill_regs(td, &pr_reg);
243447636e4SEdward Tomasz Napierala #endif
244447636e4SEdward Tomasz Napierala 		bsd_to_linux_regset(&pr_reg, &status->pr_reg);
245447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, status, sizeof(*status));
246447636e4SEdward Tomasz Napierala 		free(status, M_TEMP);
247447636e4SEdward Tomasz Napierala 	}
248447636e4SEdward Tomasz Napierala 	*sizep = sizeof(*status);
249447636e4SEdward Tomasz Napierala }
250447636e4SEdward Tomasz Napierala 
251447636e4SEdward Tomasz Napierala static void
252447636e4SEdward Tomasz Napierala __linuxN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep)
253447636e4SEdward Tomasz Napierala {
254447636e4SEdward Tomasz Napierala 	struct thread *td;
255447636e4SEdward Tomasz Napierala 	linux_elf_prfpregset_t *fpregset;
256447636e4SEdward Tomasz Napierala 
257447636e4SEdward Tomasz Napierala 	td = arg;
258447636e4SEdward Tomasz Napierala 	if (sb != NULL) {
259447636e4SEdward Tomasz Napierala 		KASSERT(*sizep == sizeof(*fpregset), ("invalid size"));
260447636e4SEdward Tomasz Napierala 		fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK);
2616f8439dbSDmitry Chagin #ifdef COMPAT_LINUX32
262447636e4SEdward Tomasz Napierala 		fill_fpregs32(td, fpregset);
263447636e4SEdward Tomasz Napierala #else
264447636e4SEdward Tomasz Napierala 		fill_fpregs(td, fpregset);
265447636e4SEdward Tomasz Napierala #endif
266447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, fpregset, sizeof(*fpregset));
267447636e4SEdward Tomasz Napierala 		free(fpregset, M_TEMP);
268447636e4SEdward Tomasz Napierala 	}
269447636e4SEdward Tomasz Napierala 	*sizep = sizeof(*fpregset);
270447636e4SEdward Tomasz Napierala }
271447636e4SEdward Tomasz Napierala 
272447636e4SEdward Tomasz Napierala /*
273447636e4SEdward Tomasz Napierala  * Allow for MD specific notes, as well as any MD
274447636e4SEdward Tomasz Napierala  * specific preparations for writing MI notes.
275447636e4SEdward Tomasz Napierala  */
276447636e4SEdward Tomasz Napierala static void
277447636e4SEdward Tomasz Napierala __linuxN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep)
278447636e4SEdward Tomasz Napierala {
279447636e4SEdward Tomasz Napierala 	struct thread *td;
280447636e4SEdward Tomasz Napierala 	void *buf;
281447636e4SEdward Tomasz Napierala 	size_t size;
282447636e4SEdward Tomasz Napierala 
283447636e4SEdward Tomasz Napierala 	td = arg;
284447636e4SEdward Tomasz Napierala 	size = *sizep;
285447636e4SEdward Tomasz Napierala 	if (size != 0 && sb != NULL)
286447636e4SEdward Tomasz Napierala 		buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK);
287447636e4SEdward Tomasz Napierala 	else
288447636e4SEdward Tomasz Napierala 		buf = NULL;
289447636e4SEdward Tomasz Napierala 	size = 0;
290447636e4SEdward Tomasz Napierala 	__elfN(dump_thread)(td, buf, &size);
291447636e4SEdward Tomasz Napierala 	KASSERT(sb == NULL || *sizep == size, ("invalid size"));
292447636e4SEdward Tomasz Napierala 	if (size != 0 && sb != NULL)
293447636e4SEdward Tomasz Napierala 		sbuf_bcat(sb, buf, size);
294447636e4SEdward Tomasz Napierala 	free(buf, M_TEMP);
295447636e4SEdward Tomasz Napierala 	*sizep = size;
296447636e4SEdward Tomasz Napierala }
297447636e4SEdward Tomasz Napierala 
298447636e4SEdward Tomasz Napierala static void
299447636e4SEdward Tomasz Napierala __linuxN(note_nt_auxv)(void *arg, struct sbuf *sb, size_t *sizep)
300447636e4SEdward Tomasz Napierala {
301447636e4SEdward Tomasz Napierala 	struct proc *p;
302447636e4SEdward Tomasz Napierala 	size_t size;
303447636e4SEdward Tomasz Napierala 
304447636e4SEdward Tomasz Napierala 	p = arg;
305447636e4SEdward Tomasz Napierala 	if (sb == NULL) {
306447636e4SEdward Tomasz Napierala 		size = 0;
307447636e4SEdward Tomasz Napierala 		sb = sbuf_new(NULL, NULL, LINUX_AT_COUNT * sizeof(Elf_Auxinfo),
308447636e4SEdward Tomasz Napierala 		    SBUF_FIXEDLEN);
309447636e4SEdward Tomasz Napierala 		sbuf_set_drain(sb, sbuf_count_drain, &size);
310447636e4SEdward Tomasz Napierala 		PHOLD(p);
311447636e4SEdward Tomasz Napierala 		proc_getauxv(curthread, p, sb);
312447636e4SEdward Tomasz Napierala 		PRELE(p);
313447636e4SEdward Tomasz Napierala 		sbuf_finish(sb);
314447636e4SEdward Tomasz Napierala 		sbuf_delete(sb);
315447636e4SEdward Tomasz Napierala 		*sizep = size;
316447636e4SEdward Tomasz Napierala 	} else {
317447636e4SEdward Tomasz Napierala 		PHOLD(p);
318447636e4SEdward Tomasz Napierala 		proc_getauxv(curthread, p, sb);
319447636e4SEdward Tomasz Napierala 		PRELE(p);
320447636e4SEdward Tomasz Napierala 	}
321447636e4SEdward Tomasz Napierala }
322*6039e966SDmitry Chagin 
323*6039e966SDmitry Chagin /*
324*6039e966SDmitry Chagin  * Copy strings out to the new process address space, constructing new arg
325*6039e966SDmitry Chagin  * and env vector tables. Return a pointer to the base so that it can be used
326*6039e966SDmitry Chagin  * as the initial stack pointer.
327*6039e966SDmitry Chagin  */
328*6039e966SDmitry Chagin int
329*6039e966SDmitry Chagin __linuxN(copyout_strings)(struct image_params *imgp, uintptr_t *stack_base)
330*6039e966SDmitry Chagin {
331*6039e966SDmitry Chagin 	char canary[LINUX_AT_RANDOM_LEN];
332*6039e966SDmitry Chagin 	char **vectp;
333*6039e966SDmitry Chagin 	char *stringp;
334*6039e966SDmitry Chagin 	uintptr_t destp, ustringp;
335*6039e966SDmitry Chagin 	struct ps_strings *arginfo;
336*6039e966SDmitry Chagin 	struct proc *p;
337*6039e966SDmitry Chagin 	size_t execpath_len;
338*6039e966SDmitry Chagin 	int argc, envc;
339*6039e966SDmitry Chagin 	int error;
340*6039e966SDmitry Chagin 
341*6039e966SDmitry Chagin 	p = imgp->proc;
342*6039e966SDmitry Chagin 	destp =	PROC_PS_STRINGS(p);
343*6039e966SDmitry Chagin 	arginfo = imgp->ps_strings = (void *)destp;
344*6039e966SDmitry Chagin 
345*6039e966SDmitry Chagin 	/*
346*6039e966SDmitry Chagin 	 * Copy the image path for the rtld.
347*6039e966SDmitry Chagin 	 */
348*6039e966SDmitry Chagin 	if (imgp->execpath != NULL && imgp->auxargs != NULL) {
349*6039e966SDmitry Chagin 		execpath_len = strlen(imgp->execpath) + 1;
350*6039e966SDmitry Chagin 		destp -= execpath_len;
351*6039e966SDmitry Chagin 		destp = rounddown2(destp, sizeof(void *));
352*6039e966SDmitry Chagin 		imgp->execpathp = (void *)destp;
353*6039e966SDmitry Chagin 		error = copyout(imgp->execpath, imgp->execpathp, execpath_len);
354*6039e966SDmitry Chagin 		if (error != 0)
355*6039e966SDmitry Chagin 			return (error);
356*6039e966SDmitry Chagin 	}
357*6039e966SDmitry Chagin 
358*6039e966SDmitry Chagin 	/*
359*6039e966SDmitry Chagin 	 * Prepare the canary for SSP.
360*6039e966SDmitry Chagin 	 */
361*6039e966SDmitry Chagin 	arc4rand(canary, sizeof(canary), 0);
362*6039e966SDmitry Chagin 	destp -= sizeof(canary);
363*6039e966SDmitry Chagin 	imgp->canary = (void *)destp;
364*6039e966SDmitry Chagin 	error = copyout(canary, imgp->canary, sizeof(canary));
365*6039e966SDmitry Chagin 	if (error != 0)
366*6039e966SDmitry Chagin 		return (error);
367*6039e966SDmitry Chagin 	imgp->canarylen = sizeof(canary);
368*6039e966SDmitry Chagin 
369*6039e966SDmitry Chagin 	/*
370*6039e966SDmitry Chagin 	 * Allocate room for the argument and environment strings.
371*6039e966SDmitry Chagin 	 */
372*6039e966SDmitry Chagin 	destp -= ARG_MAX - imgp->args->stringspace;
373*6039e966SDmitry Chagin 	destp = rounddown2(destp, sizeof(void *));
374*6039e966SDmitry Chagin 	ustringp = destp;
375*6039e966SDmitry Chagin 
376*6039e966SDmitry Chagin 	if (imgp->auxargs) {
377*6039e966SDmitry Chagin 		/*
378*6039e966SDmitry Chagin 		 * Allocate room on the stack for the ELF auxargs
379*6039e966SDmitry Chagin 		 * array.  It has up to LINUX_AT_COUNT entries.
380*6039e966SDmitry Chagin 		 */
381*6039e966SDmitry Chagin 		destp -= LINUX_AT_COUNT * sizeof(Elf_Auxinfo);
382*6039e966SDmitry Chagin 		destp = rounddown2(destp, sizeof(void *));
383*6039e966SDmitry Chagin 	}
384*6039e966SDmitry Chagin 
385*6039e966SDmitry Chagin 	vectp = (char **)destp;
386*6039e966SDmitry Chagin 
387*6039e966SDmitry Chagin 	/*
388*6039e966SDmitry Chagin 	 * Allocate room for the argv[] and env vectors including the
389*6039e966SDmitry Chagin 	 * terminating NULL pointers.
390*6039e966SDmitry Chagin 	 */
391*6039e966SDmitry Chagin 	vectp -= imgp->args->argc + 1 + imgp->args->envc + 1;
392*6039e966SDmitry Chagin 
393*6039e966SDmitry Chagin 	/*
394*6039e966SDmitry Chagin 	 * Starting with 2.24, glibc depends on a 16-byte stack alignment.
395*6039e966SDmitry Chagin 	 */
396*6039e966SDmitry Chagin 	vectp = (char **)((((uintptr_t)vectp + 8) & ~0xF) - 8);
397*6039e966SDmitry Chagin 
398*6039e966SDmitry Chagin 	/*
399*6039e966SDmitry Chagin 	 * vectp also becomes our initial stack base
400*6039e966SDmitry Chagin 	 */
401*6039e966SDmitry Chagin 	*stack_base = (uintptr_t)vectp;
402*6039e966SDmitry Chagin 
403*6039e966SDmitry Chagin 	stringp = imgp->args->begin_argv;
404*6039e966SDmitry Chagin 	argc = imgp->args->argc;
405*6039e966SDmitry Chagin 	envc = imgp->args->envc;
406*6039e966SDmitry Chagin 
407*6039e966SDmitry Chagin 	/*
408*6039e966SDmitry Chagin 	 * Copy out strings - arguments and environment.
409*6039e966SDmitry Chagin 	 */
410*6039e966SDmitry Chagin 	error = copyout(stringp, (void *)ustringp,
411*6039e966SDmitry Chagin 	    ARG_MAX - imgp->args->stringspace);
412*6039e966SDmitry Chagin 	if (error != 0)
413*6039e966SDmitry Chagin 		return (error);
414*6039e966SDmitry Chagin 
415*6039e966SDmitry Chagin 	/*
416*6039e966SDmitry Chagin 	 * Fill in "ps_strings" struct for ps, w, etc.
417*6039e966SDmitry Chagin 	 */
418*6039e966SDmitry Chagin 	imgp->argv = vectp;
419*6039e966SDmitry Chagin 	if (suword(&arginfo->ps_argvstr, (long)(intptr_t)vectp) != 0 ||
420*6039e966SDmitry Chagin 	    suword32(&arginfo->ps_nargvstr, argc) != 0)
421*6039e966SDmitry Chagin 		return (EFAULT);
422*6039e966SDmitry Chagin 
423*6039e966SDmitry Chagin 	/*
424*6039e966SDmitry Chagin 	 * Fill in argument portion of vector table.
425*6039e966SDmitry Chagin 	 */
426*6039e966SDmitry Chagin 	for (; argc > 0; --argc) {
427*6039e966SDmitry Chagin 		if (suword(vectp++, ustringp) != 0)
428*6039e966SDmitry Chagin 			return (EFAULT);
429*6039e966SDmitry Chagin 		while (*stringp++ != 0)
430*6039e966SDmitry Chagin 			ustringp++;
431*6039e966SDmitry Chagin 		ustringp++;
432*6039e966SDmitry Chagin 	}
433*6039e966SDmitry Chagin 
434*6039e966SDmitry Chagin 	/* a null vector table pointer separates the argp's from the envp's */
435*6039e966SDmitry Chagin 	if (suword(vectp++, 0) != 0)
436*6039e966SDmitry Chagin 		return (EFAULT);
437*6039e966SDmitry Chagin 
438*6039e966SDmitry Chagin 	imgp->envv = vectp;
439*6039e966SDmitry Chagin 	if (suword(&arginfo->ps_envstr, (long)(intptr_t)vectp) != 0 ||
440*6039e966SDmitry Chagin 	    suword32(&arginfo->ps_nenvstr, envc) != 0)
441*6039e966SDmitry Chagin 		return (EFAULT);
442*6039e966SDmitry Chagin 
443*6039e966SDmitry Chagin 	/*
444*6039e966SDmitry Chagin 	 * Fill in environment portion of vector table.
445*6039e966SDmitry Chagin 	 */
446*6039e966SDmitry Chagin 	for (; envc > 0; --envc) {
447*6039e966SDmitry Chagin 		if (suword(vectp++, ustringp) != 0)
448*6039e966SDmitry Chagin 			return (EFAULT);
449*6039e966SDmitry Chagin 		while (*stringp++ != 0)
450*6039e966SDmitry Chagin 			ustringp++;
451*6039e966SDmitry Chagin 		ustringp++;
452*6039e966SDmitry Chagin 	}
453*6039e966SDmitry Chagin 
454*6039e966SDmitry Chagin 	/* end of vector table is a null pointer */
455*6039e966SDmitry Chagin 	if (suword(vectp, 0) != 0)
456*6039e966SDmitry Chagin 		return (EFAULT);
457*6039e966SDmitry Chagin 
458*6039e966SDmitry Chagin 	if (imgp->auxargs) {
459*6039e966SDmitry Chagin 		vectp++;
460*6039e966SDmitry Chagin 		error = imgp->sysent->sv_copyout_auxargs(imgp,
461*6039e966SDmitry Chagin 		    (uintptr_t)vectp);
462*6039e966SDmitry Chagin 		if (error != 0)
463*6039e966SDmitry Chagin 			return (error);
464*6039e966SDmitry Chagin 	}
465*6039e966SDmitry Chagin 
466*6039e966SDmitry Chagin 	return (0);
467*6039e966SDmitry Chagin }
468