summaryrefslogtreecommitdiff
path: root/minix/lib/libc/sys/stack_utils.c
blob: 0c8ad9e01bfc9feb79e68d57a42801abb614f30a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* Utilities to generate a proper C stack.
 *
 * Author: Lionel A. Sambuc. 
 */

#define _MINIX_SYSTEM

#include <sys/cdefs.h>
#include "namespace.h"
#include <lib.h>

#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <sys/exec_elf.h>
#include <sys/exec.h>

/* Create a stack image that only needs to be patched up slightly by
 * the kernel to be used for the process to be executed.
 *
 * Every pointers are stored here as offset from the frame base, and
 * will be adapted as required for the new process address space.
 *
 * The following parameters are passed by register to either __start
 * for static binaries, or _rtld_start for dynamic ones:
 *     *fct, *ObjEntry, *ps_string
 *
 * The following stack layout is expected by _rtld():
 *
 * | XXXXXXXXXX | 0x0000_00000
 * |  ...       |
 * |  ...       | Top of the stack
 * | argc       |
 * | *argv1     | points to the first char of the argv1
 * |  ...       |
 * | *argvN     |
 * | NULL       |
 * | *env1      | 
 * |  ...       |
 * | *envN      |
 * | NULL       |
 * | ElfAuxV1   |
 * |  ...       |
 * | ElfAuxVX   |
 * | AuxExecName| fully resolve executable name, as an ASCIIZ string,
 *                at most PMEF_EXECNAMELEN1 long.
 * 
 * Here we put first the strings, then word-align, then ps_strings, to
 * comply with the expected layout of NetBSD. This seems to matter for
 * the NetBSD ps command, so let's make sure we are compatible...
 *
 * | strings    | Maybe followed by some padding to word-align.
 * | **argv     | \
 * | argc       |  +---> ps_string structure content.
 * | **env      |  |
 * | envc       | /
 * | sigcode    | On NetBSD, there may be a compatibility stub here,
 * +------------+    for native code, it is not present.
 *   Stack Base , 0xF000_0000, descending stack.
 */

/* The minimum size of the frame is composed of:
 * argc, the NULL terminator for argv as well as one for
 * environ, the ELF Aux vectors, executable name and the
 * ps_strings struct. */
#define STACK_MIN_SZ \
( \
	sizeof(int) + sizeof(void *) * 2 + \
	sizeof(AuxInfo) * PMEF_AUXVECTORS + PMEF_EXECNAMELEN1 + \
	sizeof(struct ps_strings) \
)

/***************************************************************************** 
 * Computes stack size, argc, envc, for a given set of path, argv, envp.     *
 *****************************************************************************/
void minix_stack_params(const char *path, char * const *argv, char * const *envp,
	size_t *stack_size,  char *overflow, int *argc, int *envc)
{
	char * const *p;
	size_t const min_size = STACK_MIN_SZ;

	*stack_size = min_size;	/* Size of the new initial stack. */
	*overflow = 0;		/* No overflow yet. */
	*argc = 0;		/* Argument count. */
	*envc = 0;		/* Environment count */

	/* Compute and add the size required to store argv and env. */
	for (p = argv; *p != NULL; p++) {
		size_t const n = sizeof(*p) + strlen(*p) + 1;
		*stack_size += n;
		if (*stack_size < n) {
			*overflow = 1;
		}
		(*argc)++;
	}

	for (p = envp; p && *p != NULL; p++) {
		size_t const n = sizeof(*p) + strlen(*p) + 1;
		*stack_size += n;
		if (*stack_size < n) {
			*overflow = 1;
		}
		(*envc)++;
	}

	/* Compute the aligned frame size. */
	*stack_size = (*stack_size + sizeof(void *) - 1) &
		 ~(sizeof(void *) - 1);

	if (*stack_size < min_size) {
		/* This is possible only in case of overflow. */
		*overflow = 1;
	}
}

/*****************************************************************************
 * Generate a stack in the buffer frame, ready to be used.                   *
 *****************************************************************************/
void minix_stack_fill(const char *path, int argc, char * const *argv,
	int envc, char * const *envp, size_t stack_size, char *frame,
	int *vsp, struct ps_strings **psp)
{
	char * const *p;

	/* Frame pointers (a.k.a stack pointer within the buffer in current
	 * address space.) */
	char *fp;	/* byte aligned */
	char **fpw;	/* word aligned */

	size_t const min_size = STACK_MIN_SZ;

	/* Virtual address of the stack pointer, in new memory space. */
	*vsp = minix_get_user_sp() - stack_size;

	/* Fill in the frame now. */
	fpw = (char **) frame;
	*fpw++ = (char *) argc;

	/* The strings themselves are stored after the aux vectors,
	 * cf. top comment. */
	fp = frame + (min_size - sizeof(struct ps_strings)) + 
		(envc + argc) * sizeof(char *);
	
	/* Fill in argv and the environment, as well as copy the strings
	 * themselves. */
	for (p = argv; *p != NULL; p++) {
		size_t const n = strlen(*p) + 1;
		*fpw++= (char *)(*vsp + (fp - frame));
		memcpy(fp, *p, n);
		fp += n;
	}
	*fpw++ = NULL;

	for (p = envp; p && *p != NULL; p++) {
		size_t const n = strlen(*p) + 1;
		*fpw++= (char *)(*vsp + (fp - frame));
		memcpy(fp, *p, n);
		fp += n;
	}
	*fpw++ = NULL;

	/* Padding, because of the stack alignement. */
	while ((size_t)fp % sizeof(void *)) *fp++= 0;

	/* Fill in the ps_string struct*/
	*psp = (struct ps_strings *) fp;

	(*psp)->ps_argvstr = (char **)(*vsp + sizeof(argc));
	(*psp)->ps_nargvstr = argc;
	(*psp)->ps_envstr = (*psp)->ps_argvstr + argc + 1;
	(*psp)->ps_nenvstr = envc;
}