blob: d03a99ddf93bd3dade0c5b610b2539aa55b1c235 [file] [log] [blame] [edit]
/**
* @file op_bfd.cpp
* Encapsulation of bfd objects
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author Philippe Elie
* @author John Levon
*/
#include "op_file.h"
#include "op_config.h"
#include "config.h"
#include <fcntl.h>
#include <cstring>
#include <sys/stat.h>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "op_bfd.h"
#include "locate_images.h"
#include "string_filter.h"
#include "stream_util.h"
#include "cverb.h"
using namespace std;
verbose vbfd("bfd");
namespace {
/// function object for filtering symbols to remove
struct remove_filter {
remove_filter(string_filter const & filter)
: filter_(filter) {}
bool operator()(op_bfd_symbol const & symbol) {
return !filter_.match(symbol.name());
}
string_filter filter_;
};
} // namespace anon
op_bfd_symbol::op_bfd_symbol(asymbol const * a)
: bfd_symbol(a), symb_value(a->value),
section_filepos(a->section->filepos),
section_vma(a->section->vma),
symb_size(0), symb_hidden(false), symb_weak(false),
symb_artificial(false)
{
// Some sections have unnamed symbols in them. If
// we just ignore them then we end up sticking
// things like .plt hits inside of _init. So instead
// we name the symbol after the section.
if (a->name && a->name[0] != '\0') {
symb_name = a->name;
symb_weak = a->flags & BSF_WEAK;
symb_hidden = (a->flags & BSF_LOCAL)
&& !(a->flags & BSF_GLOBAL);
} else {
symb_name = string("??") + a->section->name;
}
}
op_bfd_symbol::op_bfd_symbol(bfd_vma vma, size_t size, string const & name)
: bfd_symbol(0), symb_value(vma),
section_filepos(0), section_vma(0),
symb_size(size), symb_name(name),
symb_artificial(true)
{
}
bool op_bfd_symbol::operator<(op_bfd_symbol const & rhs) const
{
return filepos() < rhs.filepos();
}
unsigned long op_bfd_symbol::symbol_endpos(void) const
{
return bfd_symbol->section->filepos + bfd_symbol->section->size;
}
op_bfd::op_bfd(string const & fname, string_filter const & symbol_filter,
extra_images const & extra_images, bool & ok)
:
filename(fname),
archive_path(extra_images.get_archive_path()),
extra_found_images(extra_images),
file_size(-1),
anon_obj(false)
{
int fd;
struct stat st;
// after creating all symbol it's convenient for user code to access
// symbols through a vector. We use an intermediate list to avoid a
// O(N²) behavior when we will filter vector element below
symbols_found_t symbols;
asection const * sect;
string suf = ".jo";
image_error img_ok;
string const image_path =
extra_images.find_image_path(filename, img_ok, true);
cverb << vbfd << "op_bfd ctor for " << image_path << endl;
// if there's a problem already, don't try to open it
if (!ok || img_ok != image_ok) {
cverb << vbfd << "can't locate " << image_path << endl;
goto out_fail;
}
fd = open(image_path.c_str(), O_RDONLY);
if (fd == -1) {
cverb << vbfd << "open failed for " << image_path << endl;
ok = false;
goto out_fail;
}
if (fstat(fd, &st)) {
cverb << vbfd << "stat failed for " << image_path << endl;
ok = false;
goto out_fail;
}
file_size = st.st_size;
ibfd.abfd = fdopen_bfd(image_path, fd);
if (!ibfd.valid()) {
cverb << vbfd << "fdopen_bfd failed for " << image_path << endl;
ok = false;
goto out_fail;
}
string::size_type pos;
pos = filename.rfind(suf);
if (pos != string::npos && pos == filename.size() - suf.size())
anon_obj = true;
// find .text and use it
for (sect = ibfd.abfd->sections; sect; sect = sect->next) {
if (sect->flags & SEC_CODE) {
if (filepos_map[sect->name] != 0) {
cerr << "Found section \"" << sect->name
<< "\" twice for " << get_filename()
<< endl;
abort();
}
filepos_map[sect->name] = sect->filepos;
if (sect->vma == 0 && strcmp(sect->name, ".text"))
filtered_section.push_back(sect);
}
}
get_symbols(symbols);
out:
add_symbols(symbols, symbol_filter);
return;
out_fail:
ibfd.close();
dbfd.close();
// make the fake symbol fit within the fake file
file_size = -1;
goto out;
}
op_bfd::~op_bfd()
{
}
unsigned long op_bfd::get_start_offset(bfd_vma vma) const
{
if (!vma || !ibfd.valid()) {
filepos_map_t::const_iterator it = filepos_map.find(".text");
if (it != filepos_map.end())
return it->second;
return 0;
}
return 0;
}
void op_bfd::get_symbols(op_bfd::symbols_found_t & symbols)
{
ibfd.get_symbols();
// On separate debug file systems, the main bfd has no symbols,
// so even for non -g reports, we want to process the dbfd.
// This hurts us pretty badly (the CRC), but we really don't
// have much choice at the moment.
has_debug_info();
dbfd.set_image_bfd_info(&ibfd);
dbfd.get_symbols();
size_t i;
for (i = 0; i < ibfd.nr_syms; ++i) {
if (!interesting_symbol(ibfd.syms[i]))
continue;
if (find(filtered_section.begin(), filtered_section.end(),
ibfd.syms[i]->section) != filtered_section.end())
continue;
symbols.push_back(op_bfd_symbol(ibfd.syms[i]));
}
for (i = 0; i < dbfd.nr_syms; ++i) {
if (!interesting_symbol(dbfd.syms[i]))
continue;
// need to use filepos of original file's section for
// debug file symbols. We probably need to be more
// careful for special symbols which have ->section from
// .rodata like *ABS*
u32 filepos = filepos_map[dbfd.syms[i]->section->name];
if (filepos != 0)
dbfd.syms[i]->section->filepos = filepos;
symbols.push_back(op_bfd_symbol(dbfd.syms[i]));
}
symbols.sort();
symbols_found_t::iterator it = symbols.begin();
// we need to ensure than for a given vma only one symbol exist else
// we read more than one time some samples. Fix #526098
while (it != symbols.end()) {
symbols_found_t::iterator temp = it;
++temp;
if (temp != symbols.end() && (it->vma() == temp->vma()) &&
(it->filepos() == temp->filepos())) {
if (boring_symbol(*it, *temp)) {
it = symbols.erase(it);
} else {
symbols.erase(temp);
}
} else {
++it;
}
}
// now we can calculate the symbol size, we can't first include/exclude
// symbols because the size of symbol is calculated from the difference
// between the vma of a symbol and the next one.
for (it = symbols.begin() ; it != symbols.end(); ++it) {
op_bfd_symbol const * next = 0;
symbols_found_t::iterator temp = it;
++temp;
if (temp != symbols.end())
next = &*temp;
it->size(symbol_size(*it, next));
}
}
void op_bfd::add_symbols(op_bfd::symbols_found_t & symbols,
string_filter const & symbol_filter)
{
// images with no symbols debug info available get a placeholder symbol
if (symbols.empty())
symbols.push_back(create_artificial_symbol());
cverb << vbfd << "number of symbols before filtering "
<< dec << symbols.size() << hex << endl;
symbols_found_t::iterator it;
it = remove_if(symbols.begin(), symbols.end(),
remove_filter(symbol_filter));
copy(symbols.begin(), it, back_inserter(syms));
cverb << vbfd << "number of symbols now "
<< dec << syms.size() << hex << endl;
}
bfd_vma op_bfd::offset_to_pc(bfd_vma offset) const
{
asection const * sect = ibfd.abfd->sections;
for (; sect; sect = sect->next) {
if (offset >= bfd_vma(sect->filepos) &&
(!sect->next || offset < bfd_vma(sect->next->filepos))) {
return sect->vma + (offset - sect->filepos);
}
}
return 0;
}
bool op_bfd::
symbol_has_contents(symbol_index_t sym_idx)
{
op_bfd_symbol const & bfd_sym = syms[sym_idx];
string const name = bfd_sym.name();
if (name.size() == 0 || bfd_sym.artificial() || !ibfd.valid())
return false;
else
return true;
}
bool op_bfd::
get_symbol_contents(symbol_index_t sym_index, unsigned char * contents) const
{
op_bfd_symbol const & bfd_sym = syms[sym_index];
size_t size = bfd_sym.size();
if (!bfd_get_section_contents(ibfd.abfd, bfd_sym.symbol()->section,
contents,
static_cast<file_ptr>(bfd_sym.value()), size)) {
return false;
}
return true;
}
bool op_bfd::has_debug_info() const
{
if (debug_info.cached())
return debug_info.get();
if (!ibfd.valid())
return debug_info.reset(false);
if (ibfd.has_debug_info())
return debug_info.reset(true);
// check to see if there is an .debug file
if (find_separate_debug_file(ibfd.abfd, filename, debug_filename, extra_found_images)) {
cverb << vbfd << "now loading: " << debug_filename << endl;
dbfd.abfd = open_bfd(debug_filename);
if (dbfd.has_debug_info())
return debug_info.reset(true);
}
// .debug is optional, so will not fail if there's a problem
cverb << vbfd << "failed to process separate debug file "
<< debug_filename << endl;
return debug_info.reset(false);
}
bool op_bfd::get_linenr(symbol_index_t sym_idx, bfd_vma offset,
string & source_filename, unsigned int & linenr) const
{
if (!has_debug_info())
return false;
bfd_info const & b = dbfd.valid() ? dbfd : ibfd;
op_bfd_symbol const & sym = syms[sym_idx];
linenr_info const info = find_nearest_line(b, sym, offset, anon_obj);
if (!info.found)
return false;
source_filename = info.filename;
linenr = info.line;
return true;
}
size_t op_bfd::symbol_size(op_bfd_symbol const & sym,
op_bfd_symbol const * next) const
{
unsigned long long start = sym.filepos();
unsigned long long end;
if (next && (sym.section() != next->section()))
end = sym.symbol_endpos();
else
end = next ? next->filepos() : file_size;
if (start > end)
return 0;
return end - start;
}
void op_bfd::get_symbol_range(symbol_index_t sym_idx,
unsigned long long & start, unsigned long long & end) const
{
op_bfd_symbol const & sym = syms[sym_idx];
bool const verbose = cverb << (vbfd & vlevel1);
if (anon_obj)
start = sym.vma();
else
start = sym.filepos();
end = start + sym.size();
if (!verbose)
return;
io_state state(cverb << (vbfd & vlevel1));
cverb << (vbfd & vlevel1) << "symbol " << sym.name()
<< ", value " << hex << sym.value() << endl;
cverb << (vbfd & vlevel1)
<< "start " << hex << start << ", end " << end << endl;
if (sym.symbol()) {
cverb << (vbfd & vlevel1) << "in section "
<< sym.symbol()->section->name << ", filepos "
<< hex << sym.symbol()->section->filepos << endl;
}
}
void op_bfd::get_vma_range(bfd_vma & start, bfd_vma & end) const
{
if (!syms.empty()) {
// syms are sorted by vma so vma of the first symbol and vma +
// size of the last symbol give the vma range for gprof output
op_bfd_symbol const & last_symb = syms[syms.size() - 1];
start = syms[0].vma();
// end is excluded from range so + 1 *if* last_symb.size() != 0
end = last_symb.vma() + last_symb.size() + (last_symb.size() != 0);
} else {
start = 0;
end = file_size;
}
}
op_bfd_symbol const op_bfd::create_artificial_symbol()
{
bfd_vma start, end;
get_vma_range(start, end);
return op_bfd_symbol(start, end - start, get_filename());
}
string op_bfd::get_filename() const
{
return filename;
}
size_t op_bfd::bfd_arch_bits_per_address() const
{
if (ibfd.valid())
return ::bfd_arch_bits_per_address(ibfd.abfd);
// FIXME: this function should be called only if the underlined ibfd
// is ok, must we throw ?
return sizeof(bfd_vma);
}