import less(1)
[unleashed/tickless.git] / usr / src / lib / libkvm / common / kvm_getcmd.c
blob44f06bdc89e6fd6cc86fd3ef86dff70b153aa6d9
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <kvm.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <kvm.h>
33 #include <strings.h>
34 #include <sys/types32.h>
36 #define _SYSCALL32
39 * VERSION FOR MACHINES WITH STACKS GROWING DOWNWARD IN MEMORY
41 * On program entry, the top of the stack frame looks like this:
43 * hi: |-----------------------|
44 * | unspecified |
45 * |-----------------------|+
46 * | : | \
47 * | arg and env strings | > no more than NCARGS bytes
48 * | : | /
49 * |-----------------------|+
50 * | unspecified |
51 * |-----------------------|
52 * | null auxiliary vector |
53 * |-----------------------|
54 * | auxiliary vector |
55 * | (2-word entries) |
56 * | : |
57 * |-----------------------|
58 * | (char *)0 |
59 * |-----------------------|
60 * | ptrs to env strings |
61 * | : |
62 * |-----------------------|
63 * | (char *)0 |
64 * |-----------------------|
65 * | ptrs to arg strings |
66 * | (argc = # of ptrs) |
67 * | : |
68 * |-----------------------|
69 * | argc |
70 * low: |-----------------------|
73 #define RoundUp(v, t) (((v) + sizeof (t) - 1) & ~(sizeof (t) - 1))
75 static int
76 kvm_getcmd32(kvm_t *kd,
77 struct proc *p, struct user *u, char ***arg, char ***env)
79 #if defined(_LP64) || defined(lint)
80 size_t size32;
81 void *stack32;
82 int i, argc, envc;
83 int auxc = 0;
84 size_t asize, esize;
85 char **argv = NULL;
86 char **envp = NULL;
87 size_t strpoolsz;
88 int aptrcount;
89 int eptrcount;
90 caddr_t stackp;
91 ptrdiff_t reloc;
92 char *str;
95 * Bring the entire stack into memory first, size it
96 * as an LP64 user stack, then allocate and copy into
97 * the buffer(s) to be returned to the caller.
99 size32 = (size_t)p->p_usrstack - (size_t)u->u_argv;
100 if ((stack32 = malloc(size32)) == NULL)
101 return (-1);
102 if (kvm_uread(kd, (uintptr_t)u->u_argv, stack32, size32) != size32) {
103 free(stack32);
104 return (-1);
108 * Find the interesting sizes of a 32-bit stack.
110 argc = u->u_argc;
111 stackp = (caddr_t)stack32 + ((1 + argc) * sizeof (caddr32_t));
113 for (envc = 0; *(caddr32_t *)stackp; envc++) {
114 stackp += sizeof (caddr32_t);
115 if ((stackp - (caddr_t)stack32) >= size32) {
116 free(stack32);
117 return (-1);
121 if (u->u_auxv[0].a_type != AT_NULL) {
122 stackp += sizeof (caddr32_t);
123 for (auxc = 0; *(int32_t *)stackp; auxc++) {
124 stackp += 2 * sizeof (caddr32_t);
125 if ((stackp - (caddr_t)stack32) >= size32) {
126 free(stack32);
127 return (-1);
130 auxc++; /* terminating AT_NULL record */
134 * Compute the sizes of the stuff we're going to allocate or copy.
136 eptrcount = (envc + 1) + 2 * auxc;
137 aptrcount = (argc + 1) + eptrcount;
138 strpoolsz = size32 - aptrcount * sizeof (caddr32_t);
140 asize = aptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t);
141 if (arg && (argv = calloc(1, asize + sizeof (uintptr_t))) == NULL) {
142 free(stack32);
143 return (-1);
146 esize = eptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t);
147 if (env && (envp = calloc(1, esize + sizeof (uintptr_t))) == NULL) {
148 free(argv);
149 free(stack32);
150 return (-1);
154 * Walk up the 32-bit stack, filling in the 64-bit argv and envp
155 * as we go.
157 stackp = (caddr_t)stack32;
160 * argument vector
162 if (argv) {
163 for (i = 0; i < argc; i++) {
164 argv[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp);
165 stackp += sizeof (caddr32_t);
167 argv[argc] = 0;
168 stackp += sizeof (caddr32_t);
169 } else
170 stackp += (1 + argc) * sizeof (caddr32_t);
173 * environment
175 if (envp) {
176 for (i = 0; i < envc; i++) {
177 envp[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp);
178 stackp += sizeof (caddr32_t);
180 envp[envc] = 0;
181 stackp += sizeof (caddr32_t);
182 } else
183 stackp += (1 + envc) * sizeof (caddr32_t);
186 * auxiliary vector (skip it..)
188 stackp += auxc * (sizeof (int32_t) + sizeof (uint32_t));
191 * Copy the string pool, untranslated
193 if (argv)
194 (void) memcpy(argv + aptrcount, (void *)stackp, strpoolsz);
195 if (envp)
196 (void) memcpy(envp + eptrcount, (void *)stackp, strpoolsz);
198 free(stack32);
201 * Relocate the pointers to point at the newly allocated space.
202 * Use the same algorithms as kvm_getcmd to handle naughty
203 * changes to the argv and envp arrays.
205 if (argv) {
206 char *argv_null = (char *)argv + asize;
208 reloc = (char *)(argv + aptrcount) - (char *)
209 ((caddr_t)u->u_argv + aptrcount * sizeof (caddr32_t));
211 for (i = 0; i < argc; i++)
212 if (argv[i] != NULL) {
213 str = (argv[i] += reloc);
214 if (str < (char *)argv ||
215 str >= (char *)argv + asize)
216 argv[i] = argv_null;
219 *arg = argv;
222 if (envp) {
223 char *envp_null = (char *)envp + esize;
224 char *last_str;
226 reloc = (char *)(envp + eptrcount) - (char *)
227 ((caddr_t)u->u_envp + eptrcount * sizeof (caddr32_t));
229 last_str = (char *)((size_t)u->u_argv +
230 (1 + argc) * sizeof (caddr32_t) + reloc);
231 if (last_str < (char *)envp ||
232 last_str >= (char *)envp + esize)
233 last_str = envp_null;
235 for (i = 0; i < envc; i++) {
236 str = (envp[i] += reloc);
237 if (str < (char *)envp ||
238 str >= (char *)envp + esize) {
239 if (last_str != envp_null)
240 envp[i] = (char *)((size_t)last_str +
241 strlen(last_str) + 1);
242 else
243 envp[i] = envp_null;
245 last_str = envp[i];
247 *env = envp;
249 #endif /* _LP64 || lint */
250 return (0);
254 * reconstruct an argv-like argument list from the target process
257 kvm_getcmd(kvm_t *kd,
258 struct proc *proc, struct user *u, char ***arg, char ***env)
260 size_t asize;
261 size_t esize;
262 size_t offset;
263 int i;
264 int argc;
265 char **argv = NULL;
266 char **envp = NULL;
267 char *str;
268 char *last_str;
269 char *argv_null; /* Known null in the returned argv */
270 char *envp_null; /* Known null in the returned envp */
272 if (proc->p_flag & SSYS) /* system process */
273 return (-1);
276 * Protect against proc structs found by kvm_nextproc()
277 * while the kernel was doing a fork(). Such a proc struct
278 * may have p_usrstack set but a still zeroed uarea.
279 * We wouldn't want to unecessarily allocate 4GB memory ...
281 if (u->u_argv == (uintptr_t)NULL || u->u_envp == (uintptr_t)NULL)
282 return (-1);
285 * If this is a 32-bit process running on a 64-bit system,
286 * then the stack is laid out using ILP32 pointers, not LP64.
287 * To minimize potential confusion, we blow it up to "LP64
288 * shaped" right here.
290 if (proc->p_model != DATAMODEL_NATIVE &&
291 proc->p_model == DATAMODEL_ILP32)
292 return (kvm_getcmd32(kd, proc, u, arg, env));
295 * Space for the stack, from the argument vector. An additional
296 * word is added to guarantee a NULL word terminates the buffer.
298 if (arg) {
299 asize = (size_t)proc->p_usrstack - (size_t)u->u_argv;
300 if ((argv = malloc(asize + sizeof (uintptr_t))) == NULL)
301 return (-1);
302 argv_null = (char *)argv + asize;
303 *(uintptr_t *)argv_null = 0;
307 * Space for the stack, from the environment vector. An additional
308 * word is added to guarantee a NULL word terminates the buffer.
310 if (env) {
311 esize = (size_t)proc->p_usrstack - (size_t)u->u_envp;
312 if ((envp = malloc(esize + sizeof (uintptr_t))) == NULL) {
313 free(argv);
314 return (-1);
316 envp_null = (char *)envp + esize;
317 *(uintptr_t *)envp_null = 0;
320 argc = u->u_argc;
322 if (argv) {
323 /* read the whole initial stack */
324 if (kvm_uread(kd,
325 (uintptr_t)u->u_argv, argv, asize) != asize) {
326 free(argv);
327 free(envp);
328 return (-1);
330 argv[argc] = 0;
331 if (envp) {
333 * Copy it to the malloc()d space for the envp array
335 (void) memcpy(envp, &argv[argc + 1], esize);
337 } else if (envp) {
338 /* read most of the initial stack (excluding argv) */
339 if (kvm_uread(kd,
340 (uintptr_t)u->u_envp, envp, esize) != esize) {
341 free(envp);
342 return (-1);
347 * Relocate and sanity check the argv array. Entries which have
348 * been explicity nulled are left that way. Entries which have
349 * been replaced are pointed to a null string. Well behaved apps
350 * don't do any of this.
352 if (argv) {
353 /* relocate the argv[] addresses */
354 offset = (char *)argv - (char *)u->u_argv;
355 for (i = 0; i < argc; i++) {
356 if (argv[i] != NULL) {
357 str = (argv[i] += offset);
358 if (str < (char *)argv ||
359 str >= (char *)argv + asize)
360 argv[i] = argv_null;
363 argv[i] = NULL;
364 *arg = argv;
368 * Relocate and sanity check the envp array. A null entry indicates
369 * the end of the environment. Entries which point outside of the
370 * initial stack are replaced with what must have been the initial
371 * value based on the known ordering of the string table by the
372 * kernel. If stack corruption prevents the calculation of the
373 * location of an initial string value, a pointer to a null string
374 * is returned. To return a null pointer would prematurely terminate
375 * the list. Well behaved apps do set pointers outside of the
376 * initial stack via the putenv(3C) library routine.
378 if (envp) {
381 * Determine the start of the environment strings as one
382 * past the last argument string.
384 offset = (char *)envp - (char *)u->u_envp;
386 if (kvm_uread(kd,
387 (uintptr_t)u->u_argv + (argc - 1) * sizeof (char **),
388 &last_str, sizeof (last_str)) != sizeof (last_str))
389 last_str = envp_null;
390 else {
391 last_str += offset;
392 if (last_str < (char *)envp ||
393 last_str >= (char *)envp + esize)
394 last_str = envp_null;
398 * Relocate the envp[] addresses, while ensuring that we
399 * don't return bad addresses.
401 for (i = 0; envp[i] != NULL; i++) {
402 str = (envp[i] += offset);
403 if (str < (char *)envp || str >= (char *)envp + esize) {
404 if (last_str != envp_null)
405 envp[i] = last_str +
406 strlen(last_str) + 1;
407 else
408 envp[i] = envp_null;
410 last_str = envp[i];
412 envp[i] = NULL;
413 *env = envp;
416 return (0);