summaryrefslogtreecommitdiff
path: root/usr/src/lib/libkvm/common/kvm_getcmd.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libkvm/common/kvm_getcmd.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libkvm/common/kvm_getcmd.c')
-rw-r--r--usr/src/lib/libkvm/common/kvm_getcmd.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/usr/src/lib/libkvm/common/kvm_getcmd.c b/usr/src/lib/libkvm/common/kvm_getcmd.c
new file mode 100644
index 0000000000..66b3d5c70d
--- /dev/null
+++ b/usr/src/lib/libkvm/common/kvm_getcmd.c
@@ -0,0 +1,422 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <kvm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <strings.h>
+#include <sys/types32.h>
+
+#define _SYSCALL32
+
+/*
+ * VERSION FOR MACHINES WITH STACKS GROWING DOWNWARD IN MEMORY
+ *
+ * On program entry, the top of the stack frame looks like this:
+ *
+ * hi: |-----------------------|
+ * | unspecified |
+ * |-----------------------|+
+ * | : | \
+ * | arg and env strings | > no more than NCARGS bytes
+ * | : | /
+ * |-----------------------|+
+ * | unspecified |
+ * |-----------------------|
+ * | null auxiliary vector |
+ * |-----------------------|
+ * | auxiliary vector |
+ * | (2-word entries) |
+ * | : |
+ * |-----------------------|
+ * | (char *)0 |
+ * |-----------------------|
+ * | ptrs to env strings |
+ * | : |
+ * |-----------------------|
+ * | (char *)0 |
+ * |-----------------------|
+ * | ptrs to arg strings |
+ * | (argc = # of ptrs) |
+ * | : |
+ * |-----------------------|
+ * | argc |
+ * low: |-----------------------|
+ */
+
+#define RoundUp(v, t) (((v) + sizeof (t) - 1) & ~(sizeof (t) - 1))
+
+static int
+kvm_getcmd32(kvm_t *kd,
+ struct proc *p, struct user *u, char ***arg, char ***env)
+{
+#if defined(_LP64) || defined(lint)
+ size_t size32;
+ void *stack32;
+ int i, argc, envc;
+ int auxc = 0;
+ size_t asize, esize;
+ char **argv = NULL;
+ char **envp = NULL;
+ size_t strpoolsz;
+ int aptrcount;
+ int eptrcount;
+ caddr_t stackp;
+ ptrdiff_t reloc;
+ char *str;
+
+ /*
+ * Bring the entire stack into memory first, size it
+ * as an LP64 user stack, then allocate and copy into
+ * the buffer(s) to be returned to the caller.
+ */
+ size32 = (size_t)p->p_usrstack - (size_t)u->u_argv;
+ if ((stack32 = malloc(size32)) == NULL)
+ return (-1);
+ if (kvm_uread(kd, (uintptr_t)u->u_argv, stack32, size32) != size32) {
+ free(stack32);
+ return (-1);
+ }
+
+ /*
+ * Find the interesting sizes of a 32-bit stack.
+ */
+ argc = u->u_argc;
+ stackp = (caddr_t)stack32 + ((1 + argc) * sizeof (caddr32_t));
+
+ for (envc = 0; *(caddr32_t *)stackp; envc++) {
+ stackp += sizeof (caddr32_t);
+ if ((stackp - (caddr_t)stack32) >= size32) {
+ free(stack32);
+ return (-1);
+ }
+ }
+
+ if (u->u_auxv[0].a_type != AT_NULL) {
+ stackp += sizeof (caddr32_t);
+ for (auxc = 0; *(int32_t *)stackp; auxc++) {
+ stackp += 2 * sizeof (caddr32_t);
+ if ((stackp - (caddr_t)stack32) >= size32) {
+ free(stack32);
+ return (-1);
+ }
+ }
+ auxc++; /* terminating AT_NULL record */
+ }
+
+ /*
+ * Compute the sizes of the stuff we're going to allocate or copy.
+ */
+ eptrcount = (envc + 1) + 2 * auxc;
+ aptrcount = (argc + 1) + eptrcount;
+ strpoolsz = size32 - aptrcount * sizeof (caddr32_t);
+
+ asize = aptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t);
+ if (arg && (argv = calloc(1, asize + sizeof (uintptr_t))) == NULL) {
+ free(stack32);
+ return (-1);
+ }
+
+ esize = eptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t);
+ if (env && (envp = calloc(1, esize + sizeof (uintptr_t))) == NULL) {
+ if (argv)
+ free(argv);
+ free(stack32);
+ return (-1);
+ }
+
+ /*
+ * Walk up the 32-bit stack, filling in the 64-bit argv and envp
+ * as we go.
+ */
+ stackp = (caddr_t)stack32;
+
+ /*
+ * argument vector
+ */
+ if (argv) {
+ for (i = 0; i < argc; i++) {
+ argv[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp);
+ stackp += sizeof (caddr32_t);
+ }
+ argv[argc] = 0;
+ stackp += sizeof (caddr32_t);
+ } else
+ stackp += (1 + argc) * sizeof (caddr32_t);
+
+ /*
+ * environment
+ */
+ if (envp) {
+ for (i = 0; i < envc; i++) {
+ envp[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp);
+ stackp += sizeof (caddr32_t);
+ }
+ envp[envc] = 0;
+ stackp += sizeof (caddr32_t);
+ } else
+ stackp += (1 + envc) * sizeof (caddr32_t);
+
+ /*
+ * auxiliary vector (skip it..)
+ */
+ stackp += auxc * (sizeof (int32_t) + sizeof (uint32_t));
+
+ /*
+ * Copy the string pool, untranslated
+ */
+ if (argv)
+ (void) memcpy(argv + aptrcount, (void *)stackp, strpoolsz);
+ if (envp)
+ (void) memcpy(envp + eptrcount, (void *)stackp, strpoolsz);
+
+ free(stack32);
+
+ /*
+ * Relocate the pointers to point at the newly allocated space.
+ * Use the same algorithms as kvm_getcmd to handle naughty
+ * changes to the argv and envp arrays.
+ */
+ if (argv) {
+ char *argv_null = (char *)argv + asize;
+
+ reloc = (char *)(argv + aptrcount) - (char *)
+ ((caddr_t)u->u_argv + aptrcount * sizeof (caddr32_t));
+
+ for (i = 0; i < argc; i++)
+ if (argv[i] != NULL) {
+ str = (argv[i] += reloc);
+ if (str < (char *)argv ||
+ str >= (char *)argv + asize)
+ argv[i] = argv_null;
+ }
+
+ *arg = argv;
+ }
+
+ if (envp) {
+ char *envp_null = (char *)envp + esize;
+ char *last_str;
+
+ reloc = (char *)(envp + eptrcount) - (char *)
+ ((caddr_t)u->u_envp + eptrcount * sizeof (caddr32_t));
+
+ last_str = (char *)((size_t)u->u_argv +
+ (1 + argc) * sizeof (caddr32_t) + reloc);
+ if (last_str < (char *)envp ||
+ last_str >= (char *)envp + esize)
+ last_str = envp_null;
+
+ for (i = 0; i < envc; i++) {
+ str = (envp[i] += reloc);
+ if (str < (char *)envp ||
+ str >= (char *)envp + esize) {
+ if (last_str != envp_null)
+ envp[i] = (char *)((size_t)last_str +
+ strlen(last_str) + 1);
+ else
+ envp[i] = envp_null;
+ }
+ last_str = envp[i];
+ }
+ *env = envp;
+ }
+#endif /* _LP64 || lint */
+ return (0);
+}
+
+/*
+ * reconstruct an argv-like argument list from the target process
+ */
+int
+kvm_getcmd(kvm_t *kd,
+ struct proc *proc, struct user *u, char ***arg, char ***env)
+{
+ size_t asize;
+ size_t esize;
+ size_t offset;
+ int i;
+ int argc;
+ char **argv = NULL;
+ char **envp = NULL;
+ char *str;
+ char *last_str;
+ char *argv_null; /* Known null in the returned argv */
+ char *envp_null; /* Known null in the returned envp */
+
+ if (proc->p_flag & SSYS) /* system process */
+ return (-1);
+
+ /*
+ * Protect against proc structs found by kvm_nextproc()
+ * while the kernel was doing a fork(). Such a proc struct
+ * may have p_usrstack set but a still zeroed uarea.
+ * We wouldn't want to unecessarily allocate 4GB memory ...
+ */
+ if (u->u_argv == NULL || u->u_envp == NULL)
+ return (-1);
+
+ /*
+ * If this is a 32-bit process running on a 64-bit system,
+ * then the stack is laid out using ILP32 pointers, not LP64.
+ * To minimize potential confusion, we blow it up to "LP64
+ * shaped" right here.
+ */
+ if (proc->p_model != DATAMODEL_NATIVE &&
+ proc->p_model == DATAMODEL_ILP32)
+ return (kvm_getcmd32(kd, proc, u, arg, env));
+
+ /*
+ * Space for the stack, from the argument vector. An additional
+ * word is added to guarantee a NULL word terminates the buffer.
+ */
+ if (arg) {
+ asize = (size_t)proc->p_usrstack - (size_t)u->u_argv;
+ if ((argv = malloc(asize + sizeof (uintptr_t))) == NULL)
+ return (-1);
+ argv_null = (char *)argv + asize;
+ *(uintptr_t *)argv_null = 0;
+ }
+
+ /*
+ * Space for the stack, from the environment vector. An additional
+ * word is added to guarantee a NULL word terminates the buffer.
+ */
+ if (env) {
+ esize = (size_t)proc->p_usrstack - (size_t)u->u_envp;
+ if ((envp = malloc(esize + sizeof (uintptr_t))) == NULL) {
+ if (argv)
+ free(argv);
+ return (-1);
+ }
+ envp_null = (char *)envp + esize;
+ *(uintptr_t *)envp_null = 0;
+ }
+
+ argc = u->u_argc;
+
+ if (argv) {
+ /* read the whole initial stack */
+ if (kvm_uread(kd,
+ (uintptr_t)u->u_argv, argv, asize) != asize) {
+ free(argv);
+ if (envp)
+ free(envp);
+ return (-1);
+ }
+ argv[argc] = 0;
+ if (envp) {
+ /*
+ * Copy it to the malloc()d space for the envp array
+ */
+ (void) memcpy(envp, &argv[argc + 1], esize);
+ }
+ } else if (envp) {
+ /* read most of the initial stack (excluding argv) */
+ if (kvm_uread(kd,
+ (uintptr_t)u->u_envp, envp, esize) != esize) {
+ free(envp);
+ return (-1);
+ }
+ }
+
+ /*
+ * Relocate and sanity check the argv array. Entries which have
+ * been explicity nulled are left that way. Entries which have
+ * been replaced are pointed to a null string. Well behaved apps
+ * don't do any of this.
+ */
+ if (argv) {
+ /* relocate the argv[] addresses */
+ offset = (char *)argv - (char *)u->u_argv;
+ for (i = 0; i < argc; i++) {
+ if (argv[i] != NULL) {
+ str = (argv[i] += offset);
+ if (str < (char *)argv ||
+ str >= (char *)argv + asize)
+ argv[i] = argv_null;
+ }
+ }
+ argv[i] = NULL;
+ *arg = argv;
+ }
+
+ /*
+ * Relocate and sanity check the envp array. A null entry indicates
+ * the end of the environment. Entries which point outside of the
+ * initial stack are replaced with what must have been the initial
+ * value based on the known ordering of the string table by the
+ * kernel. If stack corruption prevents the calculation of the
+ * location of an initial string value, a pointer to a null string
+ * is returned. To return a null pointer would prematurely terminate
+ * the list. Well behaved apps do set pointers outside of the
+ * initial stack via the putenv(3C) library routine.
+ */
+ if (envp) {
+
+ /*
+ * Determine the start of the environment strings as one
+ * past the last argument string.
+ */
+ offset = (char *)envp - (char *)u->u_envp;
+
+ if (kvm_uread(kd,
+ (uintptr_t)u->u_argv + (argc - 1) * sizeof (char **),
+ &last_str, sizeof (last_str)) != sizeof (last_str))
+ last_str = envp_null;
+ else {
+ last_str += offset;
+ if (last_str < (char *)envp ||
+ last_str >= (char *)envp + esize)
+ last_str = envp_null;
+ }
+
+ /*
+ * Relocate the envp[] addresses, while ensuring that we
+ * don't return bad addresses.
+ */
+ for (i = 0; envp[i] != NULL; i++) {
+ str = (envp[i] += offset);
+ if (str < (char *)envp || str >= (char *)envp + esize) {
+ if (last_str != envp_null)
+ envp[i] = last_str +
+ strlen(last_str) + 1;
+ else
+ envp[i] = envp_null;
+ }
+ last_str = envp[i];
+ }
+ envp[i] = NULL;
+ *env = envp;
+ }
+
+ return (0);
+}