op_bfd.cpp revision 8cfa702f803c5ef6a2b062a489a1b2cf66b45b5e
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
34using namespace std;
35
36
37verbose vbfd("bfd");
38
39
40namespace {
41
42/// function object for filtering symbols to remove
43struct 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
58op_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
80op_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
89bool op_bfd_symbol::operator<(op_bfd_symbol const & rhs) const
90{
91	return filepos() < rhs.filepos();
92}
93
94unsigned long op_bfd_symbol::symbol_endpos(void) const
95{
96	return bfd_symbol->section->filepos + bfd_symbol->section->size;
97}
98
99
100op_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
178out:
179	add_symbols(symbols, symbol_filter);
180	return;
181out_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
190op_bfd::~op_bfd()
191{
192}
193
194
195unsigned 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
208void 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
280void 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
301bfd_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
315bool op_bfd::
316symbol_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
326bool op_bfd::
327get_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
340bool 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
368bool 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
388size_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
403void 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
434void 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
450op_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
459string op_bfd::get_filename() const
460{
461	return filename;
462}
463
464
465size_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