blob: 3199e92c83adb38df0a38e8280525c10e7f9b3fa [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2001-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2001 Hewlett-Packard Company */
/* support for looking up function containing a pc
* for pc-based profiling
* based on code from Giuseppe Desoli
* modified by bruening, Jan 2001
* need to add to build command "-lbfd -liberty"
* FIXME: assumes ELF executable compiled w/ -static
*/
#include <string.h>
#include <errno.h>
#include <bfd.h>
/* globals.h gives us stdio, stdlib, and assert */
#include "../globals.h"
static uint bfd_symcount, nonnull_symcount;
static asymbol **bfd_syms = NULL;
static bfd *infile = (bfd *) NULL;
static int
compare_symbols (const void *ap, const void *bp)
{
const asymbol *a = *(const asymbol **) ap;
const asymbol *b = *(const asymbol **) bp;
/* check for null pointers -- these are discarded syms */
/* they end up at the end of the sort */
if (a == NULL)
return 1;
if (b == NULL)
return -1;
if (bfd_asymbol_value(a) > bfd_asymbol_value(b))
return 1;
if (bfd_asymbol_value(a) < bfd_asymbol_value(b))
return -1;
return strcmp (a->name, b->name);
}
/* sort the symbol table by section and by offset from the start of the section */
static void
sort_symtab()
{
long i;
qsort (bfd_syms, bfd_symcount, sizeof (asymbol *), compare_symbols);
if (stats->loglevel > 2) {
LOG(GLOBAL, LOG_ALL, 3, "\n\nSYMBOL TABLE\n");
for (i = 0; i < bfd_symcount; i++) {
if (bfd_syms[i]) {
LOG(GLOBAL, LOG_ALL, 3, "[%5d] "PFX" 0x%x %5s %s\n", i,
bfd_asymbol_value(bfd_syms[i]),
bfd_syms[i]->flags,
bfd_syms[i]->section->name,
bfd_syms[i]->name);
} else {
LOG(GLOBAL, LOG_ALL, 3, "(null symbol)\n");
}
}
LOG(GLOBAL, LOG_ALL, 3, "\n\n");
}
}
int
lookup_symbol_address(ptr_uint_t addr)
{
uint low_idx, high_idx, middle_idx;
uint middle_addr;
/* the idea is to use a binary search on the sorted symbols table */
/* the table is sorted by sections and addresses */
low_idx = 0;
high_idx = nonnull_symcount;
/* first try to find the two symbols containing it */
for (;;) {
middle_idx = low_idx + ((high_idx - low_idx) >> 1);
if (middle_idx == low_idx)
break;
/* we need real addresses so use bfd macros for the value here */
/* basically it's the symbols' value + their sections' vma */
middle_addr = bfd_asymbol_value (bfd_syms[middle_idx]);
if (middle_addr > addr)
high_idx = middle_idx;
else if (middle_addr < addr)
low_idx = middle_idx;
else
break;
}
return middle_idx;
}
/* sets to null uninteresting symbols, sets nonnull_symcount to the number of
* nonnull symbols left
*/
void
prepare_symtab()
{
int i;
nonnull_symcount = bfd_symcount;
for (i = 0; i < bfd_symcount; i++) {
if (bfd_syms[i]) {
/* FIXME: the BSF_FUNCTION flag is only for ELF
* other ideas: remove all non-text-section symbols
*/
if (! (bfd_syms[i]->flags & BSF_FUNCTION) ) {
/* remove from table by marking as null */
bfd_syms[i] = NULL;
nonnull_symcount--;
}
}
}
}
static asymbol **
get_symtab(bfd *abfd)
{
asymbol **sy = (asymbol **) NULL;
long storage;
if (!(bfd_get_file_flags (abfd) & HAS_SYMS)) {
print_file(STDERR, "No symbols in \"%s\".\n", bfd_get_filename (abfd));
bfd_symcount = 0;
return NULL;
}
storage = bfd_get_symtab_upper_bound (abfd);
if (storage < 0) {
print_file(STDERR, "BFD fatal error bfd_get_symtab_upper_bound\n");
return NULL;
}
if (storage) {
sy = (asymbol **) xmalloc (storage);
}
bfd_symcount = bfd_canonicalize_symtab (abfd, sy);
if (bfd_symcount < 0) {
print_file(STDERR, "BFD fatal error bfd_canonicalize_symtab\n");
return NULL;
}
if (bfd_symcount == 0)
print_file(STDERR, "%s: No symbols\n", bfd_get_filename (abfd));
return sy;
}
/* this is currently called by pcprofile_init(), so nobody else can use
* symtab stuff until that is changed
*/
bool
symtab_init()
{
char *filein = dynamo_options.profexecname;
if (filein == NULL) {
return false;
}
infile = bfd_openr(filein, NULL);
if (infile == NULL) {
USAGE_ERROR("can't find file \"%s\" named in -profexecname", filein);
return false;
}
/* check the bfd format, this is required */
if (!bfd_check_format(infile, bfd_object)) {
bfd_close(infile);
USAGE_ERROR("-profexecname \"%s\" : not a bfd_object!", filein);
return false;
}
if ((bfd_syms = get_symtab(infile)) == NULL) {
if (bfd_syms) {
free(bfd_syms);
}
if (infile)
bfd_close(infile);
USAGE_ERROR("-profexecname \"%s\" : error getting symbol table",
filein);
return false;
}
prepare_symtab();
sort_symtab();
LOG(GLOBAL, LOG_ALL, 1, "SYMBOL TABLE FOR %s SUCCESSFULLY LOADED\n\n", filein);
return true;
}
void
symtab_exit()
{
bfd_close(infile);
}
const char *
symtab_lookup_pc(void * pc)
{
int idx = lookup_symbol_address((ptr_uint_t)pc);
if (bfd_syms[idx] == NULL)
return "(null)";
else {
/* FIXME: try to find line number? */
return bfd_syms[idx]->name;
}
}