Home | History | Annotate | Download | only in libutil++
      1 /**
      2  * @file op_bfd.cpp
      3  * Encapsulation of bfd objects
      4  *
      5  * @remark Copyright 2002 OProfile authors
      6  * @remark Read the file COPYING
      7  *
      8  * @author Philippe Elie
      9  * @author John Levon
     10  */
     11 
     12 #include "op_file.h"
     13 #include "op_config.h"
     14 #include "config.h"
     15 
     16 #include <fcntl.h>
     17 #include <cstring>
     18 
     19 #include <sys/stat.h>
     20 
     21 #include <cstdlib>
     22 
     23 #include <algorithm>
     24 #include <iostream>
     25 #include <iomanip>
     26 #include <sstream>
     27 
     28 #include "op_bfd.h"
     29 #include "locate_images.h"
     30 #include "string_filter.h"
     31 #include "stream_util.h"
     32 #include "cverb.h"
     33 
     34 using namespace std;
     35 
     36 
     37 verbose vbfd("bfd");
     38 
     39 
     40 namespace {
     41 
     42 /// function object for filtering symbols to remove
     43 struct remove_filter {
     44 	remove_filter(string_filter const & filter)
     45 		: filter_(filter) {}
     46 
     47 	bool operator()(op_bfd_symbol const & symbol) {
     48 		return !filter_.match(symbol.name());
     49 	}
     50 
     51 	string_filter filter_;
     52 };
     53 
     54 
     55 } // namespace anon
     56 
     57 
     58 op_bfd_symbol::op_bfd_symbol(asymbol const * a)
     59 	: bfd_symbol(a), symb_value(a->value),
     60 	  section_filepos(a->section->filepos),
     61 	  section_vma(a->section->vma),
     62 	  symb_size(0), symb_hidden(false), symb_weak(false),
     63 	  symb_artificial(false)
     64 {
     65 	// Some sections have unnamed symbols in them. If
     66 	// we just ignore them then we end up sticking
     67 	// things like .plt hits inside of _init. So instead
     68 	// we name the symbol after the section.
     69 	if (a->name && a->name[0] != '\0') {
     70 		symb_name = a->name;
     71 		symb_weak = a->flags & BSF_WEAK;
     72 		symb_hidden = (a->flags & BSF_LOCAL)
     73  			&& !(a->flags & BSF_GLOBAL);
     74 	} else {
     75 		symb_name = string("??") + a->section->name;
     76 	}
     77 }
     78 
     79 
     80 op_bfd_symbol::op_bfd_symbol(bfd_vma vma, size_t size, string const & name)
     81 	: bfd_symbol(0), symb_value(vma),
     82 	  section_filepos(0), section_vma(0),
     83 	  symb_size(size), symb_name(name),
     84 	  symb_artificial(true)
     85 {
     86 }
     87 
     88 
     89 bool op_bfd_symbol::operator<(op_bfd_symbol const & rhs) const
     90 {
     91 	return filepos() < rhs.filepos();
     92 }
     93 
     94 unsigned long op_bfd_symbol::symbol_endpos(void) const
     95 {
     96 	return bfd_symbol->section->filepos + bfd_symbol->section->size;
     97 }
     98 
     99 
    100 op_bfd::op_bfd(string const & fname, string_filter const & symbol_filter,
    101 	       extra_images const & extra_images, bool & ok)
    102 	:
    103 	filename(fname),
    104 	archive_path(extra_images.get_archive_path()),
    105 	extra_found_images(extra_images),
    106 	file_size(-1),
    107 	anon_obj(false)
    108 {
    109 	int fd;
    110 	struct stat st;
    111 	// after creating all symbol it's convenient for user code to access
    112 	// symbols through a vector. We use an intermediate list to avoid a
    113 	// O(N) behavior when we will filter vector element below
    114 	symbols_found_t symbols;
    115 	asection const * sect;
    116 	string suf = ".jo";
    117 
    118 	image_error img_ok;
    119 	string const image_path =
    120 		extra_images.find_image_path(filename, img_ok, true);
    121 
    122 	cverb << vbfd << "op_bfd ctor for " << image_path << endl;
    123 
    124 	// if there's a problem already, don't try to open it
    125 	if (!ok || img_ok != image_ok) {
    126 		cverb << vbfd << "can't locate " << image_path << endl;
    127 		goto out_fail;
    128 	}
    129 
    130 	fd = open(image_path.c_str(), O_RDONLY);
    131 	if (fd == -1) {
    132 		cverb << vbfd << "open failed for " << image_path << endl;
    133 		ok = false;
    134 		goto out_fail;
    135 	}
    136 
    137 	if (fstat(fd, &st)) {
    138 		cverb << vbfd << "stat failed for " << image_path << endl;
    139 		ok = false;
    140 		goto out_fail;
    141 	}
    142 
    143 	file_size = st.st_size;
    144 
    145 	ibfd.abfd = fdopen_bfd(image_path, fd);
    146 
    147 	if (!ibfd.valid()) {
    148 		cverb << vbfd << "fdopen_bfd failed for " << image_path << endl;
    149 		ok = false;
    150 		goto out_fail;
    151 	}
    152 
    153 	string::size_type pos;
    154 	pos = filename.rfind(suf);
    155 	if (pos != string::npos && pos == filename.size() - suf.size())
    156 		anon_obj = true;
    157 
    158 
    159 	// find .text and use it
    160 	for (sect = ibfd.abfd->sections; sect; sect = sect->next) {
    161 		if (sect->flags & SEC_CODE) {
    162 			if (filepos_map[sect->name] != 0) {
    163 				cerr << "Found section \"" << sect->name
    164 				     << "\" twice for " << get_filename()
    165 				     << endl;
    166 				abort();
    167 			}
    168 
    169 			filepos_map[sect->name] = sect->filepos;
    170 
    171 			if (sect->vma == 0 && strcmp(sect->name, ".text"))
    172 				filtered_section.push_back(sect);
    173 		}
    174 	}
    175 
    176 	get_symbols(symbols);
    177 
    178 out:
    179 	add_symbols(symbols, symbol_filter);
    180 	return;
    181 out_fail:
    182 	ibfd.close();
    183 	dbfd.close();
    184 	// make the fake symbol fit within the fake file
    185 	file_size = -1;
    186 	goto out;
    187 }
    188 
    189 
    190 op_bfd::~op_bfd()
    191 {
    192 }
    193 
    194 
    195 unsigned long op_bfd::get_start_offset(bfd_vma vma) const
    196 {
    197 	if (!vma || !ibfd.valid()) {
    198 		filepos_map_t::const_iterator it = filepos_map.find(".text");
    199 		if (it != filepos_map.end())
    200 			return it->second;
    201 		return 0;
    202 	}
    203 
    204 	return 0;
    205 }
    206 
    207 
    208 void op_bfd::get_symbols(op_bfd::symbols_found_t & symbols)
    209 {
    210 	ibfd.get_symbols();
    211 
    212 	// On separate debug file systems, the main bfd has no symbols,
    213 	// so even for non -g reports, we want to process the dbfd.
    214 	// This hurts us pretty badly (the CRC), but we really don't
    215 	// have much choice at the moment.
    216 	has_debug_info();
    217 
    218 	dbfd.set_image_bfd_info(&ibfd);
    219 	dbfd.get_symbols();
    220 
    221 	size_t i;
    222 	for (i = 0; i < ibfd.nr_syms; ++i) {
    223 		if (!interesting_symbol(ibfd.syms[i]))
    224 			continue;
    225 		if (find(filtered_section.begin(), filtered_section.end(),
    226 			 ibfd.syms[i]->section) != filtered_section.end())
    227 			continue;
    228 		symbols.push_back(op_bfd_symbol(ibfd.syms[i]));
    229 	}
    230 
    231 	for (i = 0; i < dbfd.nr_syms; ++i) {
    232 		if (!interesting_symbol(dbfd.syms[i]))
    233 			continue;
    234 
    235 		// need to use filepos of original file's section for
    236 		// debug file symbols. We probably need to be more
    237 		// careful for special symbols which have ->section from
    238 		// .rodata like *ABS*
    239 		u32 filepos = filepos_map[dbfd.syms[i]->section->name];
    240 		if (filepos != 0)
    241 			dbfd.syms[i]->section->filepos = filepos;
    242 		symbols.push_back(op_bfd_symbol(dbfd.syms[i]));
    243 	}
    244 
    245 	symbols.sort();
    246 
    247 	symbols_found_t::iterator it = symbols.begin();
    248 
    249 	// we need to ensure than for a given vma only one symbol exist else
    250 	// we read more than one time some samples. Fix #526098
    251 	while (it != symbols.end()) {
    252 		symbols_found_t::iterator temp = it;
    253 		++temp;
    254 		if (temp != symbols.end() && (it->vma() == temp->vma()) &&
    255 			(it->filepos() == temp->filepos())) {
    256 			if (boring_symbol(*it, *temp)) {
    257 				it = symbols.erase(it);
    258 			} else {
    259 				symbols.erase(temp);
    260 			}
    261 		} else {
    262 			++it;
    263 		}
    264 	}
    265 
    266 	// now we can calculate the symbol size, we can't first include/exclude
    267 	// symbols because the size of symbol is calculated from the difference
    268 	// between the vma of a symbol and the next one.
    269 	for (it = symbols.begin() ; it != symbols.end(); ++it) {
    270 		op_bfd_symbol const * next = 0;
    271 		symbols_found_t::iterator temp = it;
    272 		++temp;
    273 		if (temp != symbols.end())
    274 			next = &*temp;
    275 		it->size(symbol_size(*it, next));
    276 	}
    277 }
    278 
    279 
    280 void op_bfd::add_symbols(op_bfd::symbols_found_t & symbols,
    281                          string_filter const & symbol_filter)
    282 {
    283 	// images with no symbols debug info available get a placeholder symbol
    284 	if (symbols.empty())
    285 		symbols.push_back(create_artificial_symbol());
    286 
    287 	cverb << vbfd << "number of symbols before filtering "
    288 	      << dec << symbols.size() << hex << endl;
    289 
    290 	symbols_found_t::iterator it;
    291 	it = remove_if(symbols.begin(), symbols.end(),
    292 	               remove_filter(symbol_filter));
    293 
    294 	copy(symbols.begin(), it, back_inserter(syms));
    295 
    296 	cverb << vbfd << "number of symbols now "
    297 	      << dec << syms.size() << hex << endl;
    298 }
    299 
    300 
    301 bfd_vma op_bfd::offset_to_pc(bfd_vma offset) const
    302 {
    303 	asection const * sect = ibfd.abfd->sections;
    304 
    305 	for (; sect; sect = sect->next) {
    306 		if (offset >= bfd_vma(sect->filepos) &&
    307 		    (!sect->next || offset < bfd_vma(sect->next->filepos))) {
    308 			return sect->vma + (offset - sect->filepos);
    309 		}
    310 	}
    311 
    312 	return 0;
    313 }
    314 
    315 bool op_bfd::
    316 symbol_has_contents(symbol_index_t sym_idx)
    317 {
    318 	op_bfd_symbol const & bfd_sym = syms[sym_idx];
    319 	string const name = bfd_sym.name();
    320 	if (name.size() == 0 || bfd_sym.artificial() || !ibfd.valid())
    321 		return false;
    322 	else
    323 		return true;
    324 }
    325 
    326 bool op_bfd::
    327 get_symbol_contents(symbol_index_t sym_index, unsigned char * contents) const
    328 {
    329 	op_bfd_symbol const & bfd_sym = syms[sym_index];
    330 	size_t size = bfd_sym.size();
    331 
    332 	if (!bfd_get_section_contents(ibfd.abfd, bfd_sym.symbol()->section,
    333 				 contents,
    334 				 static_cast<file_ptr>(bfd_sym.value()), size)) {
    335 		return false;
    336 	}
    337 	return true;
    338 }
    339 
    340 bool op_bfd::has_debug_info() const
    341 {
    342 	if (debug_info.cached())
    343 		return debug_info.get();
    344 
    345 	if (!ibfd.valid())
    346 		return debug_info.reset(false);
    347 
    348 	if (ibfd.has_debug_info())
    349 		return debug_info.reset(true);
    350 
    351 	// check to see if there is an .debug file
    352 
    353 	if (find_separate_debug_file(ibfd.abfd, filename, debug_filename, extra_found_images)) {
    354 		cverb << vbfd << "now loading: " << debug_filename << endl;
    355 		dbfd.abfd = open_bfd(debug_filename);
    356 		if (dbfd.has_debug_info())
    357 			return debug_info.reset(true);
    358 	}
    359 
    360 	// .debug is optional, so will not fail if there's a problem
    361 	cverb << vbfd << "failed to process separate debug file "
    362 	      << debug_filename << endl;
    363 
    364 	return debug_info.reset(false);
    365 }
    366 
    367 
    368 bool op_bfd::get_linenr(symbol_index_t sym_idx, bfd_vma offset,
    369 			string & source_filename, unsigned int & linenr) const
    370 {
    371 	if (!has_debug_info())
    372 		return false;
    373 
    374 	bfd_info const & b = dbfd.valid() ? dbfd : ibfd;
    375 	op_bfd_symbol const & sym = syms[sym_idx];
    376 
    377 	linenr_info const info = find_nearest_line(b, sym, offset, anon_obj);
    378 
    379 	if (!info.found)
    380 		return false;
    381 
    382 	source_filename = info.filename;
    383 	linenr = info.line;
    384 	return true;
    385 }
    386 
    387 
    388 size_t op_bfd::symbol_size(op_bfd_symbol const & sym,
    389 			   op_bfd_symbol const * next) const
    390 {
    391 	unsigned long long start = sym.filepos();
    392 	unsigned long long end;
    393 
    394 	if (next && (sym.section() != next->section()))
    395 		end = sym.symbol_endpos();
    396 	else
    397 		end = next ? next->filepos() : file_size;
    398 
    399 	return end - start;
    400 }
    401 
    402 
    403 void op_bfd::get_symbol_range(symbol_index_t sym_idx,
    404 			      unsigned long long & start, unsigned long long & end) const
    405 {
    406 	op_bfd_symbol const & sym = syms[sym_idx];
    407 
    408 	bool const verbose = cverb << (vbfd & vlevel1);
    409 
    410 	if (anon_obj)
    411 		start = sym.vma();
    412 	else
    413 		start = sym.filepos();
    414 	end = start + sym.size();
    415 
    416 	if (!verbose)
    417 		return;
    418 
    419 	io_state state(cverb << (vbfd & vlevel1));
    420 
    421 	cverb << (vbfd & vlevel1) << "symbol " << sym.name()
    422 	      << ", value " << hex << sym.value() << endl;
    423 	cverb << (vbfd & vlevel1)
    424 	      << "start " << hex << start << ", end " << end << endl;
    425 
    426 	if (sym.symbol()) {
    427 		cverb << (vbfd & vlevel1) << "in section "
    428 		      << sym.symbol()->section->name << ", filepos "
    429 		      << hex << sym.symbol()->section->filepos << endl;
    430 	}
    431 }
    432 
    433 
    434 void op_bfd::get_vma_range(bfd_vma & start, bfd_vma & end) const
    435 {
    436 	if (!syms.empty()) {
    437 		// syms are sorted by vma so vma of the first symbol and vma +
    438 		// size of the last symbol give the vma range for gprof output
    439 		op_bfd_symbol const & last_symb = syms[syms.size() - 1];
    440 		start = syms[0].vma();
    441 		// end is excluded from range so + 1 *if* last_symb.size() != 0
    442 		end = last_symb.vma() + last_symb.size() + (last_symb.size() != 0);
    443 	} else {
    444 		start = 0;
    445 		end = file_size;
    446 	}
    447 }
    448 
    449 
    450 op_bfd_symbol const op_bfd::create_artificial_symbol()
    451 {
    452 
    453 	bfd_vma start, end;
    454 	get_vma_range(start, end);
    455 	return op_bfd_symbol(start, end - start, get_filename());
    456 }
    457 
    458 
    459 string op_bfd::get_filename() const
    460 {
    461 	return filename;
    462 }
    463 
    464 
    465 size_t op_bfd::bfd_arch_bits_per_address() const
    466 {
    467 	if (ibfd.valid())
    468 		return ::bfd_arch_bits_per_address(ibfd.abfd);
    469 	// FIXME: this function should be called only if the underlined ibfd
    470 	// is ok, must we throw ?
    471 	return sizeof(bfd_vma);
    472 }
    473