/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2018 Joyent, Inc. */ /* * Kernel Process View Target * * The kproc target is activated when the user is debugging a kernel using the * kvm target and executes a ::context dcmd to change the debugger view to one * of the running processes. The kvm target's t_setcontext operation will * create and activate a kproc target in response to this call. The kproc * target itself is built upon the kvm target's libkvm cookie and the ability * to read information from the kernel itself and the ability to read the * address space of a particular user process with kvm_aread(). It also relies * on a special set of functions provided by the kvm target's mdb_ks support * module in order to bootstrap: specifically, given the initial proc pointer, * mdb_ks provides functions to return the set of address space mappings, the * address space pointer itself, the aux vector vector saved in the u-area, * and the process data model. The kproc target maintains a list of address * space mappings (kp_map_t) and load objects (kp_file_t), and for each load * object will attempt to read the corresponding dynamic symbol table. In * order to bootstrap, the target uses the AT_BASE and AT_ENTRY aux vector * elements to locate the dynamic linker and executable mappings. With these * mappings in place, we initialize a librtld_db agent on the target (see * mdb_pservice.c for how this is done), and then process each load object * found in the link-map chain. In order to simplify the construction of * symbol tables for each load object, we would like make use of our existing * library of GElf processing code. Since the MDB GElf code uses mdb_io * objects to read in an ELF file, we simply define a new type of mdb_io object * where each read operation is translated into a call to kproc's t_vread * function to read from the range of the address space defined by the mapping * as if it were a file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct kp_symarg { mdb_tgt_sym_f *sym_cb; /* Caller's callback function */ void *sym_data; /* Callback function argument */ uint_t sym_type; /* Symbol type/binding filter */ uintptr_t sym_adjust; /* Symbol value adjustment */ mdb_syminfo_t sym_info; /* Symbol id and table id */ const char *sym_obj; /* Containing object */ } kp_symarg_t; typedef struct kp_file { mdb_gelf_file_t *kpf_file; /* ELF file object */ mdb_io_t *kpf_fio; /* ELF file back-end */ mdb_gelf_symtab_t *kpf_dynsym; /* Dynamic symbol table */ struct kp_map *kpf_map; /* Primary (text) mapping */ const char *kpf_basename; /* Mapping basename */ uintptr_t kpf_dyn_base; /* Load address for ET_DYN files */ uintptr_t kpf_text_base; /* Base address of text mapping */ uintptr_t kpf_data_base; /* Base address of data mapping */ struct kp_file *kpf_next; /* Pointer to next file */ } kp_file_t; typedef struct kp_map { mdb_map_t kpm_map; /* Mapping information */ kp_file_t *kpm_file; /* Pointer to load object */ struct kp_map *kpm_next; /* Pointer to next mapping */ } kp_map_t; typedef struct kp_io { mdb_tgt_t *kpi_tgt; /* Backpointer to kproc target */ kp_map_t *kpi_map; /* Mapping for this i/o */ uintptr_t kpi_ptr; /* Virtual address pointer */ uintptr_t kpi_lim; /* Virtual address limit */ } kp_io_t; typedef struct kp_data { mdb_tgt_t *kp_parent; /* Parent kvm target */ kvm_t *kp_cookie; /* Cookie for libkvm routines */ rd_agent_t *kp_rap; /* Cookie for librtld_db routines */ proc_t *kp_proc; /* Proc address in dump */ struct as *kp_as; /* Proc as address in dump */ pid_t kp_pid; /* Process ID */ auxv_t *kp_auxv; /* Auxv array from u-area */ int kp_nauxv; /* Length of kp_auxv */ const char *kp_platform; /* Platform string from kvm target */ uint_t kp_model; /* Process data model */ kp_file_t *kp_file_head; /* Head of load object list */ kp_file_t *kp_file_tail; /* Tail of load object list */ kp_map_t *kp_map_head; /* Head of mapping list */ kp_map_t *kp_map_tail; /* Tail of mapping list */ int kp_num_files; /* Length of load object list */ int kp_num_maps; /* Length of mapping list */ kp_map_t *kp_map_exec; /* Executable mapping */ kp_map_t *kp_map_ldso; /* Interpreter mapping */ kp_file_t kp_prfile; /* Fake file for mdb.m_prsym */ } kp_data_t; static mdb_io_t *kp_io_create(mdb_tgt_t *, kp_map_t *); static kp_map_t * kp_addr_to_kpmap(kp_data_t *kp, uintptr_t addr) { kp_map_t *kpm; for (kpm = kp->kp_map_head; kpm != NULL; kpm = kpm->kpm_next) { if (addr >= kpm->kpm_map.map_base && addr < kpm->kpm_map.map_base + kpm->kpm_map.map_size) return (kpm); } return (NULL); } static long kp_getauxval(kp_data_t *kp, int type) { auxv_t *auxp; for (auxp = kp->kp_auxv; auxp->a_type != AT_NULL; auxp++) { if (auxp->a_type == type) return (auxp->a_un.a_val); } return (-1L); } static void kp_add_mapping(const mdb_map_t *pmp, void *data) { kp_map_t *kpm = mdb_zalloc(sizeof (kp_map_t), UM_SLEEP); kp_data_t *kp = data; bcopy(pmp, &kpm->kpm_map, sizeof (mdb_map_t)); if (kp->kp_map_tail != NULL) kp->kp_map_tail->kpm_next = kpm; else kp->kp_map_head = kpm; kp->kp_map_tail = kpm; kp->kp_num_maps++; } static kp_file_t * kp_file_create(mdb_tgt_t *t, kp_map_t *kpm, GElf_Half etype) { kp_file_t *kpf = mdb_zalloc(sizeof (kp_file_t), UM_SLEEP); kp_data_t *kp = t->t_data; size_t dyns_sz; void *dyns; kpf->kpf_fio = kp_io_create(t, kpm); kpf->kpf_map = kpm; kpf->kpf_basename = strbasename(kpm->kpm_map.map_name); kpf->kpf_file = mdb_gelf_create(kpf->kpf_fio, etype, GF_PROGRAM); kpf->kpf_text_base = kpm->kpm_map.map_base; if (kpm != kp->kp_map_exec) kpf->kpf_dyn_base = kpf->kpf_text_base; if (kpf->kpf_file == NULL) goto err; /* Failed to create ELF file */ mdb_dprintf(MDB_DBG_TGT, "loading symbols for %s\n", kpm->kpm_map.map_name); if ((kp->kp_rap != NULL) && (rd_get_dyns(kp->kp_rap, kpf->kpf_text_base, &dyns, &dyns_sz) == RD_OK)) mdb_gelf_dyns_set(kpf->kpf_file, dyns, dyns_sz); kpf->kpf_dynsym = mdb_gelf_symtab_create_dynamic(kpf->kpf_file, MDB_TGT_DYNSYM); if (kpf->kpf_dynsym == NULL) goto err; /* Failed to create symbol table */ kpm->kpm_file = kpf; if (kp->kp_file_tail != NULL) kp->kp_file_tail->kpf_next = kpf; else kp->kp_file_head = kpf; kp->kp_file_tail = kpf; kp->kp_num_files++; return (kpf); err: if (kpf->kpf_file != NULL) mdb_gelf_destroy(kpf->kpf_file); else mdb_io_destroy(kpf->kpf_fio); mdb_free(kpf, sizeof (kp_file_t)); return (NULL); } static void kp_file_destroy(kp_file_t *kpf) { if (kpf->kpf_dynsym != NULL) mdb_gelf_symtab_destroy(kpf->kpf_dynsym); mdb_gelf_destroy(kpf->kpf_file); mdb_free(kpf, sizeof (kp_file_t)); } static int kp_setcontext(mdb_tgt_t *t, void *context) { kp_data_t *kp = t->t_data; if (kp->kp_proc != context) { mdb_tgt_destroy(t); return (mdb_tgt_setcontext(mdb.m_target, context)); } mdb_warn("debugger context is already set to proc %p\n", context); return (0); } static kp_map_t * kp_find_data(kp_data_t *kp, kp_file_t *kpf, const rd_loadobj_t *rlp) { GElf_Phdr *gpp = kpf->kpf_file->gf_phdrs; size_t i, n = kpf->kpf_file->gf_npload; /* * Find the first loadable, writeable Phdr and compute kpf_data_base * as the virtual address at which is was loaded. */ for (i = 0; i < n; i++, gpp++) { if (gpp->p_type == PT_LOAD && (gpp->p_flags & PF_W)) { kpf->kpf_data_base = gpp->p_vaddr; if (kpf->kpf_map != kp->kp_map_exec) kpf->kpf_data_base += rlp->rl_base; break; } } /* * If we found a suitable Phdr and set kpf_data_base, return * the mapping information for this address; otherwise fail. */ if (kpf->kpf_data_base != 0) return (kp_addr_to_kpmap(kp, kpf->kpf_data_base)); return (NULL); } static int kp_iter_mapping(const rd_loadobj_t *rlp, mdb_tgt_t *t) { kp_data_t *kp = t->t_data; kp_file_t *kpf; kp_map_t *kpm; char name[MDB_TGT_MAPSZ]; if (mdb_tgt_readstr(t, MDB_TGT_AS_VIRT, name, sizeof (name), (mdb_tgt_addr_t)rlp->rl_nameaddr) <= 0) { mdb_dprintf(MDB_DBG_TGT, "failed to read name %p", (void *)rlp->rl_nameaddr); return (1); /* Keep going; forget this if we can't read name */ } mdb_dprintf(MDB_DBG_TGT, "rd_loadobj name = \"%s\" rl_base = %p\n", name, (void *)rlp->rl_base); if ((kpm = kp_addr_to_kpmap(kp, rlp->rl_base)) == NULL) return (1); /* Keep going; no mapping at this address */ (void) strncpy(kpm->kpm_map.map_name, name, MDB_TGT_MAPSZ); kpm->kpm_map.map_name[MDB_TGT_MAPSZ - 1] = '\0'; if ((kpf = kpm->kpm_file) == NULL) { if (kpm == kp->kp_map_exec) kpf = kp_file_create(t, kpm, ET_EXEC); else kpf = kp_file_create(t, kpm, ET_DYN); if (kpf == NULL) return (1); /* Keep going; failed to build ELF file */ } else kpf->kpf_basename = strbasename(kpm->kpm_map.map_name); if ((kpm = kp_find_data(kp, kpf, rlp)) != NULL) { mdb_dprintf(MDB_DBG_TGT, "found data for %s at %p\n", kpf->kpf_basename, (void *)kpm->kpm_map.map_base); kpm->kpm_file = kpf; } return (1); } /*ARGSUSED*/ static int kp_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { kp_data_t *kp = mdb.m_target->t_data; mdb_printf("debugging PID %d (%d-bit) in kernel crash dump\n", kp->kp_pid, kp->kp_model == PR_MODEL_ILP32 ? 32 : 64); if (kp->kp_map_exec != NULL) { mdb_printf("executable file: %s\n", kp->kp_map_exec->kpm_map.map_name); } return (DCMD_OK); } static const mdb_dcmd_t kp_dcmds[] = { { "status", NULL, "print summary of current target", kp_status_dcmd }, { NULL } }; static void kp_activate(mdb_tgt_t *t) { kp_data_t *kp = t->t_data; mdb_prop_postmortem = TRUE; mdb_prop_kernel = FALSE; if (kp->kp_model == PR_MODEL_ILP32) mdb_prop_datamodel = MDB_TGT_MODEL_ILP32; else mdb_prop_datamodel = MDB_TGT_MODEL_LP64; /* * Initialize our rtld_db agent and then iterate over the link map, * instantiating kp_file objects as we go. */ if ((kp->kp_rap = rd_new((struct ps_prochandle *)t)) != NULL) { (void) rd_loadobj_iter(kp->kp_rap, (rl_iter_f *) kp_iter_mapping, t); } else { mdb_warn("unable to initialize rtld_db agent for proc %p\n", (void *)kp->kp_proc); } (void) mdb_tgt_register_dcmds(t, &kp_dcmds[0], MDB_MOD_FORCE); if (kp->kp_map_exec != NULL && kp->kp_map_exec->kpm_file != NULL) mdb_tgt_elf_export(kp->kp_map_exec->kpm_file->kpf_file); else mdb_tgt_elf_export(NULL); } static void kp_deactivate(mdb_tgt_t *t) { const mdb_dcmd_t *dcp; for (dcp = &kp_dcmds[0]; dcp->dc_name != NULL; dcp++) { if (mdb_module_remove_dcmd(t->t_module, dcp->dc_name) == -1) warn("failed to remove dcmd %s", dcp->dc_name); } mdb_prop_postmortem = FALSE; mdb_prop_kernel = FALSE; mdb_prop_datamodel = MDB_TGT_MODEL_UNKNOWN; } static void kp_destroy(mdb_tgt_t *t) { kp_data_t *kp = t->t_data; kp_map_t *kpm, *nkpm; kp_file_t *kpf, *nkpf; if (kp->kp_rap != NULL) rd_delete(kp->kp_rap); for (kpm = kp->kp_map_head; kpm != NULL; kpm = nkpm) { nkpm = kpm->kpm_next; mdb_free(kpm, sizeof (kp_map_t)); } for (kpf = kp->kp_file_head; kpf != NULL; kpf = nkpf) { nkpf = kpf->kpf_next; kp_file_destroy(kpf); } mdb_free(kp->kp_auxv, kp->kp_nauxv * sizeof (auxv_t)); mdb_free(kp, sizeof (kp_data_t)); } /*ARGSUSED*/ static const char * kp_name(mdb_tgt_t *t) { return ("kproc"); } static const char * kp_isa(mdb_tgt_t *t) { kp_data_t *kp = t->t_data; #ifdef __sparc return (kp->kp_model == PR_MODEL_ILP32 ? "sparc" : "sparcv9"); #else return (kp->kp_model == PR_MODEL_ILP32 ? "i386" : "amd64"); #endif } static const char * kp_platform(mdb_tgt_t *t) { return (((kp_data_t *)t->t_data)->kp_platform); } static int kp_uname(mdb_tgt_t *t, struct utsname *utsp) { kp_data_t *kp = t->t_data; return (mdb_tgt_uname(kp->kp_parent, utsp)); } static int kp_dmodel(mdb_tgt_t *t) { kp_data_t *kp = t->t_data; switch (kp->kp_model) { case PR_MODEL_ILP32: return (MDB_TGT_MODEL_ILP32); case PR_MODEL_LP64: return (MDB_TGT_MODEL_LP64); } return (MDB_TGT_MODEL_UNKNOWN); } static kp_map_t * kp_name_to_kpmap(kp_data_t *kp, const char *name) { size_t namelen; kp_file_t *kpf; kp_map_t *kpm; /* * Handle special reserved names (except for MDB_TGT_OBJ_EVERY): */ if (name == MDB_TGT_OBJ_EXEC) return (kp->kp_map_exec); if (name == MDB_TGT_OBJ_RTLD) return (kp->kp_map_ldso); /* * First pass: look for exact matches on the entire pathname * associated with the mapping or its basename. */ for (kpm = kp->kp_map_head; kpm != NULL; kpm = kpm->kpm_next) { if ((kpf = kpm->kpm_file) != NULL) { if (strcmp(kpm->kpm_map.map_name, name) == 0 || strcmp(kpf->kpf_basename, name) == 0) return (kpf->kpf_map); } } namelen = strlen(name); /* * Second pass: look for partial matches (initial basename match * up to a '.' suffix); allows "libc.so" or "libc" to match "libc.so.1" */ for (kpm = kp->kp_map_head; kpm != NULL; kpm = kpm->kpm_next) { if ((kpf = kpm->kpm_file) != NULL) { if (strncmp(kpf->kpf_basename, name, namelen) == 0 && kpf->kpf_basename[namelen] == '.') return (kpf->kpf_map); } } /* * One last check: we allow "a.out" to always alias the executable, * assuming this name was not in use for something else. */ if (strcmp(name, "a.out") == 0) return (kp->kp_map_exec); return (NULL); } static ssize_t kp_vread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr) { kp_data_t *kp = t->t_data; ssize_t n = kvm_aread(kp->kp_cookie, addr, buf, nbytes, kp->kp_as); if (n == -1) return (set_errno(EMDB_NOMAP)); return (n); } static ssize_t kp_vwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) { kp_data_t *kp = t->t_data; ssize_t n = kvm_awrite(kp->kp_cookie, addr, buf, nbytes, kp->kp_as); if (n == -1) return (set_errno(EMDB_NOMAP)); return (n); } int kp_vtop(mdb_tgt_t *t, mdb_tgt_as_t as, uintptr_t va, physaddr_t *pap) { kp_data_t *kp = t->t_data; physaddr_t pa; if (as != MDB_TGT_AS_VIRT) return (set_errno(EINVAL)); if ((pa = kvm_physaddr(kp->kp_cookie, kp->kp_as, va)) != -1ULL) { *pap = pa; return (0); } return (set_errno(EMDB_NOMAP)); } static int kp_lookup_by_name(mdb_tgt_t *t, const char *object, const char *name, GElf_Sym *symp, mdb_syminfo_t *sip) { kp_data_t *kp = t->t_data; kp_file_t *kpf; int n; GElf_Sym sym; uint_t symid; int rv = -1; /* * Simplify our task: if object is EVERY, then we need to search * kp_num_files files beginning at kp_file_head; otherwise we are * searching 1 file whose file pointer is obtained via object_to_map. */ if (object != MDB_TGT_OBJ_EVERY) { kp_map_t *kpm = kp_name_to_kpmap(kp, object); if (kpm == NULL || kpm->kpm_file == NULL) return (set_errno(EMDB_NOOBJ)); kpf = kpm->kpm_file; n = 1; } else { kpf = kp->kp_file_head; n = kp->kp_num_files; } /* * Iterate through the load object files and look for the symbol name * in the .dynsym of each. If we encounter a match with SHN_UNDEF, * keep looking in hopes of finding a better match. This means that * a name such as "puts" will match the puts function in libc instead * of matching the puts PLT entry in the a.out file. */ for (; n > 0; n--, kpf = kpf->kpf_next) { if (kpf->kpf_dynsym == NULL) continue; /* No symbols for this file */ if (mdb_gelf_symtab_lookup_by_name(kpf->kpf_dynsym, name, symp, &sip->sym_id) != 0) continue; /* Symbol name not found */ symp->st_value += kpf->kpf_dyn_base; if (symp->st_shndx != SHN_UNDEF) { sip->sym_table = MDB_TGT_DYNSYM; return (0); } if (rv != 0) { sym = *symp; symid = sip->sym_id; rv = 0; } } if (rv != 0) return (set_errno(EMDB_NOSYM)); sip->sym_table = MDB_TGT_DYNSYM; sip->sym_id = symid; *symp = sym; return (0); } static int kp_lookup_by_addr(mdb_tgt_t *t, uintptr_t addr, uint_t flags, char *buf, size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip) { kp_data_t *kp = t->t_data; kp_map_t *kpm = kp_addr_to_kpmap(kp, addr); kp_file_t *sym_kpf = NULL; GElf_Sym sym; uint_t symid; const char *name; kp_file_t *kpf; int n; /* * Check the user's private symbol table first; if a match is * found there, we're done or we have a first guess. */ if (mdb_gelf_symtab_lookup_by_addr(mdb.m_prsym, addr, flags, buf, nbytes, symp, &sip->sym_id) == 0) { sym_kpf = &kp->kp_prfile; if (flags & MDB_TGT_SYM_EXACT) goto found; sym = *symp; symid = sip->sym_id; } /* * If no mapping contains the address and EXACT mode is set, we're done. * Otherwise we need to search all the symbol tables in fuzzy mode. * If we find a mapping, then we only need to search that symtab. */ if (kpm == NULL || kpm->kpm_file == NULL) { if (flags & MDB_TGT_SYM_EXACT) return (set_errno(EMDB_NOSYMADDR)); kpf = kp->kp_file_head; n = kp->kp_num_files; } else { kpf = kpm->kpm_file; n = 1; } /* * Iterate through our list of load objects, scanning each one which * has a symbol table. In fuzzy mode, we continue looking and * improve our choice if we find a closer symbol. */ for (; n > 0; n--, kpf = kpf->kpf_next) { if (kpf->kpf_dynsym == NULL) continue; /* No symbols for this file */ if (mdb_gelf_symtab_lookup_by_addr(kpf->kpf_dynsym, addr - kpf->kpf_dyn_base, flags, buf, nbytes, symp, &sip->sym_id) != 0) continue; /* No symbol for this address */ symp->st_value += kpf->kpf_dyn_base; if (flags & MDB_TGT_SYM_EXACT) { sym_kpf = kpf; goto found; } if (sym_kpf == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) { sym_kpf = kpf; sym = *symp; symid = sip->sym_id; } } if (sym_kpf == NULL) return (set_errno(EMDB_NOSYMADDR)); *symp = sym; /* Copy our best symbol into the caller's symbol */ sip->sym_id = symid; found: /* * Once we've found something, copy the final name into the caller's * buffer and prefix it with the load object name if appropriate. */ name = mdb_gelf_sym_name(sym_kpf->kpf_dynsym, symp); if (sym_kpf != kp->kp_map_exec->kpm_file && sym_kpf != &kp->kp_prfile) { (void) mdb_snprintf(buf, nbytes, "%s`%s", sym_kpf->kpf_basename, name); } else if (nbytes > 0) { (void) strncpy(buf, name, nbytes); buf[nbytes - 1] = '\0'; } if (sym_kpf == &kp->kp_prfile) sip->sym_table = MDB_TGT_PRVSYM; else sip->sym_table = MDB_TGT_DYNSYM; return (0); } static int kp_symtab_func(void *data, const GElf_Sym *symp, const char *name, uint_t id) { kp_symarg_t *argp = data; if (mdb_tgt_sym_match(symp, argp->sym_type)) { GElf_Sym sym = *symp; sym.st_value += argp->sym_adjust; argp->sym_info.sym_id = id; return (argp->sym_cb(argp->sym_data, &sym, name, &argp->sym_info, argp->sym_obj)); } return (0); } static void kp_symtab_iter(kp_file_t *kpf, uint_t type, const char *obj, mdb_tgt_sym_f *cb, void *data) { if (kpf->kpf_dynsym != NULL) { kp_symarg_t arg; arg.sym_cb = cb; arg.sym_data = data; arg.sym_type = type; arg.sym_adjust = kpf->kpf_dyn_base; arg.sym_info.sym_table = kpf->kpf_dynsym->gst_tabid; arg.sym_obj = obj; mdb_gelf_symtab_iter(kpf->kpf_dynsym, kp_symtab_func, &arg); } } /*ARGSUSED*/ static int kp_symbol_iter(mdb_tgt_t *t, const char *object, uint_t which, uint_t type, mdb_tgt_sym_f *func, void *private) { kp_data_t *kp = t->t_data; kp_file_t *kpf = NULL; kp_map_t *kpm; switch ((uintptr_t)object) { case (uintptr_t)MDB_TGT_OBJ_EVERY: if (kp->kp_map_exec && kp->kp_map_exec->kpm_file) { kpf = kp->kp_map_exec->kpm_file; kp_symtab_iter(kpf, type, MDB_TGT_OBJ_EXEC, func, private); } if (kp->kp_map_ldso && kp->kp_map_ldso->kpm_file) { kpf = kp->kp_map_ldso->kpm_file; kp_symtab_iter(kpf, type, MDB_TGT_OBJ_RTLD, func, private); } return (0); case (uintptr_t)MDB_TGT_OBJ_EXEC: if (kp->kp_map_exec && kp->kp_map_exec->kpm_file) kpf = kp->kp_map_exec->kpm_file; break; case (uintptr_t)MDB_TGT_OBJ_RTLD: if (kp->kp_map_ldso && kp->kp_map_ldso->kpm_file) kpf = kp->kp_map_ldso->kpm_file; break; default: if ((kpm = kp_name_to_kpmap(kp, object)) != NULL) { kpf = kpm->kpm_file; break; } else return (set_errno(EMDB_NOOBJ)); } if (kpf != NULL) kp_symtab_iter(kpf, type, object, func, private); return (0); } static int kp_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private) { kp_data_t *kp = t->t_data; kp_map_t *kpm; for (kpm = kp->kp_map_head; kpm != NULL; kpm = kpm->kpm_next) { if (func(private, &kpm->kpm_map, kpm->kpm_map.map_name) != 0) break; } return (0); } static int kp_object_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private) { kp_data_t *kp = t->t_data; kp_file_t *kpf; for (kpf = kp->kp_file_head; kpf != NULL; kpf = kpf->kpf_next) { if (func(private, &kpf->kpf_map->kpm_map, kpf->kpf_map->kpm_map.map_name) != 0) break; } return (0); } static const mdb_map_t * kp_addr_to_map(mdb_tgt_t *t, uintptr_t addr) { kp_map_t *kpm = kp_addr_to_kpmap(t->t_data, addr); if (kpm != NULL) return (&kpm->kpm_map); (void) set_errno(EMDB_NOMAP); return (NULL); } static const mdb_map_t * kp_name_to_map(mdb_tgt_t *t, const char *name) { kp_map_t *kpm = kp_name_to_kpmap(t->t_data, name); if (kpm != NULL) return (&kpm->kpm_map); (void) set_errno(EMDB_NOOBJ); return (NULL); } /*ARGSUSED*/ static int kp_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp) { bzero(tsp, sizeof (mdb_tgt_status_t)); tsp->st_state = MDB_TGT_DEAD; return (0); } static int kp_auxv(mdb_tgt_t *t, const auxv_t **auxvp) { kp_data_t *kp = t->t_data; *auxvp = kp->kp_auxv; return (0); } static const mdb_tgt_ops_t kproc_ops = { (int (*)()) mdb_tgt_notsup, /* t_setflags */ kp_setcontext, /* t_setcontext */ kp_activate, /* t_activate */ kp_deactivate, /* t_deactivate */ (void (*)()) mdb_tgt_nop, /* t_periodic */ kp_destroy, /* t_destroy */ kp_name, /* t_name */ kp_isa, /* t_isa */ kp_platform, /* t_platform */ kp_uname, /* t_uname */ kp_dmodel, /* t_dmodel */ (ssize_t (*)()) mdb_tgt_notsup, /* t_aread */ (ssize_t (*)()) mdb_tgt_notsup, /* t_awrite */ kp_vread, /* t_vread */ kp_vwrite, /* t_vwrite */ (ssize_t (*)()) mdb_tgt_notsup, /* t_pread */ (ssize_t (*)()) mdb_tgt_notsup, /* t_pwrite */ (ssize_t (*)()) mdb_tgt_notsup, /* t_fread */ (ssize_t (*)()) mdb_tgt_notsup, /* t_fwrite */ (ssize_t (*)()) mdb_tgt_notsup, /* t_ioread */ (ssize_t (*)()) mdb_tgt_notsup, /* t_iowrite */ kp_vtop, /* t_vtop */ kp_lookup_by_name, /* t_lookup_by_name */ kp_lookup_by_addr, /* t_lookup_by_addr */ kp_symbol_iter, /* t_symbol_iter */ kp_mapping_iter, /* t_mapping_iter */ kp_object_iter, /* t_object_iter */ kp_addr_to_map, /* t_addr_to_map */ kp_name_to_map, /* t_name_to_map */ (struct ctf_file *(*)()) mdb_tgt_null, /* t_addr_to_ctf */ (struct ctf_file *(*)()) mdb_tgt_null, /* t_name_to_ctf */ kp_status, /* t_status */ (int (*)()) mdb_tgt_notsup, /* t_run */ (int (*)()) mdb_tgt_notsup, /* t_step */ (int (*)()) mdb_tgt_notsup, /* t_step_out */ (int (*)()) mdb_tgt_notsup, /* t_next */ (int (*)()) mdb_tgt_notsup, /* t_cont */ (int (*)()) mdb_tgt_notsup, /* t_signal */ (int (*)()) mdb_tgt_null, /* t_add_sbrkpt */ (int (*)()) mdb_tgt_null, /* t_add_vbrkpt */ (int (*)()) mdb_tgt_null, /* t_add_pwapt */ (int (*)()) mdb_tgt_null, /* t_add_vwapt */ (int (*)()) mdb_tgt_null, /* t_add_iowapt */ (int (*)()) mdb_tgt_null, /* t_add_sysenter */ (int (*)()) mdb_tgt_null, /* t_add_sysexit */ (int (*)()) mdb_tgt_null, /* t_add_signal */ (int (*)()) mdb_tgt_null, /* t_add_fault */ (int (*)()) mdb_tgt_notsup, /* t_getareg XXX */ (int (*)()) mdb_tgt_notsup, /* t_putareg XXX */ (int (*)()) mdb_tgt_notsup, /* t_stack_iter XXX */ kp_auxv /* t_auxv */ }; int mdb_kproc_tgt_create(mdb_tgt_t *t, int argc, const char *argv[]) { kp_data_t *kp = mdb_zalloc(sizeof (kp_data_t), UM_SLEEP); void *proc = (void *)argv[0]; long at_entry, at_base; GElf_Sym sym; int (*f_asiter)(uintptr_t, void (*)(const mdb_map_t *, void *), void *); int (*f_auxv)(uintptr_t, auxv_t *); uintptr_t (*f_as)(uintptr_t); uint_t (*f_model)(uintptr_t); pid_t (*f_pid)(uintptr_t); if (argc != 1) return (set_errno(EINVAL)); t->t_flags &= ~MDB_TGT_F_RDWR; t->t_data = kp; t->t_ops = &kproc_ops; f_asiter = (int (*)()) dlsym(RTLD_NEXT, "mdb_kproc_asiter"); f_auxv = (int (*)()) dlsym(RTLD_NEXT, "mdb_kproc_auxv"); f_as = (uintptr_t (*)()) dlsym(RTLD_NEXT, "mdb_kproc_as"); f_model = (model_t (*)()) dlsym(RTLD_NEXT, "mdb_kproc_model"); f_pid = (pid_t (*)()) dlsym(RTLD_NEXT, "mdb_kproc_pid"); if (f_asiter == NULL || f_auxv == NULL || f_as == NULL || f_model == NULL || f_pid == NULL) { warn("required kernel support module is not loaded\n"); goto err; } /* * Here the kproc target relies on the fact that at the time of its * instantiation, mdb.m_target is pointing at a kvm target, and * that the kvm target has stored its libkvm handle in t_pshandle. */ kp->kp_parent = mdb.m_target; kp->kp_cookie = mdb.m_target->t_pshandle; kp->kp_platform = mdb_tgt_platform(mdb.m_target); kp->kp_proc = proc; kp->kp_as = (struct as *)f_as((uintptr_t)proc); kp->kp_pid = f_pid((uintptr_t)proc); if (kp->kp_as == NULL) { warn("failed to obtain address space for proc %p\n", proc); goto err; } if (kp->kp_pid == -1) { warn("failed to obtain PID for proc %p\n", proc); goto err; } if (mdb_tgt_lookup_by_name(kp->kp_parent, MDB_TGT_OBJ_EXEC, "kas", &sym, NULL) == 0 && kp->kp_as == (struct as *)(uintptr_t)sym.st_value) { warn("specified process is a system process (no context)\n"); goto err; } if ((kp->kp_model = f_model((uintptr_t)proc)) == PR_MODEL_UNKNOWN) { warn("failed to obtain data model for proc %p\n", proc); goto err; } if (f_asiter((uintptr_t)kp->kp_as, kp_add_mapping, kp) == -1) { warn("failed to load mappings for proc %p", proc); goto err; } kp->kp_nauxv = f_auxv((uintptr_t)proc, NULL) + 1; kp->kp_auxv = mdb_alloc(sizeof (auxv_t) * kp->kp_nauxv, UM_SLEEP); if (f_auxv((uintptr_t)proc, kp->kp_auxv) == -1) { warn("failed to load auxv for proc %p", proc); goto err; } kp->kp_auxv[kp->kp_nauxv - 1].a_type = AT_NULL; kp->kp_auxv[kp->kp_nauxv - 1].a_un.a_val = 0; if ((at_entry = kp_getauxval(kp, AT_ENTRY)) == -1L) { warn("auxv for proc %p is missing AT_ENTRY\n", proc); goto err; } if ((at_base = kp_getauxval(kp, AT_BASE)) == -1L) { warn("auxv for proc %p is missing AT_BASE\n", proc); goto err; } /* * If we're applying kproc to a live kernel, we need to force libkvm * to set the current process to the process in question so we can * read from its address space. If kvm_getproc returns NULL, the * process may have gone away since our previous calls to mdb_ks. */ if (mdb_prop_postmortem == FALSE && kvm_getproc(kp->kp_cookie, kp->kp_pid) == NULL) warn("failed to attach to PID %d\n", (int)kp->kp_pid); kp->kp_map_exec = kp_addr_to_kpmap(kp, at_entry); kp->kp_map_ldso = kp_addr_to_kpmap(kp, at_base); (void) kp_file_create(t, kp->kp_map_exec, ET_EXEC); (void) kp_file_create(t, kp->kp_map_ldso, ET_DYN); kp->kp_prfile.kpf_dynsym = mdb.m_prsym; return (0); err: kp_destroy(t); return (-1); } static ssize_t kp_io_read(mdb_io_t *io, void *buf, size_t nbytes) { kp_io_t *kpi = io->io_data; kp_data_t *kp = kpi->kpi_tgt->t_data; kp_map_t *kpm = kp_addr_to_kpmap(kp, kpi->kpi_ptr); size_t left; if (kpm != NULL) { const mdb_map_t *mp = &kpm->kpm_map; left = mp->map_base + mp->map_size - kpi->kpi_ptr; } else left = 0; if (left != 0) { ssize_t rbytes = kp_vread(kpi->kpi_tgt, buf, MIN(nbytes, left), kpi->kpi_ptr); if (rbytes >= 0) kpi->kpi_ptr += rbytes; return (rbytes); } return (0); /* At end of segment or in hole; return EOF */ } static off64_t kp_io_seek(mdb_io_t *io, off64_t offset, int whence) { kp_io_t *kpi = io->io_data; const mdb_map_t *mp = &kpi->kpi_map->kpm_map; uintptr_t nptr; if (io->io_next != NULL) return (IOP_SEEK(io->io_next, offset, whence)); switch (whence) { case SEEK_SET: nptr = mp->map_base + offset; break; case SEEK_CUR: nptr = kpi->kpi_ptr + offset; break; case SEEK_END: nptr = kpi->kpi_lim + offset; break; default: return (set_errno(EINVAL)); } if (nptr < mp->map_base || nptr >= kpi->kpi_lim) return (set_errno(EINVAL)); kpi->kpi_ptr = nptr; return ((off64_t)(nptr - mp->map_base)); } static void kp_io_close(mdb_io_t *io) { mdb_free(io->io_data, sizeof (kp_io_t)); } static const char * kp_io_name(mdb_io_t *io) { kp_io_t *kpi = io->io_data; if (io->io_next != NULL) return (IOP_NAME(io->io_next)); return (kpi->kpi_map->kpm_map.map_name); } static const mdb_io_ops_t kp_io_ops = { kp_io_read, no_io_write, kp_io_seek, no_io_ctl, kp_io_close, kp_io_name, no_io_link, no_io_unlink, no_io_setattr, no_io_suspend, no_io_resume }; static mdb_io_t * kp_io_create(mdb_tgt_t *t, kp_map_t *kpm) { kp_data_t *kp = t->t_data; mdb_map_t *mp = &kp->kp_map_tail->kpm_map; mdb_io_t *io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP); kp_io_t *kpi = mdb_alloc(sizeof (kp_io_t), UM_SLEEP); kpi->kpi_tgt = t; kpi->kpi_map = kpm; kpi->kpi_ptr = kpm->kpm_map.map_base; kpi->kpi_lim = mp->map_base + mp->map_size; io->io_ops = &kp_io_ops; io->io_data = kpi; io->io_next = NULL; io->io_refcnt = 0; return (io); }