/*
* 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 2011 Jason King. All rights reserved.
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <gelf.h>
#include <libelf.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include "dis_target.h"
#include "dis_util.h"
/*
* Standard ELF disassembler target.
*
* We only support disassembly of ELF files, though this target interface could
* be extended in the future. Each basic type (target, func, section) contains
* enough information to uniquely identify the location within the file. The
* interfaces use libelf(3LIB) to do the actual processing of the file.
*/
/*
* Symbol table entry type. We maintain our own symbol table sorted by address,
* with the symbol name already resolved against the ELF symbol table.
*/
typedef struct sym_entry {
} sym_entry_t;
/*
* Create a map of the virtual address ranges of every section. This will
* allow us to create dummpy mappings for unassigned addresses. Otherwise
* multiple sections with unassigned addresses will appear to overlap and
* mess up symbol resolution (which uses the virtual address).
*/
typedef struct dis_shnmap {
} dis_shnmap_t;
/*
* Target data structure. This structure keeps track of the ELF file
* information, a few bits of pre-processed section index information, and
* sorted versions of the symbol table. We also keep track of the last symbol
* looked up, as the majority of lookups remain within the same symbol.
*/
struct dis_tgt {
};
/*
* Function data structure. We resolve the symbol and lookup the associated ELF
* data when building this structure. The offset is calculated based on the
* section's starting address.
*/
struct dis_func {
};
/*
* Section data structure. We store the entire section header so that we can
* determine some properties (such as whether or not it contains text) after
* building the structure.
*/
struct dis_scn {
const char *ds_name;
};
/* Lifted from Psymtab.c, omitting STT_TLS */
#define DATA_TYPES \
/*
* Save the virtual address range for this section and select the
* best section to use as the symbol table. We prefer SHT_SYMTAB
* over SHT_DYNSYM.
*/
/* ARGSUSED */
static void
{
*index += 1;
/*
* Prefer SHT_SYMTAB over SHT_DYNSYM
*/
}
static int
sym_compare(const void *a, const void *b)
{
return (-1);
return (1);
/*
* Prefer functions over non-functions
*/
return (-1);
return (1);
}
/*
* For symbols with the same address and type, we sort them according to
* a hierarchy:
*
* 1. weak symbols (common name)
* 2. global symbols (external name)
* 3. local symbols
*/
return (-1);
return (1);
return (-1);
return (1);
}
/*
* As a last resort, if we have multiple symbols of the same type at the
* same address, prefer the version with the fewest leading underscores.
*/
return (-1);
return (1);
aname++;
bname++;
}
if (*bname == '_')
return (-1);
if (*aname == '_')
return (1);
/*
* Prefer the symbol with the smaller size.
*/
return (-1);
return (1);
/*
* We really do have two identical symbols for some reason. Just report
* them as equal, and to the lucky one go the spoils.
*/
return (0);
}
/*
* Construct an optimized symbol table sorted by starting address.
*/
static void
{
int i;
int symshndx_size;
/*
* Find the symshndx section, if any
*/
break;
sizeof (GElf_Word);
break;
}
}
}
if (shdr.sh_entsize == 0)
1, EV_CURRENT);
warn("%s: gelf_getsym returned NULL for %d",
tgt->dt_filename, i);
nsym++;
continue;
}
/*
* We're only interested in data symbols.
*/
nsym++;
continue;
}
if (i > symshndx_size) {
warn("%s: bad SHNX_XINDEX %d",
tgt->dt_filename, i);
} else {
}
} else {
}
/* Deal with symbols with special section indicies */
/*
* If st_value == 0, references to these
* symbols in code are modified in situ
* thus we will never attempt to look
* them up.
*/
/*
* References to these symbols in code
* are modified in situ by the runtime
* linker and no code on disk will ever
* attempt to look them up.
*/
nsym++;
continue;
} else {
/*
* If st_value != 0, (such as examining
* the values should resolve to a value
* within an existing section (such as
* .data). This also means it never needs
* to have st_value mapped.
*/
sym++;
continue;
}
}
/*
* Ignore the symbol if it has some other special
* section index
*/
nsym++;
continue;
}
warn("%s: failed to lookup symbol %d name",
tgt->dt_filename, i);
nsym++;
continue;
}
/*
* If we had to map this section, its symbol value
* also needs to be mapped.
*/
sym++;
}
sizeof (sym_entry_t));
}
/*
* Assign virtual address ranges for sections that need it
*/
static void
{
int i;
return;
/* find the greatest used address */
/*
* Assign section a starting address beyond the largest mapped section
* if no address was given.
*/
continue;
}
}
/*
* Create a target backed by an ELF file.
*/
{
int idx;
int cmd;
die("libelf(3ELF) out of date");
return (NULL);
}
if ((tgt->dt_elf_root =
return (NULL);
}
cmd = ELF_C_READ;
return (NULL);
}
/*
* Make sure that this Elf file is sane
*/
/*
* For archives, we drive on in the face of bad
* members. The "/" and "//" members are
* special, and should be silently ignored.
*/
warn("%s[%s]: invalid file type",
continue;
}
return (NULL);
}
/*
* If we're seeing a new Elf object, then we have an
* archive. In this case, we create a new target, and chain it
* off the master target. We can later iterate over these
* targets using dis_tgt_next().
*/
}
warn("%s: failed to get section string table for "
"file", file);
return (NULL);
}
warn("%s: failed to get number of sections in file",
file);
return (NULL);
}
shnum);
idx = 0;
}
/*
* Final sanity check. If we had an archive with no members, then bail
* out with a nice message.
*/
return (NULL);
}
return (tgt);
}
/*
* Return the filename associated with the target.
*/
const char *
{
return (tgt->dt_filename);
}
/*
* Return the archive member name, if any.
*/
const char *
{
else
return (NULL);
}
/*
* Return the Elf_Ehdr associated with this target. Needed to determine which
* disassembler to use.
*/
void
{
}
/*
* Return the next target in the list, if this is an archive.
*/
{
}
/*
* Destroy a target and free up any associated memory.
*/
void
{
}
if (tgt->dt_elf_root)
}
/*
* Given an address, return the section it is in and set the offset within
* the section.
*/
const char *
{
int i;
}
}
*offset = 0;
return (NULL);
}
/*
* Given an address, returns the name of the corresponding symbol, as well as
* the offset within that symbol. If no matching symbol is found, then NULL is
* returned.
*
* If 'cache_result' is specified, then we keep track of the resulting symbol.
* This cached result is consulted first on subsequent lookups in order to avoid
* unecessary lookups. This flag should be used for resolving the current PC,
* as the majority of addresses stay within the current function.
*/
const char *
{
int found;
*offset = 0;
*size = 0;
*isfunc = 0;
STT_FUNC);
}
lo = 0;
found = 0;
found = 1;
/*
* Particularly for .plt objects, it's possible to have
* a zero sized object. We want to return this, but we
* want it to be a last resort.
*/
}
else
}
if (!found) {
if (match)
else
return (NULL);
}
/*
* Walk backwards to find the best match.
*/
do {
break;
if (cache_result)
if (isfunc)
}
/*
* Given an address, return the starting offset of the next symbol in the file.
* Only needed on variable length instruction architectures.
*/
{
sym++;
}
return (0);
}
/*
* Iterate over all sections in the target, executing the given callback for
* each.
*/
void
{
int idx;
warn("%s: failed to get section %d header",
continue;
}
warn("%s: failed to get section %d name",
continue;
}
warn("%s: failed to get data for section '%s'",
continue;
}
/*
* dis_tgt_section_iter is also used before the section map
* is initialized, so only check when we need to. If the
* section map is uninitialized, it will return 0 and have
* no net effect.
*/
}
}
/*
* Return 1 if the given section contains text, 0 otherwise.
*/
int
{
}
/*
* Return a pointer to the section data.
*/
void *
{
}
/*
* Return the size of the section data.
*/
{
}
/*
* Return the address for the given section.
*/
{
}
/*
* Return the name of the current section.
*/
const char *
{
}
/*
* Create an allocated copy of the given section
*/
{
return (new);
}
/*
* Free section memory
*/
void
{
}
/*
* Iterate over all functions in the target, executing the given callback for
* each one.
*/
void
{
int i;
/* ignore non-functions */
continue;
/* get the ELF data associated with this function */
warn("%s: failed to read section %d",
continue;
}
/*
* Verify that the address lies within the section that we think
* it does.
*/
warn("%s: bad section %d for address %p",
continue;
}
}
}
/*
* Return the data associated with a given function.
*/
void *
{
}
/*
* Return the size of a function.
*/
{
}
/*
* Return the address of a function.
*/
{
}
/*
* Return the name of the function
*/
const char *
{
}
/*
* Return a copy of a function.
*/
{
return (new);
}
/*
* Free function memory
*/
void
{
}