• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
remove_filter__anon9c73515b0111::remove_filter44 	remove_filter(string_filter const & filter)
45 		: filter_(filter) {}
46 
operator ()__anon9c73515b0111::remove_filter47 	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 
op_bfd_symbol(asymbol const * a)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 
op_bfd_symbol(bfd_vma vma,size_t size,string const & name)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 
operator <(op_bfd_symbol const & rhs) const89 bool op_bfd_symbol::operator<(op_bfd_symbol const & rhs) const
90 {
91 	return filepos() < rhs.filepos();
92 }
93 
symbol_endpos(void) const94 unsigned long op_bfd_symbol::symbol_endpos(void) const
95 {
96 	return bfd_symbol->section->filepos + bfd_symbol->section->size;
97 }
98 
99 
op_bfd(string const & fname,string_filter const & symbol_filter,extra_images const & extra_images,bool & ok)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 
~op_bfd()190 op_bfd::~op_bfd()
191 {
192 }
193 
194 
get_start_offset(bfd_vma vma) const195 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 
get_symbols(op_bfd::symbols_found_t & symbols)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 
add_symbols(op_bfd::symbols_found_t & symbols,string_filter const & symbol_filter)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 
offset_to_pc(bfd_vma offset) const301 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::
symbol_has_contents(symbol_index_t sym_idx)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::
get_symbol_contents(symbol_index_t sym_index,unsigned char * contents) const327 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 
has_debug_info() const340 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 
get_linenr(symbol_index_t sym_idx,bfd_vma offset,string & source_filename,unsigned int & linenr) const368 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 
symbol_size(op_bfd_symbol const & sym,op_bfd_symbol const * next) const388 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 
get_symbol_range(symbol_index_t sym_idx,unsigned long long & start,unsigned long long & end) const403 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 
get_vma_range(bfd_vma & start,bfd_vma & end) const434 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 
create_artificial_symbol()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 
get_filename() const459 string op_bfd::get_filename() const
460 {
461 	return filename;
462 }
463 
464 
bfd_arch_bits_per_address() const465 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