void dt_module_destroy(dtrace_hdl_t *dtp, dt_module_t *dmp) { uint_t h = dt_strtab_hash(dmp->dm_name, NULL) % dtp->dt_modbuckets; dt_module_t **dmpp = &dtp->dt_mods[h]; dt_list_delete(&dtp->dt_modlist, dmp); assert(dtp->dt_nmods != 0); dtp->dt_nmods--; /* * Now remove this module from its hash chain. We expect to always * find the module on its hash chain, so in this loop we assert that * we don't run off the end of the list. */ while (*dmpp != dmp) { dmpp = &((*dmpp)->dm_next); assert(*dmpp != NULL); } *dmpp = dmp->dm_next; dt_module_unload(dtp, dmp); free(dmp); }
void dt_module_destroy(dtrace_hdl_t *dtp, dt_module_t *dmp) { dt_list_delete(&dtp->dt_modlist, dmp); assert(dtp->dt_nmods != 0); dtp->dt_nmods--; dt_module_unload(dtp, dmp); free(dmp); }
/* * Unload all the loaded modules and then refresh the module cache with the * latest list of loaded modules and their address ranges. */ void dtrace_update(dtrace_hdl_t *dtp) { dt_module_t *dmp; DIR *dirp; for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) dt_module_unload(dtp, dmp); if (!(dtp->dt_oflags & DTRACE_O_NOSYS)) { dt_module_update(dtp, "mach_kernel"); } /* * Look up all the macro identifiers and set di_id to the latest value. * This code collaborates with dt_lex.l on the use of di_id. We will * need to implement something fancier if we need to support non-ints. */ dt_idhash_lookup(dtp->dt_macros, "egid")->di_id = getegid(); dt_idhash_lookup(dtp->dt_macros, "euid")->di_id = geteuid(); dt_idhash_lookup(dtp->dt_macros, "gid")->di_id = getgid(); dt_idhash_lookup(dtp->dt_macros, "pid")->di_id = getpid(); dt_idhash_lookup(dtp->dt_macros, "pgid")->di_id = getpgid(0); dt_idhash_lookup(dtp->dt_macros, "ppid")->di_id = getppid(); dt_idhash_lookup(dtp->dt_macros, "projid")->di_id = getprojid(); dt_idhash_lookup(dtp->dt_macros, "sid")->di_id = getsid(0); dt_idhash_lookup(dtp->dt_macros, "taskid")->di_id = gettaskid(); dt_idhash_lookup(dtp->dt_macros, "uid")->di_id = getuid(); /* * Cache the pointers to the modules representing the base executable * and the run-time linker in the dtrace client handle. Note that on * x86 krtld is folded into unix, so if we don't find it, use unix * instead. */ dtp->dt_exec = dt_module_lookup_by_name(dtp, "mach_kernel"); dtp->dt_rtld = dt_module_lookup_by_name(dtp, "dyld"); /* XXX to what purpose? */ /* * If this is the first time we are initializing the module list, * remove the module for genunix from the module list and then move it * to the front of the module list. We do this so that type and symbol * queries encounter genunix and thereby optimize for the common case * in dtrace_lookup_by_name() and dtrace_lookup_by_type(), below. */ if (dtp->dt_exec != NULL && dtp->dt_cdefs == NULL && dtp->dt_ddefs == NULL) { dt_list_delete(&dtp->dt_modlist, dtp->dt_exec); dt_list_prepend(&dtp->dt_modlist, dtp->dt_exec); } }
int dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp) { if (dmp->dm_flags & DT_DM_LOADED) return (0); /* module is already loaded */ if (dmp->dm_pid != 0) return (dt_module_load_proc(dtp, dmp)); dmp->dm_ctdata.cts_name = ".SUNW_ctf"; dmp->dm_ctdata.cts_type = SHT_PROGBITS; dmp->dm_ctdata.cts_flags = 0; dmp->dm_ctdata.cts_data = NULL; dmp->dm_ctdata.cts_size = 0; dmp->dm_ctdata.cts_entsize = 0; dmp->dm_ctdata.cts_offset = 0; dmp->dm_symtab.cts_name = ".symtab"; dmp->dm_symtab.cts_type = SHT_SYMTAB; dmp->dm_symtab.cts_flags = 0; dmp->dm_symtab.cts_data = NULL; dmp->dm_symtab.cts_size = 0; dmp->dm_symtab.cts_entsize = dmp->dm_ops == &dt_modops_64 ? sizeof (Elf64_Sym) : sizeof (Elf32_Sym); dmp->dm_symtab.cts_offset = 0; dmp->dm_strtab.cts_name = ".strtab"; dmp->dm_strtab.cts_type = SHT_STRTAB; dmp->dm_strtab.cts_flags = 0; dmp->dm_strtab.cts_data = NULL; dmp->dm_strtab.cts_size = 0; dmp->dm_strtab.cts_entsize = 0; dmp->dm_strtab.cts_offset = 0; /* * Attempt to load the module's CTF section, symbol table section, and * string table section. Note that modules may not contain CTF data: * this will result in a successful load_sect but data of size zero. * We will then fail if dt_module_getctf() is called, as shown below. */ if (dt_module_load_sect(dtp, dmp, &dmp->dm_ctdata) == -1 || dt_module_load_sect(dtp, dmp, &dmp->dm_symtab) == -1 || dt_module_load_sect(dtp, dmp, &dmp->dm_strtab) == -1) { dt_module_unload(dtp, dmp); return (-1); /* dt_errno is set for us */ } /* * Allocate the hash chains and hash buckets for symbol name lookup. * This is relatively simple since the symbol table is of fixed size * and is known in advance. We allocate one extra element since we * use element indices instead of pointers and zero is our sentinel. */ dmp->dm_nsymelems = dmp->dm_symtab.cts_size / dmp->dm_symtab.cts_entsize; dmp->dm_nsymbuckets = _dtrace_strbuckets; dmp->dm_symfree = 1; /* first free element is index 1 */ dmp->dm_symbuckets = malloc(sizeof (uint_t) * dmp->dm_nsymbuckets); dmp->dm_symchains = malloc(sizeof (dt_sym_t) * dmp->dm_nsymelems + 1); if (dmp->dm_symbuckets == NULL || dmp->dm_symchains == NULL) { dt_module_unload(dtp, dmp); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_symbuckets, sizeof (uint_t) * dmp->dm_nsymbuckets); bzero(dmp->dm_symchains, sizeof (dt_sym_t) * dmp->dm_nsymelems + 1); /* * Iterate over the symbol table data buffer and insert each symbol * name into the name hash if the name and type are valid. Then * allocate the address map, fill it in, and sort it. */ dmp->dm_asrsv = dmp->dm_ops->do_syminit(dmp); dt_dprintf("hashed %s [%s] (%u symbols)\n", dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_symfree - 1); if ((dmp->dm_asmap = malloc(sizeof (void *) * dmp->dm_asrsv)) == NULL) { dt_module_unload(dtp, dmp); return (dt_set_errno(dtp, EDT_NOMEM)); } dmp->dm_ops->do_symsort(dmp); dt_dprintf("sorted %s [%s] (%u symbols)\n", dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_aslen); dmp->dm_flags |= DT_DM_LOADED; return (0); }
/* * We've been asked to load data that belongs to another process. As such we're * going to pgrab it at this instant, load everything that we might ever care * about, and then drive on. The reason for this is that the process that we're * interested in might be changing. As long as we have grabbed it, then this * can't be a problem for us. * * For now, we're actually going to punt on most things and just try to get CTF * data, nothing else. Basically this is only useful as a source of type * information, we can't go and do the stacktrace lookups, etc. */ static int dt_module_load_proc(dtrace_hdl_t *dtp, dt_module_t *dmp) { struct ps_prochandle *p; dt_module_cb_arg_t arg; /* * Note that on success we do not release this hold. We must hold this * for our life time. */ p = dt_proc_grab(dtp, dmp->dm_pid, 0, PGRAB_RDONLY | PGRAB_FORCE); if (p == NULL) { dt_dprintf("failed to grab pid: %d\n", (int)dmp->dm_pid); return (dt_set_errno(dtp, EDT_CANTLOAD)); } dt_proc_lock(dtp, p); arg.dpa_proc = p; arg.dpa_dtp = dtp; arg.dpa_dmp = dmp; arg.dpa_count = 0; if (Pobject_iter_resolved(p, dt_module_load_proc_count, &arg) != 0) { dt_dprintf("failed to iterate objects\n"); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_CANTLOAD)); } if (arg.dpa_count == 0) { dt_dprintf("no ctf data present\n"); dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_CANTLOAD)); } dmp->dm_libctfp = malloc(sizeof (ctf_file_t *) * arg.dpa_count); if (dmp->dm_libctfp == NULL) { dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_libctfp, sizeof (ctf_file_t *) * arg.dpa_count); dmp->dm_libctfn = malloc(sizeof (char *) * arg.dpa_count); if (dmp->dm_libctfn == NULL) { free(dmp->dm_libctfp); dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_libctfn, sizeof (char *) * arg.dpa_count); dmp->dm_nctflibs = arg.dpa_count; arg.dpa_count = 0; if (Pobject_iter_resolved(p, dt_module_load_proc_build, &arg) != 0) { dt_proc_unlock(dtp, p); dt_module_unload(dtp, dmp); dt_proc_release(dtp, p); return (dt_set_errno(dtp, EDT_CANTLOAD)); } assert(arg.dpa_count == dmp->dm_nctflibs); dt_dprintf("loaded %d ctf modules for pid %d\n", arg.dpa_count, (int)dmp->dm_pid); dt_proc_unlock(dtp, p); dt_proc_release(dtp, p); dmp->dm_flags |= DT_DM_LOADED; return (0); }
/* * Unload all the loaded modules and then refresh the module cache with the * latest list of loaded modules and their address ranges. */ void dtrace_update(dtrace_hdl_t *dtp) { dt_module_t *dmp; DIR *dirp; #if defined(__FreeBSD__) int fileid; #endif for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) dt_module_unload(dtp, dmp); #if defined(sun) /* * Open /system/object and attempt to create a libdtrace module for * each kernel module that is loaded on the current system. */ if (!(dtp->dt_oflags & DTRACE_O_NOSYS) && (dirp = opendir(OBJFS_ROOT)) != NULL) { struct dirent *dp; while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] != '.') dt_module_update(dtp, dp->d_name); } (void) closedir(dirp); } #elif defined(__FreeBSD__) /* * Use FreeBSD's kernel loader interface to discover what kernel * modules are loaded and create a libdtrace module for each one. */ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { struct kld_file_stat k_stat; k_stat.version = sizeof(k_stat); if (kldstat(fileid, &k_stat) == 0) dt_module_update(dtp, &k_stat); } #endif /* * Look up all the macro identifiers and set di_id to the latest value. * This code collaborates with dt_lex.l on the use of di_id. We will * need to implement something fancier if we need to support non-ints. */ dt_idhash_lookup(dtp->dt_macros, "egid")->di_id = getegid(); dt_idhash_lookup(dtp->dt_macros, "euid")->di_id = geteuid(); dt_idhash_lookup(dtp->dt_macros, "gid")->di_id = getgid(); dt_idhash_lookup(dtp->dt_macros, "pid")->di_id = getpid(); dt_idhash_lookup(dtp->dt_macros, "pgid")->di_id = getpgid(0); dt_idhash_lookup(dtp->dt_macros, "ppid")->di_id = getppid(); #if defined(sun) dt_idhash_lookup(dtp->dt_macros, "projid")->di_id = getprojid(); #endif dt_idhash_lookup(dtp->dt_macros, "sid")->di_id = getsid(0); #if defined(sun) dt_idhash_lookup(dtp->dt_macros, "taskid")->di_id = gettaskid(); #endif dt_idhash_lookup(dtp->dt_macros, "uid")->di_id = getuid(); /* * Cache the pointers to the modules representing the base executable * and the run-time linker in the dtrace client handle. Note that on * x86 krtld is folded into unix, so if we don't find it, use unix * instead. */ dtp->dt_exec = dt_module_lookup_by_name(dtp, "genunix"); dtp->dt_rtld = dt_module_lookup_by_name(dtp, "krtld"); if (dtp->dt_rtld == NULL) dtp->dt_rtld = dt_module_lookup_by_name(dtp, "unix"); /* * If this is the first time we are initializing the module list, * remove the module for genunix from the module list and then move it * to the front of the module list. We do this so that type and symbol * queries encounter genunix and thereby optimize for the common case * in dtrace_lookup_by_name() and dtrace_lookup_by_type(), below. */ if (dtp->dt_exec != NULL && dtp->dt_cdefs == NULL && dtp->dt_ddefs == NULL) { dt_list_delete(&dtp->dt_modlist, dtp->dt_exec); dt_list_prepend(&dtp->dt_modlist, dtp->dt_exec); } }