blob: f1bef781c31b94cac385a4962b30104ae9253eaf [file] [log] [blame]
/**
* @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 <sys/stat.h>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "op_bfd.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)
{
// 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)
{
}
bool op_bfd_symbol::operator<(op_bfd_symbol const & rhs) const
{
return filepos() < rhs.filepos();
}
op_bfd::op_bfd(string const & archive, string const & fname,
string_filter const & symbol_filter, bool & ok)
:
filename(fname),
archive_path(archive),
file_size(-1)
{
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 const image_path = archive_path + filename;
cverb << vbfd << "op_bfd ctor for " << image_path << endl;
// if there's a problem already, don't try to open it
if (!ok)
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;
}
// 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;
}
}
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 const 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;
}
for (asection * sect = ibfd.abfd->sections; sect; sect = sect->next) {
if (sect->vma == vma)
return sect->filepos;
}
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.get_symbols();
size_t i;
for (i = 0; i < ibfd.nr_syms; ++i) {
if (interesting_symbol(ibfd.syms[i]))
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
for (; it != symbols.end();) {
symbols_found_t::iterator temp = it;
++temp;
if (temp != symbols.end() && (it->vma() == temp->vma())) {
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;
}
unsigned long op_bfd::sym_offset(symbol_index_t sym_index, u32 num) const
{
/* take off section offset and symb value */
return num - syms[sym_index].filepos();
}
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::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
string const global(archive_path + DEBUGDIR);
string const image_path = archive_path + filename;
string const dirname(image_path.substr(0, image_path.rfind('/')));
if (find_separate_debug_file(ibfd.abfd, dirname, global, debug_filename)) {
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, unsigned int offset,
string & source_filename, unsigned int & linenr) const
{
if (!has_debug_info())
return false;
// FIXME: to test, I'm unsure if from this point we must use abfd
// or the check if (pc >= bfd_section_size(abfd, section)) must be done
// with ibfd.
bfd_info const & b = dbfd.valid() ? dbfd : ibfd;
linenr_info const info = find_nearest_line(b, syms[sym_idx], offset);
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 start = sym.filepos();
unsigned long end = next ? next->filepos() : file_size;
return end - start;
}
void op_bfd::get_symbol_range(symbol_index_t sym_idx,
unsigned long & start, unsigned long & end) const
{
op_bfd_symbol const & sym = syms[sym_idx];
bool const verbose = cverb << (vbfd & vlevel1);
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()
{
// FIXME: prefer a bool artificial; to this ??
string symname = "?";
symname += get_filename();
bfd_vma start, end;
get_vma_range(start, end);
return op_bfd_symbol(start, end - start, symname);
}
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);
}