dt_module.c revision a386cc11a86ecb60f5a48078d22c1500e2ad003e
/*
* 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
* 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) 2013, Joyent, Inc. All rights reserved.
*/
#include <sys/kobj_impl.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <project.h>
#include <strings.h>
#include <stdlib.h>
#include <libelf.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <dirent.h>
#include <dt_strtab.h>
#include <dt_module.h>
#include <dt_impl.h>
static const char *dt_module_strtab; /* active strtab for qsort callbacks */
static void
{
uint_t h;
}
static uint_t
{
#error "STT_NUM has grown. update dt_module_syminit32()"
#endif
for (i = 0; i < n; i++, sym++) {
continue; /* skip sections and unknown types */
continue; /* skip null or invalid names */
asrsv++; /* reserve space in the address map */
}
return (asrsv);
}
static uint_t
{
#error "STT_NUM has grown. update dt_module_syminit64()"
#endif
for (i = 0; i < n; i++, sym++) {
continue; /* skip sections and unknown types */
continue; /* skip null or invalid names */
asrsv++; /* reserve space in the address map */
}
return (asrsv);
}
/*
* Sort comparison function for 32-bit symbol address-to-name lookups. We sort
* symbols by value. If values are equal, we prefer the symbol that is
* non-zero sized, typed, not weak, or lexically first, in that order.
*/
static int
{
}
/*
* Sort comparison function for 64-bit symbol address-to-name lookups. We sort
* symbols by value. If values are equal, we prefer the symbol that is
* non-zero sized, typed, not weak, or lexically first, in that order.
*/
static int
{
}
static void
{
for (i = 1; i < n; i++, dsp++) {
}
sizeof (Elf32_Sym *), dt_module_symcomp32);
}
static void
{
for (i = 1; i < n; i++, dsp++) {
}
sizeof (Elf64_Sym *), dt_module_symcomp64);
}
static GElf_Sym *
{
}
return (dst);
}
static GElf_Sym *
{
return (dst);
}
static GElf_Sym *
{
uint_t i, h;
if (dmp->dm_nsymelems == 0)
return (NULL);
}
}
return (NULL);
}
static GElf_Sym *
{
uint_t i, h;
if (dmp->dm_nsymelems == 0)
return (NULL);
}
}
return (NULL);
}
static GElf_Sym *
{
Elf32_Addr v;
return (NULL);
else
}
/*
* If the previous entry has the same value, improve our choice. The
* order of equal-valued symbols is determined by the comparison func.
*/
}
return (NULL);
}
static GElf_Sym *
{
Elf64_Addr v;
return (NULL);
else
}
/*
* If the previous entry has the same value, improve our choice. The
* order of equal-valued symbols is determined by the comparison func.
*/
}
return (NULL);
}
static const dt_modops_t dt_modops_32 = {
};
static const dt_modops_t dt_modops_64 = {
};
{
long pid;
char *eptr;
return (dmp);
}
return (NULL); /* caller must handle allocation failure */
else
/*
* Modules for userland processes are special. They always refer to a
* specific process and have a copy of their CTF data from a specific
* instant in time. Any dt_module_t that begins with 'pid' is a module
* for a specific process, much like how any probe description that
* begins with 'pid' is special. pid123 refers to process 123. A module
* that is just 'pid' refers specifically to pid$target. This is
* generally done as D does not currently allow for macros to be
* evaluated when working with types.
*/
errno = 0;
} else {
else
dt_dprintf("encountered malformed pid "
}
}
return (dmp);
}
{
return (dmp);
}
return (NULL);
}
/*ARGSUSED*/
{
}
static int
{
const char *s;
continue; /* skip any malformed sections */
break; /* section matches specification */
}
/*
* If the section isn't found, return success but leave cts_data set
* to NULL and cts_size set to zero for our caller.
*/
return (0);
dt_dprintf("loaded %s [%s] (%lu bytes)\n",
return (0);
}
typedef struct dt_module_cb_arg {
struct ps_prochandle *dpa_proc;
/* ARGSUSED */
static int
{
ctf_file_t *fp;
/* Try to grab a ctf container if it exists */
return (0);
}
/* ARGSUSED */
static int
{
ctf_file_t *fp;
char buf[MAXPATHLEN], *p;
return (0);
return (0);
/*
* While it'd be nice to simply use objname here, because of our prior
* actions we'll always get a resolved object name to its on disk file.
* Like the pid provider, we need to tell a bit of a lie here. The type
* that the user thinks of is in terms of the libraries they requested,
* eg. libc.so.1, they don't care about the fact that it's
* libc_hwcap.so.1.
*/
p = buf;
else
p++;
/*
* If for some reason we can't find a link map id for this module, which
* would be really quite weird. We instead just say the link map id is
* zero.
*/
lmid = 0;
if (lmid == 0)
else
"LM%lx`%s", lmid, p);
return (1);
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
{
struct ps_prochandle *p;
/*
* Note that on success we do not release this hold. We must hold this
* for our life time.
*/
if (p == NULL) {
}
dt_proc_lock(dtp, p);
dt_dprintf("failed to iterate objects\n");
dt_proc_release(dtp, p);
}
dt_dprintf("no ctf data present\n");
dt_proc_unlock(dtp, p);
dt_proc_release(dtp, p);
}
dt_proc_unlock(dtp, p);
dt_proc_release(dtp, p);
}
dt_proc_unlock(dtp, p);
dt_proc_release(dtp, p);
}
dt_proc_unlock(dtp, p);
dt_proc_release(dtp, p);
}
dt_proc_unlock(dtp, p);
dt_proc_release(dtp, p);
return (0);
}
int
{
return (0); /* module is already loaded */
/*
* 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.
*/
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 =
}
/*
* 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.
*/
dt_dprintf("hashed %s [%s] (%u symbols)\n",
}
dt_dprintf("sorted %s [%s] (%u symbols)\n",
return (0);
}
int
{
return (1);
}
{
const char *parent;
int model;
else
/*
* If the data model of the module does not match our program data
* model, then do not permit CTF from this module to be opened and
* returned to the compiler. If we support mixed data models in the
*/
return (NULL);
}
return (NULL);
}
return (NULL);
}
goto err;
}
goto err;
}
}
dt_dprintf("loaded CTF container for %s (%p)\n",
err:
return (NULL);
}
/*ARGSUSED*/
void
{
int i;
for (i = 0; i < dmp->dm_nctflibs; i++) {
}
dmp->dm_nctflibs = 0;
}
}
}
}
dmp->dm_symfree = 0;
dmp->dm_nsymbuckets = 0;
dmp->dm_nsymelems = 0;
dmp->dm_text_size = 0;
dmp->dm_data_size = 0;
dmp->dm_bss_size = 0;
}
}
void
{
/*
* 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.
*/
}
}
/*
* Insert a new external symbol reference into the specified module. The new
* symbol will be marked as undefined and is assigned a symbol index beyond
* any existing cached symbols from this module. We use the ident's di_data
* field to store a pointer to a copy of the dtrace_syminfo_t for this symbol.
*/
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (idp);
}
const char *
{
return ("64-bit");
else
return ("32-bit");
}
/* ARGSUSED */
int
{
int i;
for (i = 0; i < dmp->dm_nctflibs; i++) {
return (i);
}
return (-1);
}
/* ARGSUSED */
{
int i;
for (i = 0; i < dmp->dm_nctflibs; i++) {
return (dmp->dm_libctfp[i]);
}
return (NULL);
}
/*
* Update our module cache by adding an entry for the specified module 'name'.
*/
static void
{
char fname[MAXPATHLEN];
const char *s;
return;
}
/*
* will return ENOENT), tell libelf to cook the entire file now and
* then close the underlying file descriptor immediately. If this
* succeeds, we know that we can continue safely using dmp->dm_elf.
*/
dt_dprintf("failed to load %s: %s\n",
return;
}
case ELFCLASS32:
bits = 32;
break;
case ELFCLASS64:
bits = 64;
break;
default:
return;
}
/*
* Iterate over the section headers locating various sections of
* interest and use their attributes to flesh out the dt_module_t.
*/
continue; /* skip any malformed sections */
if (strcmp(s, ".text") == 0) {
} else if (strcmp(s, ".data") == 0) {
} else if (strcmp(s, ".bss") == 0) {
} else if (strcmp(s, ".info") == 0 &&
} else if (strcmp(s, ".filename") == 0 &&
}
}
dt_dprintf("opened %d-bit module %s (%s) [%d]\n",
}
/*
* Unload all the loaded modules and then refresh the module cache with the
* latest list of loaded modules and their address ranges.
*/
void
{
/*
* each kernel module that is loaded on the current system.
*/
}
}
/*
* 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.
*/
/*
* 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.
*/
/*
* 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.
*/
}
}
static dt_module_t *
{
case (uintptr_t)DTRACE_OBJ_EXEC:
break;
case (uintptr_t)DTRACE_OBJ_RTLD:
break;
case (uintptr_t)DTRACE_OBJ_CDEFS:
break;
case (uintptr_t)DTRACE_OBJ_DDEFS:
break;
default:
}
return (dmp);
}
/*
* Exported interface to look up a symbol by name. We return the GElf_Sym and
* complete symbol information for the matching symbol.
*/
int
{
if (object != DTRACE_OBJ_EVERY &&
object != DTRACE_OBJ_KMODS &&
object != DTRACE_OBJ_UMODS) {
return (-1); /* dt_errno is set for us */
return (-1); /* dt_errno is set for us */
n = 1;
} else {
if (object == DTRACE_OBJ_KMODS)
else if (object == DTRACE_OBJ_UMODS)
mask = DT_DM_KERNEL;
}
continue; /* failed to match required attributes */
continue; /* failed to load symbol table */
}
return (0);
}
}
}
return (0);
}
}
}
/*
* Exported interface to look up a symbol by address. We return the GElf_Sym
* and complete symbol information for the matching symbol.
*/
int
{
if (v != NULL)
break;
}
return (-1); /* dt_errno is set for us */
}
} else {
}
}
return (0);
}
int
{
int found = 0;
uint_t n, i;
int justone;
ctf_file_t *fp;
char *buf, *p, *q;
if (object != DTRACE_OBJ_EVERY &&
object != DTRACE_OBJ_KMODS &&
object != DTRACE_OBJ_UMODS) {
return (-1); /* dt_errno is set for us */
return (-1); /* dt_errno is set for us */
n = 1;
justone = 1;
} else {
if (object == DTRACE_OBJ_KMODS)
else if (object == DTRACE_OBJ_UMODS)
mask = DT_DM_KERNEL;
justone = 0;
}
continue; /* failed to match required attributes */
/*
* If we can't load the CTF container, continue on to the next
* module. If our search was scoped to only one module then
* return immediately leaving dt_errno unmodified.
*/
if (justone)
return (-1);
continue;
}
/*
* Look up the type in the module's CTF container. If our
* match is a forward declaration tag, save this choice in
* 'tip' and keep going in the hope that we will locate the
* underlying structure definition. Otherwise just return.
*/
} else {
p = q;
*p = '\0';
p + 1)) == CTF_ERR)
} else {
for (i = 0; i < dmp->dm_nctflibs; i++) {
break;
}
}
}
return (0);
found++;
}
}
if (found == 0)
return (0);
}
int
{
dt_ident_t *idp =
return (-1); /* errno is set for us */
}
} else {
}
return (0);
}
static dtrace_objinfo_t *
{
return (dto);
}
int
{
int rv;
return (rv);
}
return (0);
}
int
{
return (-1); /* dt_errno is set for us */
return (-1); /* dt_errno is set for us */
return (0);
}