2 * FreeBSD setup_initial_stack() implementation.
4 * Copyright (c) 2013-14 Stacey D. Son
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #ifndef TARGET_OS_STACK_H
21 #define TARGET_OS_STACK_H
23 #include <sys/param.h>
24 #include "target_arch_sigtramp.h"
25 #include "qemu/guest-random.h"
28 * The inital FreeBSD stack is as follows:
29 * (see kern/kern_exec.c exec_copyout_strings() )
31 * Hi Address -> char **ps_argvstr (struct ps_strings for ps, w, etc.)
32 * unsigned ps_nargvstr
34 * PS_STRINGS -> unsigned ps_nenvstr
36 * machine dependent sigcode (sv_sigcode of size
39 * execpath (absolute image path for rtld)
41 * SSP Canary (sizeof(long) * 8)
43 * page sizes array (usually sizeof(u_long) )
45 * "destp" -> argv, env strings (up to 262144 bytes)
47 static inline int setup_initial_stack(struct bsd_binprm
*bprm
,
48 abi_ulong
*ret_addr
, abi_ulong
*stringp
)
51 abi_ulong stack_hi_addr
;
52 size_t execpath_len
, stringspace
;
53 abi_ulong destp
, argvp
, envp
, p
;
54 struct target_ps_strings ps_strs
;
55 char canary
[sizeof(abi_long
) * 8];
57 stack_hi_addr
= p
= target_stkbas
+ target_stksiz
;
59 /* Save some space for ps_strings. */
60 p
-= sizeof(struct target_ps_strings
);
62 /* Add machine depedent sigcode. */
63 p
-= TARGET_SZSIGCODE
;
64 if (setup_sigtramp(p
, (unsigned)offsetof(struct target_sigframe
, sf_uc
),
65 TARGET_FREEBSD_NR_sigreturn
)) {
70 execpath_len
= strlen(bprm
->fullpath
) + 1;
71 p
-= roundup(execpath_len
, sizeof(abi_ulong
));
72 if (memcpy_to_target(p
, bprm
->fullpath
, execpath_len
)) {
77 /* Add canary for SSP. */
78 qemu_guest_getrandom_nofail(canary
, sizeof(canary
));
79 p
-= roundup(sizeof(canary
), sizeof(abi_ulong
));
80 if (memcpy_to_target(p
, canary
, sizeof(canary
))) {
84 /* Add page sizes array. */
85 p
-= sizeof(abi_ulong
);
86 if (put_user_ual(TARGET_PAGE_SIZE
, p
)) {
91 * Deviate from FreeBSD stack layout: force stack to new page here
92 * so that signal trampoline is not sharing the page with user stack
93 * frames. This is actively harmful in qemu as it marks pages with
94 * code it translated as read-only, which is somewhat problematic
95 * for user trying to use the stack as intended.
97 p
= rounddown(p
, TARGET_PAGE_SIZE
);
99 /* Calculate the string space needed */
101 for (i
= 0; i
< bprm
->argc
; ++i
) {
102 stringspace
+= strlen(bprm
->argv
[i
]) + 1;
104 for (i
= 0; i
< bprm
->envc
; ++i
) {
105 stringspace
+= strlen(bprm
->envp
[i
]) + 1;
107 if (stringspace
> TARGET_ARG_MAX
) {
111 /* Make room for the argv and envp strings */
112 destp
= rounddown(p
- stringspace
, sizeof(abi_ulong
));
113 p
= argvp
= destp
- (bprm
->argc
+ bprm
->envc
+ 2) * sizeof(abi_ulong
);
114 /* Remember the strings pointer */
119 * Add argv strings. Note that the argv[] vectors are added by
120 * loader_build_argptr()
122 /* XXX need to make room for auxargs */
123 ps_strs
.ps_argvstr
= tswapl(argvp
);
124 ps_strs
.ps_nargvstr
= tswap32(bprm
->argc
);
125 for (i
= 0; i
< bprm
->argc
; ++i
) {
126 size_t len
= strlen(bprm
->argv
[i
]) + 1;
128 if (memcpy_to_target(destp
, bprm
->argv
[i
], len
)) {
132 if (put_user_ual(destp
, argvp
)) {
136 argvp
+= sizeof(abi_ulong
);
139 if (put_user_ual(0, argvp
)) {
144 * Add env strings. Note that the envp[] vectors are added by
145 * loader_build_argptr().
147 envp
= argvp
+ sizeof(abi_ulong
);
148 ps_strs
.ps_envstr
= tswapl(envp
);
149 ps_strs
.ps_nenvstr
= tswap32(bprm
->envc
);
150 for (i
= 0; i
< bprm
->envc
; ++i
) {
151 size_t len
= strlen(bprm
->envp
[i
]) + 1;
153 if (memcpy_to_target(destp
, bprm
->envp
[i
], len
)) {
157 if (put_user_ual(destp
, envp
)) {
161 envp
+= sizeof(abi_ulong
);
164 if (put_user_ual(0, envp
)) {
168 if (memcpy_to_target(stack_hi_addr
- sizeof(ps_strs
), &ps_strs
,
181 #endif /* TARGET_OS_STACK_H */