Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame] | 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 | } |