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