mdb_gelf.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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
* 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 <sys/isa_defs.h>
#include <strings.h>
#include <stdlib.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_gelf.h>
static const char *gelf_strtab; /* Active string table for qsort callbacks */
static mdb_gelf_file_t *
{
return (gf); /* If no section headers we're done here */
return (NULL);
}
return (NULL);
}
warn("section name for %s:[%u] is corrupt: %u\n",
}
npbit++; /* Keep count for ET_REL code below */
}
/*
* If the file is of type ET_REL, we would still like to provide file
* i/o using the mdb_gelf_rw() function defined below. To simplify
* things, we forge up a sequence of Phdrs based on Shdrs which have
* been marked SHF_ALLOC and are of type SHT_PROGBITS. We convert
* relevant Shdr fields to their Phdr equivalents, and then set the
* p_vaddr (virtual base address) to the section's file offset.
* This allows us to relocate a given symbol by simply incrementing
* its st_value by the file offset of the section corresponding to
* its st_shndx, and then perform i/o to read or write the symbol's
* value in the object file.
*/
gpp++;
}
}
}
return (gf);
}
static void *
{
warn("failed to seek to start of %s:%s",
goto err;
}
if (nbytes < 0) {
goto err;
}
}
err:
return (NULL);
}
void
{
}
static GElf_Shdr *
{
return (dst);
}
return (NULL);
}
static GElf_Shdr *
{
return (dst);
}
return (NULL);
}
static mdb_gelf_file_t *
{
GElf_Half i;
return (gf);
return (NULL);
}
return (NULL);
}
return (gf);
}
static GElf_Phdr *
{
return (dst);
}
return (NULL);
}
static GElf_Phdr *
{
return (dst);
}
return (NULL);
}
static int
{
/*
* If both p_type fields are PT_LOAD, we want to sort by vaddr.
* Exception is that p_vaddr == 0 means ignore this (put at end).
*/
return (1); /* lhs is "greater" */
return (-1); /* rhs is "greater" */
}
return (0);
}
/*
* If the p_type fields don't match, we need to make sure that PT_LOAD
* entries are considered "less" (i.e. move towards the beginning
* of the array we are sorting)
*/
return (-1); /* rhs is "greater" */
return (1); /* lhs is "greater" */
}
/*
* If the p_type is the same but neither is PT_LOAD, then
* just sort by file offset (doesn't really matter)
*/
return (0);
}
static mdb_gelf_file_t *
{
GElf_Half i;
return (gf);
return (NULL);
}
return (NULL);
}
/*
* Iterate through the list of phdrs locating those that are of type
* PT_LOAD; increment gf_npload so we know how many are loadable.
*/
continue;
"memsz=%llu filesz=%llu off=%llu\n", (u_longlong_t)
}
/*
* Now we sort the phdrs array using a comparison routine which
* arranges for the PT_LOAD phdrs with non-zero virtual addresses
* to come first sorted by virtual address. This means that we
* can access the complete phdr table by examining the array
* gf->gf_phdrs[0 .. gf->gf_ehdr.e_phnum - 1], and we can access a
* sorted array of valid PT_LOAD pdhrs by examining the array
* gf->gf_phdrs[0 .. gf->gf_npload - 1].
*/
/*
* Locate the PT_DYNAMIC Phdr if one is present; we save this
* Phdr pointer in gf->gf_dynp for future use.
*/
"filesize = %lluULL off=%lluULL\n",
break;
}
}
return (gf);
}
static GElf_Dyn *
{
return (dst);
}
return (NULL);
}
static GElf_Dyn *
{
return (dst);
}
return (NULL);
}
static GElf_Xword
{
size_t i;
}
return ((GElf_Xword)-1L);
}
static GElf_Dyn *
{
return (NULL); /* No PT_DYNAMIC entry was found */
/*
* If this is an executable in PROGRAM view, then p_vaddr is an
* absolute address; we need to subtract the virtual base address of
* the mapping. In FILE view, dyn_addr is just the file offset.
*/
else
} else {
break;
}
}
return (NULL); /* No SHT_DYNAMIC entry was found */
}
return (NULL);
}
return (NULL);
}
}
static mdb_gelf_file_t *
{
/*
* Convert the Elf32_Ehdr to a GElf_Ehdr
*/
/*
* Initialize the section and program headers. We skip initializing
* the section headers if this is a program image because they are
* not loadable and thus we can't get at them.
*/
return (NULL);
return (NULL);
return (gf);
}
static mdb_gelf_file_t *
{
/*
* Save a copy of the ELF file header
*/
/*
* Initialize the section and program headers. We skip initializing
* the section headers if this is a program image because they are
* not loadable and thus we can't get at them.
*/
return (NULL);
return (NULL);
return (gf);
}
int
{
#ifdef _BIG_ENDIAN
#else
#endif
if (nbytes == -1) {
return (-1);
}
if (nbytes != sizeof (Elf32_Ehdr) ||
return (-1);
}
warn("ELF file %s has different endianness from debugger\n",
return (-1);
}
warn("ELF file %s uses different ELF version (%lu) than "
return (-1);
}
return (-1);
}
return (0);
}
{
union {
} ehdr;
/*
* Assign the i/o backend now, but don't hold it until we're sure
* we're going to succeed; otherwise the caller will be responsible
* for mdb_io_destroy()ing it.
*/
goto err;
case ELFCLASS32:
break;
case ELFCLASS64:
goto err;
}
goto err;
}
break;
default:
warn("%s is an unsupported ELF class: %u\n",
goto err;
}
return (gf);
}
err:
sizeof (mdb_gelf_sect_t));
}
}
return (NULL);
}
void
{
GElf_Half i;
}
}
/*
* 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
{
mdb_var_t *v;
}
sizeof (Elf32_Sym *), gelf32_sym_compare);
gelf_strtab = NULL;
}
static void
{
GElf_Word i, n;
warn("%s sh_entsize %llu != sizeof (Elf32_Sym); "
}
for (i = 0; i < n; i++, sym++) {
continue; /* skip sections and unknown types */
warn("ignoring %s symbol [%u]: invalid name\n",
}
continue; /* skip corrupt or empty names */
}
asrsv++; /* reserve space in the address map */
}
asrsv++; /* reserve space in asmap */
}
}
}
}
static void
{
mdb_var_t *v;
}
sizeof (Elf64_Sym *), gelf64_sym_compare);
gelf_strtab = NULL;
}
static void
{
GElf_Word i, n;
warn("%s sh_entsize %llu != sizeof (Elf64_Sym); "
}
for (i = 0; i < n; i++, sym++) {
continue; /* skip sections and unknown types */
warn("ignoring %s symbol [%u]: invalid name\n",
}
continue; /* skip corrupt or empty names */
}
asrsv++; /* reserve space in the address map */
}
asrsv++; /* reserve space in asmap */
}
}
}
}
{
const char *ssname;
GElf_Half i;
/*
* Examine the sh_link field in the the Elf header to get the name
* of the corresponding strings section
*/
break;
}
}
return (NULL);
/*
* Invalid link number due to corrupt elf file.
*/
warn("link number %ud larger than number of sections %d\n",
return (NULL);
}
}
{
GElf_Half i;
break;
}
}
break;
}
}
goto err; /* Failed to locate data or string section */
goto err; /* Failed to load data section */
goto err; /* Failed to load string section */
else
return (gst);
err:
return (NULL);
}
{
} else {
}
return (gst);
}
{
/*
* Read in and cache the array of GElf_Dyn structures from the
* PT_DYNAMIC phdr. Abort if this is not possible.
*/
} else {
}
/*
* Pre-fetch all the DT_* entries we will need for creating the
* dynamic symbol table; abort if any are missing.
*/
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
/*
* If this is an executable, then DT_HASH is an absolute address;
* we need to subtract the virtual base address of the mapping.
*/
/*
* Read in the header for the DT_HASH: this consists of nbucket
* and nchain values (nchain is the number of hashed symbols).
*/
warn("failed to seek ELF file to start of DT_HASH");
return (NULL);
}
warn("failed to read DT_HASH header");
return (NULL);
}
goto err;
goto err;
else
return (gst);
err:
return (NULL);
}
{
#ifdef _LP64
#else
#endif
#ifdef _BIG_ENDIAN
#else
#endif
/*
* Since all mutable symbol tables will use a native Ehdr,
* we can just have a single static copy which they all
* point to and we only need initialize once.
*/
}
return (gst);
}
void
{
mdb_var_t *v;
char *name = (char *)mdb_nv_get_name(v);
}
} else {
}
}
}
{
}
static GElf_Sym *
{
return (dst);
}
return (NULL);
}
static GElf_Sym *
{
return (dst);
}
return (NULL);
}
/*ARGSUSED*/
static GElf_Sym *
{
}
static const void *
{
Elf32_Addr v;
if (aslen == 0)
return (NULL);
else
}
/*
* If the previous entry has the same value, improve our choice. The
* order of equal-valued symbols is determined by gelf32_sym_compare().
*/
/*
* If an absolute symbol distance was specified, use that; otherwise
* use the ELF symbol size, or 1 byte if the ELF size is zero.
*/
else
return (symp);
return (NULL);
}
static const void *
{
Elf64_Addr v;
if (aslen == 0)
return (NULL);
else
}
/*
* If the previous entry has the same value, improve our choice. The
* order of equal-valued symbols is determined by gelf64_sym_compare().
*/
/*
* If an absolute symbol distance was specified, use that; otherwise
* use the ELF symbol size, or 1 byte if the ELF size is zero.
*/
else
return (symp);
return (NULL);
}
const char *
{
const mdb_gelf_dsym_t *dsp;
else
return (NULL);
}
int
{
}
int
{
union {
const mdb_gelf_dsym_t *dsp;
} u;
const char *name;
return (set_errno(EMDB_NOSYMADDR));
return (set_errno(EMDB_NOSYMADDR));
} else {
return (set_errno(EMDB_NOSYMADDR));
}
return (set_errno(EMDB_NOSYMADDR));
}
} else {
}
if (nbytes > 0) {
}
return (0);
}
int
{
mdb_var_t *v;
else
} else {
}
}
return (0);
}
return (set_errno(EMDB_NOSYM));
}
int
{
mdb_var_t *v;
return (set_errno(EMDB_NOSYM));
return (set_errno(EMDB_NOOBJ));
} else {
}
return (set_errno(EMDB_NOOBJ));
/*
* We assume that symbol lookups scoped by source file name are only
* relevant for userland debugging and are a relatively rare request,
* and so we use a simple but inefficient linear search with copying.
*/
break; /* End of this file's locals */
}
return (0);
}
}
return (set_errno(EMDB_NOSYM));
}
void
{
} else {
}
/*
* If this is a mutable symbol table, we iterate over the hash table
* of symbol names; otherwise we go iterate over the data buffer. For
* non-mutable tables, this means that ::nm will show all symbols,
* including those with duplicate names (not present in gst_nv).
*/
mdb_var_t *v;
dsp = mdb_nv_get_cookie(v);
break;
}
} else {
uint_t i;
break;
}
}
}
static void
{
}
static void
{
}
void
{
mdb_var_t *v;
if (v == NULL) {
}
} else
dsp = mdb_nv_get_cookie(v);
} else {
}
}
void
{
mdb_var_t *v;
if (v != NULL) {
char *name = (char *)mdb_nv_get_name(v);
else
}
}
static const GElf_Phdr *
{
GElf_Half i;
return (gpp);
}
return (NULL);
}
{
while (resid != 0) {
break; /* No mapping for this address */
break;
}
}
return (set_errno(EMDB_NOMAP));
}