diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libkvm/common | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libkvm/common')
-rw-r--r-- | usr/src/lib/libkvm/common/kvm.c | 429 | ||||
-rw-r--r-- | usr/src/lib/libkvm/common/kvm_getcmd.c | 422 | ||||
-rw-r--r-- | usr/src/lib/libkvm/common/llib-lkvm | 56 | ||||
-rw-r--r-- | usr/src/lib/libkvm/common/test.c | 468 |
4 files changed, 1375 insertions, 0 deletions
diff --git a/usr/src/lib/libkvm/common/kvm.c b/usr/src/lib/libkvm/common/kvm.c new file mode 100644 index 0000000000..d3a5dbd81c --- /dev/null +++ b/usr/src/lib/libkvm/common/kvm.c @@ -0,0 +1,429 @@ +/* + * 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 <stdarg.h> +#include <unistd.h> +#include <limits.h> +#include <fcntl.h> +#include <strings.h> +#include <sys/mem.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/dumphdr.h> +#include <sys/sysmacros.h> + +struct _kvmd { + struct dumphdr kvm_dump; + char *kvm_debug; + int kvm_openflag; + int kvm_corefd; + int kvm_kmemfd; + int kvm_memfd; + size_t kvm_coremapsize; + char *kvm_core; + dump_map_t *kvm_map; + pfn_t *kvm_pfn; + struct as *kvm_kas; + proc_t *kvm_practive; + pid_t kvm_pid; + char kvm_namelist[MAXNAMELEN + 1]; + proc_t kvm_proc; +}; + +#define PREAD (ssize_t (*)(int, void *, size_t, offset_t))pread64 +#define PWRITE (ssize_t (*)(int, void *, size_t, offset_t))pwrite64 + +static kvm_t * +fail(kvm_t *kd, const char *err, const char *message, ...) +{ + va_list args; + + va_start(args, message); + if (err || (kd && kd->kvm_debug)) { + (void) fprintf(stderr, "%s: ", err ? err : "KVM_DEBUG"); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + } + va_end(args); + if (kd != NULL) + (void) kvm_close(kd); + return (NULL); +} + +/*ARGSUSED*/ +kvm_t * +kvm_open(const char *namelist, const char *corefile, const char *swapfile, + int flag, const char *err) +{ + kvm_t *kd; + struct stat64 memstat, kmemstat, allkmemstat, corestat; + struct nlist nl[3] = { { "kas" }, { "practive" }, { "" } }; + + if ((kd = calloc(1, sizeof (kvm_t))) == NULL) + return (fail(NULL, err, "cannot allocate space for kvm_t")); + + kd->kvm_corefd = kd->kvm_kmemfd = kd->kvm_memfd = -1; + kd->kvm_debug = getenv("KVM_DEBUG"); + + if ((kd->kvm_openflag = flag) != O_RDONLY && flag != O_RDWR) + return (fail(kd, err, "illegal flag 0x%x to kvm_open()", flag)); + + if (corefile == NULL) + corefile = "/dev/kmem"; + + if (stat64(corefile, &corestat) == -1) + return (fail(kd, err, "cannot stat %s", corefile)); + + if (S_ISCHR(corestat.st_mode)) { + if (stat64("/dev/mem", &memstat) == -1) + return (fail(kd, err, "cannot stat /dev/mem")); + + if (stat64("/dev/kmem", &kmemstat) == -1) + return (fail(kd, err, "cannot stat /dev/kmem")); + + if (stat64("/dev/allkmem", &allkmemstat) == -1) + return (fail(kd, err, "cannot stat /dev/allkmem")); + if (corestat.st_rdev == memstat.st_rdev || + corestat.st_rdev == kmemstat.st_rdev || + corestat.st_rdev == allkmemstat.st_rdev) { + char *kmem = (corestat.st_rdev == allkmemstat.st_rdev ? + "/dev/allkmem" : "/dev/kmem"); + + if ((kd->kvm_kmemfd = open64(kmem, flag)) == -1) + return (fail(kd, err, "cannot open %s", kmem)); + if ((kd->kvm_memfd = open64("/dev/mem", flag)) == -1) + return (fail(kd, err, "cannot open /dev/mem")); + } + } else { + if ((kd->kvm_corefd = open64(corefile, flag)) == -1) + return (fail(kd, err, "cannot open %s", corefile)); + if (pread64(kd->kvm_corefd, &kd->kvm_dump, + sizeof (kd->kvm_dump), 0) != sizeof (kd->kvm_dump)) + return (fail(kd, err, "cannot read dump header")); + if (kd->kvm_dump.dump_magic != DUMP_MAGIC) + return (fail(kd, err, "%s is not a kernel core file " + "(bad magic number %x)", corefile, + kd->kvm_dump.dump_magic)); + if (kd->kvm_dump.dump_version != DUMP_VERSION) + return (fail(kd, err, + "libkvm version (%u) != corefile version (%u)", + DUMP_VERSION, kd->kvm_dump.dump_version)); + if (kd->kvm_dump.dump_wordsize != DUMP_WORDSIZE) + return (fail(kd, err, "%s is a %d-bit core file - " + "cannot examine with %d-bit libkvm", corefile, + kd->kvm_dump.dump_wordsize, DUMP_WORDSIZE)); + /* + * We try to mmap(2) the entire corefile for performance + * (so we can use bcopy(3C) rather than pread(2)). Failing + * that, we insist on at least mmap(2)ing the dump map. + */ + kd->kvm_coremapsize = (size_t)corestat.st_size; + if (corestat.st_size > LONG_MAX || + (kd->kvm_core = mmap64(0, kd->kvm_coremapsize, + PROT_READ, MAP_SHARED, kd->kvm_corefd, 0)) == MAP_FAILED) { + kd->kvm_coremapsize = kd->kvm_dump.dump_data; + if ((kd->kvm_core = mmap64(0, kd->kvm_coremapsize, + PROT_READ, MAP_SHARED, kd->kvm_corefd, 0)) == + MAP_FAILED) + return (fail(kd, err, "cannot mmap corefile")); + } + kd->kvm_map = (void *)(kd->kvm_core + kd->kvm_dump.dump_map); + kd->kvm_pfn = (void *)(kd->kvm_core + kd->kvm_dump.dump_pfn); + } + + if (namelist == NULL) + namelist = "/dev/ksyms"; + + (void) strncpy(kd->kvm_namelist, namelist, MAXNAMELEN); + + if (kvm_nlist(kd, nl) == -1) + return (fail(kd, err, "%s is not a %d-bit kernel namelist", + namelist, DUMP_WORDSIZE)); + + kd->kvm_kas = (struct as *)nl[0].n_value; + kd->kvm_practive = (proc_t *)nl[1].n_value; + + (void) kvm_setproc(kd); + return (kd); +} + +int +kvm_close(kvm_t *kd) +{ + if (kd->kvm_core != NULL && kd->kvm_core != MAP_FAILED) + (void) munmap(kd->kvm_core, kd->kvm_coremapsize); + if (kd->kvm_corefd != -1) + (void) close(kd->kvm_corefd); + if (kd->kvm_kmemfd != -1) + (void) close(kd->kvm_kmemfd); + if (kd->kvm_memfd != -1) + (void) close(kd->kvm_memfd); + free(kd); + return (0); +} + +int +kvm_nlist(kvm_t *kd, struct nlist nl[]) +{ + return (nlist(kd->kvm_namelist, nl)); +} + +static offset_t +kvm_lookup(kvm_t *kd, struct as *as, uint64_t addr) +{ + uintptr_t pageoff = addr & (kd->kvm_dump.dump_pagesize - 1); + uint64_t page = addr - pageoff; + offset_t off = 0; + + if (kd->kvm_debug) + fprintf(stderr, "kvm_lookup(%p, %llx):", (void *)as, addr); + + if (as == NULL) { /* physical addressing mode */ + long first = 0; + long last = kd->kvm_dump.dump_npages - 1; + pfn_t target = (pfn_t)(page >> kd->kvm_dump.dump_pageshift); + while (last >= first) { + long middle = (first + last) / 2; + pfn_t pfn = kd->kvm_pfn[middle]; + if (kd->kvm_debug) + fprintf(stderr, " %ld ->", middle); + if (pfn == target) { + off = kd->kvm_dump.dump_data + pageoff + + ((uint64_t)middle << + kd->kvm_dump.dump_pageshift); + break; + } + if (pfn < target) + first = middle + 1; + else + last = middle - 1; + } + } else { + long hash = DUMP_HASH(&kd->kvm_dump, as, page); + off = kd->kvm_map[hash].dm_first; + while (off != 0) { + dump_map_t *dmp = (void *)(kd->kvm_core + off); + if (kd->kvm_debug) + fprintf(stderr, " %llx ->", off); + if (dmp < kd->kvm_map || + dmp > kd->kvm_map + kd->kvm_dump.dump_hashmask || + (off & (sizeof (offset_t) - 1)) != 0 || + DUMP_HASH(&kd->kvm_dump, dmp->dm_as, dmp->dm_va) != + hash) { + if (kd->kvm_debug) + fprintf(stderr, " dump map corrupt\n"); + return (0); + } + if (dmp->dm_va == page && dmp->dm_as == as) { + off = dmp->dm_data + pageoff; + break; + } + off = dmp->dm_next; + } + } + if (kd->kvm_debug) + fprintf(stderr, "%s found: %llx\n", off ? "" : " not", off); + return (off); +} + +static ssize_t +kvm_rw(kvm_t *kd, uint64_t addr, void *buf, size_t size, + struct as *as, ssize_t (*prw)(int, void *, size_t, offset_t)) +{ + offset_t off; + size_t resid = size; + + /* + * read/write of zero bytes always succeeds + */ + if (size == 0) + return (0); + + if (kd->kvm_core == NULL) { + char procbuf[100]; + int procfd; + ssize_t rval; + + if (as == kd->kvm_kas) + return (prw(kd->kvm_kmemfd, buf, size, addr)); + if (as == NULL) + return (prw(kd->kvm_memfd, buf, size, addr)); + + (void) sprintf(procbuf, "/proc/%ld/as", kd->kvm_pid); + if ((procfd = open64(procbuf, kd->kvm_openflag)) == -1) + return (-1); + rval = prw(procfd, buf, size, addr); + (void) close(procfd); + return (rval); + } + + while (resid != 0) { + uintptr_t pageoff = addr & (kd->kvm_dump.dump_pagesize - 1); + ssize_t len = MIN(resid, kd->kvm_dump.dump_pagesize - pageoff); + + if ((off = kvm_lookup(kd, as, addr)) == 0) + break; + + if (prw == PREAD && off < kd->kvm_coremapsize) + bcopy(kd->kvm_core + off, buf, len); + else if ((len = prw(kd->kvm_corefd, buf, len, off)) <= 0) + break; + resid -= len; + addr += len; + buf = (char *)buf + len; + } + return (resid < size ? size - resid : -1); +} + +ssize_t +kvm_read(kvm_t *kd, uintptr_t addr, void *buf, size_t size) +{ + return (kvm_rw(kd, addr, buf, size, kd->kvm_kas, PREAD)); +} + +ssize_t +kvm_kread(kvm_t *kd, uintptr_t addr, void *buf, size_t size) +{ + return (kvm_rw(kd, addr, buf, size, kd->kvm_kas, PREAD)); +} + +ssize_t +kvm_uread(kvm_t *kd, uintptr_t addr, void *buf, size_t size) +{ + return (kvm_rw(kd, addr, buf, size, kd->kvm_proc.p_as, PREAD)); +} + +ssize_t +kvm_aread(kvm_t *kd, uintptr_t addr, void *buf, size_t size, struct as *as) +{ + return (kvm_rw(kd, addr, buf, size, as, PREAD)); +} + +ssize_t +kvm_pread(kvm_t *kd, uint64_t addr, void *buf, size_t size) +{ + return (kvm_rw(kd, addr, buf, size, NULL, PREAD)); +} + +ssize_t +kvm_write(kvm_t *kd, uintptr_t addr, const void *buf, size_t size) +{ + return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_kas, PWRITE)); +} + +ssize_t +kvm_kwrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size) +{ + return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_kas, PWRITE)); +} + +ssize_t +kvm_uwrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size) +{ + return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_proc.p_as, PWRITE)); +} + +ssize_t +kvm_awrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size, + struct as *as) +{ + return (kvm_rw(kd, addr, (void *)buf, size, as, PWRITE)); +} + +ssize_t +kvm_pwrite(kvm_t *kd, uint64_t addr, const void *buf, size_t size) +{ + return (kvm_rw(kd, addr, (void *)buf, size, NULL, PWRITE)); +} + +uint64_t +kvm_physaddr(kvm_t *kd, struct as *as, uintptr_t addr) +{ + mem_vtop_t mem_vtop; + offset_t off; + + if (kd->kvm_core == NULL) { + mem_vtop.m_as = as; + mem_vtop.m_va = (void *)addr; + if (ioctl(kd->kvm_kmemfd, MEM_VTOP, &mem_vtop) == 0) + return ((uint64_t)mem_vtop.m_pfn * getpagesize() + + (addr & (getpagesize() - 1))); + } else { + if ((off = kvm_lookup(kd, as, addr)) != 0) { + long pfn_index = + (u_offset_t)(off - kd->kvm_dump.dump_data) >> + kd->kvm_dump.dump_pageshift; + return (((uint64_t)kd->kvm_pfn[pfn_index] << + kd->kvm_dump.dump_pageshift) + + (addr & (kd->kvm_dump.dump_pagesize - 1))); + } + } + return (-1ULL); +} + +struct proc * +kvm_getproc(kvm_t *kd, pid_t pid) +{ + (void) kvm_setproc(kd); + while (kvm_nextproc(kd) != NULL) + if (kd->kvm_pid == pid) + return (&kd->kvm_proc); + return (NULL); +} + +struct proc * +kvm_nextproc(kvm_t *kd) +{ + if (kd->kvm_proc.p_next == NULL || + kvm_kread(kd, (uintptr_t)kd->kvm_proc.p_next, + &kd->kvm_proc, sizeof (proc_t)) != sizeof (proc_t) || + kvm_kread(kd, (uintptr_t)&kd->kvm_proc.p_pidp->pid_id, + &kd->kvm_pid, sizeof (pid_t)) != sizeof (pid_t)) + return (NULL); + + return (&kd->kvm_proc); +} + +int +kvm_setproc(kvm_t *kd) +{ + (void) kvm_kread(kd, (uintptr_t)kd->kvm_practive, + &kd->kvm_proc.p_next, sizeof (proc_t *)); + kd->kvm_pid = -1; + return (0); +} + +/*ARGSUSED*/ +struct user * +kvm_getu(kvm_t *kd, struct proc *p) +{ + return (&p->p_user); +} 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); +} diff --git a/usr/src/lib/libkvm/common/llib-lkvm b/usr/src/lib/libkvm/common/llib-lkvm new file mode 100644 index 0000000000..9c04b1c6eb --- /dev/null +++ b/usr/src/lib/libkvm/common/llib-lkvm @@ -0,0 +1,56 @@ +/* + * 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 + */ +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +/* + * Copyright (c) 1997-1998 by Sun Microsystems, Inc. + * All rights reserved. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <kvm.h> + +kvm_t *kvm_open(const char *, const char *, const char *, int, const char *); +int kvm_close(kvm_t *); +int kvm_nlist(kvm_t *, struct nlist []); +ssize_t kvm_read(kvm_t *, uintptr_t, void *, size_t); +ssize_t kvm_kread(kvm_t *, uintptr_t, void *, size_t); +ssize_t kvm_uread(kvm_t *, uintptr_t, void *, size_t); +ssize_t kvm_aread(kvm_t *, uintptr_t, void *, size_t, struct as *); +ssize_t kvm_pread(kvm_t *, uint64_t, void *, size_t); +ssize_t kvm_write(kvm_t *, uintptr_t, const void *, size_t); +ssize_t kvm_kwrite(kvm_t *, uintptr_t, const void *, size_t); +ssize_t kvm_uwrite(kvm_t *, uintptr_t, const void *, size_t); +ssize_t kvm_awrite(kvm_t *, uintptr_t, const void *, size_t, struct as *); +ssize_t kvm_pwrite(kvm_t *, uint64_t, const void *, size_t); +uint64_t kvm_physaddr(kvm_t *, struct as *, uintptr_t); +proc_t *kvm_getproc(kvm_t *, pid_t); +proc_t *kvm_nextproc(kvm_t *); +int kvm_setproc(kvm_t *); +user_t *kvm_getu(kvm_t *, struct proc *); +int kvm_getcmd(kvm_t *, proc_t *, user_t *, char ***, char ***); diff --git a/usr/src/lib/libkvm/common/test.c b/usr/src/lib/libkvm/common/test.c new file mode 100644 index 0000000000..973c647d5b --- /dev/null +++ b/usr/src/lib/libkvm/common/test.c @@ -0,0 +1,468 @@ +/* + * 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 (c) 1996-1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> + +#include "kvm.h" + +#include <nlist.h> +#include <sys/thread.h> +#include <sys/fcntl.h> +#include <sys/param.h> +#include <sys/user.h> +#include <sys/proc.h> +#include <sys/elf.h> + +#ifdef __sparc +#include <sys/stack.h> /* for STACK_BIAS */ +#else +#define STACK_BIAS 0 +#endif + +kvm_t *cookie; + +struct proc *tst_getproc(pid_t); +struct proc *tst_nextproc(void); +struct user *tst_getu(struct proc *); +int tst_setproc(void); +int tst_getcmd(struct proc *, struct user *); +void tst_segkp(void); +void tst_nlist(struct nlist nl[]); +void tst_open(char *, char *, char *, int); +void tst_close(void); +ssize_t tst_read(uintptr_t, void *, size_t); +ssize_t tst_write(uintptr_t, void *, size_t); +int tst_getcmd(struct proc *, struct user *); +void tst_segkvp(void); + +char *name; +char *core; +char *swap; +int wflag; + +struct nlist nl[] = { + {"free"}, + {"fragtbl"}, + {"freemem"}, + {"allthreads"}, + {"nbuckets"}, + {"cputype"}, + {0} +}; + +int +main(int argc, char *argv[], char *envp[]) +{ + int c, errflg = 0; + long xx; + struct nlist *nlp; + struct proc *proc; + struct user *u; + int envc, ccnt; + + for (envc = 0; *envp++ != NULL; envc++) + continue; + envp -= 2; + ccnt = (*envp - *argv) + strlen(*envp) + 1; + printf("pid %d:: %d args; %d envs; %d chars (%p - %p)\n", + getpid(), argc, envc, ccnt, + &argv[0], *envp + strlen(*envp)); + + while ((c = getopt(argc, argv, "w")) != EOF) + switch (c) { + case 'w': + wflag++; + break; + case '?': + errflg++; + } + if (errflg) { + fprintf(stderr, "usage: %s [-w] [name] [core] [swap]\n", + argv[0]); + return (2); + } + if (optind < argc) { + name = argv[optind++]; + if (*name == '\0') + name = NULL; + } else + name = NULL; + if (optind < argc) { + core = argv[optind++]; + if (*core == '\0') + core = NULL; + } else + core = NULL; + if (optind < argc) { + swap = argv[optind++]; + if (*swap == '\0') + swap = NULL; + } else + swap = NULL; + + tst_open(name, core, swap, (wflag ? O_RDWR : O_RDONLY)); + if (cookie == NULL) + return (1); + + tst_nlist(nl); + + for (nlp = nl; nlp[0].n_type != 0; nlp++) + tst_read(nlp[0].n_value, &xx, sizeof (xx)); + + while ((proc = tst_nextproc()) != NULL) { + struct pid pid; + if (kvm_read(cookie, (uintptr_t)proc->p_pidp, &pid, + sizeof (pid)) != sizeof (pid)) { + printf("ERROR: couldn't get pid\n"); + break; + } + tst_getproc(pid.pid_id); + } + + tst_setproc(); + + while ((proc = tst_nextproc()) != NULL) { + if ((u = tst_getu(proc)) != NULL) + (void) tst_getcmd(proc, u); + } + + tst_segkp(); + tst_close(); + + return (0); +} + +void +tst_open(char *namelist, char *corefile, char *swapfile, int flag) +{ + printf("kvm_open(%s, %s, %s, %s)\n", + (namelist == NULL) ? "LIVE_KERNEL" : namelist, + (corefile == NULL) ? "LIVE_KERNEL" : corefile, + (swapfile == NULL) ? + ((corefile == NULL) ? "LIVE_KERNEL" : "(none)") : swapfile, + (flag == O_RDONLY) ? "O_RDONLY" : ((flag == O_RDWR) ? + "O_RDWR" : "???")); + + if ((cookie = kvm_open(namelist, corefile, + swapfile, flag, "libkvm test")) == NULL) + printf("ERROR: kvm_open returned %p\n", cookie); +} + +void +tst_close(void) +{ + int i; + + printf("kvm_close()\n"); + if ((i = kvm_close(cookie)) != 0) + printf("ERROR: kvm_close returned %d\n", i); +} + +void +tst_nlist(struct nlist nl[]) +{ + int i; + char *t, *s; + + printf("kvm_nlist([nl])\n"); + if ((i = kvm_nlist(cookie, nl)) != 0) + printf("ERROR: kvm_nlist returned %d\n", i); + for (i = 0; nl[i].n_name != 0 && nl[i].n_name[0] != '\0'; i++) { + /* + * Debug: + * n_value gets filled in with st_value, + * n_type gets filled in w/ELF32_ST_TYPE(sym->st_info) + * n_scnum gets filled in w/st_shndx + */ + switch (nl[i].n_type) { + case STT_NOTYPE: + t = "NOTYPE"; + break; + case STT_OBJECT: + t = "OBJECT"; + break; + case STT_FUNC: + t = "FUNC"; + break; + case STT_SECTION: + t = "SECTION"; + break; + case STT_FILE: + t = "FILE"; + break; + case STT_NUM: + t = "NUM"; + break; + default: + t = "???"; + } + + switch ((unsigned)nl[i].n_scnum) { + static char strbuf[40]; + + case SHN_UNDEF: + s = "UNDEF"; + break; + case SHN_LORESERVE: + s = "LORESERVE"; + break; + case SHN_ABS: + s = "ABS"; + break; + case SHN_COMMON: + s = "COMMON"; + break; + case SHN_HIRESERVE: + s = "HIRESERVE"; + break; + default: + (void) sprintf(strbuf, "unknown (%d)", nl[i].n_scnum); + s = strbuf; + break; + } + + printf("%s: %lx (%s, %s)\n", + nl[i].n_name, nl[i].n_value, s, t); + } +} + +ssize_t +tst_read(uintptr_t addr, void *buf, size_t nbytes) +{ + ssize_t e; + int i; + char *b; + + printf("kvm_read(%lx, [buf], %lu)\n", addr, nbytes); + if ((e = kvm_read(cookie, addr, buf, nbytes)) != nbytes) + printf("ERROR: kvm_read returned %ld instead of %lu\n", + e, nbytes); + for (b = buf, i = 0; i < nbytes; b++, i++) + printf("%lx: %02x (%04o)\n", addr + i, + *b & 0xff, *b & 0xff); + + return (e); +} + +ssize_t +tst_write(uintptr_t addr, void *buf, size_t nbytes) +{ + ssize_t e; + ssize_t i; + void *b; + + printf("kvm_write(%lx, [buf], %lu)\n", addr, nbytes); + if ((e = kvm_write(cookie, addr, buf, nbytes)) != nbytes) + printf("ERROR: kvm_write returned %ld instead of %lu\n", + e, nbytes); + if ((b = malloc(nbytes)) == 0) + printf("ERROR: malloc for readback failed\n"); + else { + if ((i = kvm_read(cookie, addr, b, nbytes)) != nbytes) + printf("ERROR: readback returned %ld\n", i); + else if (memcmp(b, buf, nbytes)) + printf("ERROR: write check failed!\n"); + (void) free(b); + } + return (e); +} + +struct proc * +tst_getproc(pid_t pid) +{ + struct proc *proc; + struct pid pidbuf; + + printf("kvm_getproc(%d)\n", pid); + if ((proc = kvm_getproc(cookie, pid)) == NULL) { + printf("ERROR: kvm_getproc returned NULL\n"); + return (proc); + } + + if (kvm_read(cookie, (uintptr_t)proc->p_pidp, &pidbuf, + sizeof (pidbuf)) != sizeof (pidbuf)) { + printf("ERROR: couldn't get pid\n"); + return (proc); + } + + printf("p_pid: %d\n", pidbuf.pid_id); + return (proc); +} + +struct proc * +tst_nextproc(void) +{ + struct proc *proc; + struct pid pidbuf; + + printf("kvm_nextproc()\n"); + if ((proc = kvm_nextproc(cookie)) == NULL) { + printf("kvm_nextproc returned NULL\n"); + return (proc); + } + + /* + * p_pid is now a macro which turns into a ptr dereference; + * must do a kvm_read to get contents. + */ + if (kvm_read(cookie, (u_long)proc->p_pidp, (char *)&pidbuf, + sizeof (struct pid)) != sizeof (struct pid)) { + printf("ERROR: couldn't get pid\n"); + } + printf("p_pid: %d\n", pidbuf.pid_id); + + return (proc); +} + +int +tst_setproc(void) +{ + int i; + + printf("kvm_setproc()\n"); + if ((i = kvm_setproc(cookie)) != 0) + printf("ERROR: kvm_setproc returned %d\n", i); + return (i); +} + +struct user * +tst_getu(struct proc *proc) +{ + register int e; + struct proc tp; + struct user *u; + struct pid pidbuf; + + if (kvm_read(cookie, (uintptr_t)proc->p_pidp, &pidbuf, + sizeof (pidbuf)) != sizeof (pidbuf)) + printf("ERROR: couldn't get pid\n"); + + printf("kvm_getu(pid:%d)\n", pidbuf.pid_id); + if ((u = kvm_getu(cookie, proc)) == NULL) + printf("ERROR: kvm_getu returned NULL\n"); + return (u); +} + +static void +safe_printf(const char *s) +{ + char buf[BUFSIZ], *p; + + (void) strncpy(buf, s, BUFSIZ - 1); + buf[BUFSIZ - 1] = '\0'; + + for (p = buf; *p != '\0'; p++) { + if (!isprint(*p)) + *p = ' '; + } + + (void) printf("\"%s\"\n", buf); +} + +int +tst_getcmd(struct proc *proc, struct user *u) +{ + char **arg; + char **env; + int i; + char **p; + struct pid pidbuf; + + if (kvm_kread(cookie, (uintptr_t)proc->p_pidp, &pidbuf, + sizeof (pidbuf)) != sizeof (pidbuf)) { + printf("ERROR: couldn't get pid\n"); + return (-1); + } + + printf("kvm_getcmd(pid:%d, [u], arg, env)\n", pidbuf.pid_id); + if ((i = kvm_getcmd(cookie, proc, u, &arg, &env)) != 0) { + printf("kvm_getcmd returned %d\n", i); + return (i); + } + + printf("Args: "); + for (p = arg; *p != NULL; p++) + safe_printf(*p); + printf("Env: "); + for (p = env; *p != NULL; p++) + safe_printf(*p); + + (void) free(arg); + (void) free(env); + + return (0); +} + +void +tst_segkp(void) +{ + kthread_t t; + caddr_t tp, alltp; + uintptr_t stk[16]; + int i; + + if (kvm_read(cookie, nl[3].n_value, &alltp, sizeof (alltp)) + != sizeof (alltp)) { + printf("ERROR: couldn't read allthread, addr 0x%lx\n", + nl[3].n_value); + return; + } + printf("allthreads 0x%lx\n", nl[3].n_value); + printf("next offset 0x%lx\n", + (uintptr_t)&(t.t_next) - (uintptr_t)&t); + + for (tp = alltp; tp; tp = (caddr_t)(t.t_next)) { + if (kvm_read(cookie, + (uintptr_t)tp, &t, sizeof (t)) != sizeof (t)) { + printf("ERROR: couldn't read thread, addr 0x%p\n", tp); + return; + } + + printf("thread 0x%p\n", tp); + printf("\tstk 0x%p sp 0x%lx tid %d next 0x%p prev 0x%p\n", + tp, t.t_stk, t.t_pcb.val[1], t.t_tid, t.t_next, t.t_prev); + + if (kvm_read(cookie, t.t_pcb.val[1] + STACK_BIAS, stk, + sizeof (stk)) != sizeof (stk)) { + printf("ERROR: couldn't read stack, taddr 0x%p\n", tp); + continue; + } + for (i = 0; i < 16; i++) { + printf("%-16lx ", stk[i]); + if (((i + 1) % 4) == 0) + printf("\n"); + } + + if ((caddr_t)(t.t_next) == alltp) + break; + } +} |