| /** |
| * @file bfd_support.cpp |
| * BFD muck we have to deal with. |
| * |
| * @remark Copyright 2005 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author John Levon |
| */ |
| |
| #include "bfd_support.h" |
| |
| #include "op_bfd.h" |
| #include "op_fileio.h" |
| #include "op_config.h" |
| #include "string_manip.h" |
| #include "file_manip.h" |
| #include "cverb.h" |
| #include "locate_images.h" |
| #include "op_libiberty.h" |
| #include "op_exception.h" |
| |
| #include <unistd.h> |
| #include <errno.h> |
| #include <elf.h> |
| #include <cstdlib> |
| #include <cstring> |
| #include <cassert> |
| #include <iostream> |
| #include <sstream> |
| #include <fstream> |
| #include <sstream> |
| #include <string> |
| #include <cstring> |
| #include <cstdlib> |
| |
| using namespace std; |
| |
| extern verbose vbfd; |
| |
| namespace { |
| |
| #ifndef NT_GNU_BUILD_ID |
| #define NT_GNU_BUILD_ID 3 |
| #endif |
| static size_t build_id_size; |
| |
| |
| void check_format(string const & file, bfd ** ibfd) |
| { |
| if (!bfd_check_format_matches(*ibfd, bfd_object, NULL)) { |
| cverb << vbfd << "BFD format failure for " << file << endl; |
| bfd_close(*ibfd); |
| *ibfd = NULL; |
| } |
| } |
| |
| |
| bool separate_debug_file_exists(string & name, unsigned long const crc, |
| extra_images const & extra) |
| { |
| unsigned long file_crc = 0; |
| // The size of 2 * 1024 elements for the buffer is arbitrary. |
| char buffer[2 * 1024]; |
| |
| image_error img_ok; |
| string const image_path = extra.find_image_path(name, img_ok, true); |
| |
| if (img_ok != image_ok) |
| return false; |
| |
| name = image_path; |
| |
| ifstream file(image_path.c_str()); |
| if (!file) |
| return false; |
| |
| cverb << vbfd << "found " << name; |
| while (file) { |
| file.read(buffer, sizeof(buffer)); |
| file_crc = calc_crc32(file_crc, |
| reinterpret_cast<unsigned char *>(&buffer[0]), |
| file.gcount()); |
| } |
| ostringstream message; |
| message << " with crc32 = " << hex << file_crc << endl; |
| cverb << vbfd << message.str(); |
| return crc == file_crc; |
| } |
| |
| static bool find_debuginfo_file_by_buildid(unsigned char * buildid, string & debug_filename) |
| { |
| size_t build_id_fname_size = strlen (DEBUGDIR) + (sizeof "/.build-id/" - 1) + 1 |
| + (2 * build_id_size) + (sizeof ".debug" - 1) + 1; |
| char * buildid_symlink = (char *) xmalloc(build_id_fname_size); |
| char * sptr = buildid_symlink; |
| unsigned char * bptr = buildid; |
| bool retval = false; |
| size_t build_id_segment_len = strlen("/.build-id/"); |
| |
| |
| memcpy(sptr, DEBUGDIR, strlen(DEBUGDIR)); |
| sptr += strlen(DEBUGDIR); |
| memcpy(sptr, "/.build-id/", build_id_segment_len); |
| sptr += build_id_segment_len; |
| sptr += sprintf(sptr, "%02x", (unsigned) *bptr++); |
| *sptr++ = '/'; |
| for (int i = build_id_size - 1; i > 0; i--) |
| sptr += sprintf(sptr, "%02x", (unsigned) *bptr++); |
| |
| strcpy(sptr, ".debug"); |
| |
| if (access (buildid_symlink, F_OK) == 0) { |
| debug_filename = op_realpath (buildid_symlink); |
| if (debug_filename.compare(buildid_symlink)) { |
| retval = true; |
| cverb << vbfd << "Using build-id symlink" << endl; |
| } |
| } |
| free(buildid_symlink); |
| if (!retval) |
| cverb << vbfd << "build-id file not found; falling back to CRC method." << endl; |
| |
| return retval; |
| } |
| |
| static bool get_build_id(bfd * ibfd, unsigned char * build_id) |
| { |
| Elf32_Nhdr op_note_hdr; |
| asection * sect; |
| char * ptr; |
| bool retval = false; |
| |
| cverb << vbfd << "fetching build-id from runtime binary ..."; |
| if (!(sect = bfd_get_section_by_name(ibfd, ".note.gnu.build-id"))) { |
| if (!(sect = bfd_get_section_by_name(ibfd, ".notes"))) { |
| cverb << vbfd << " No build-id section found" << endl; |
| return false; |
| } |
| } |
| |
| bfd_size_type buildid_sect_size = bfd_section_size(ibfd, sect); |
| char * contents = (char *) xmalloc(buildid_sect_size); |
| errno = 0; |
| if (!bfd_get_section_contents(ibfd, sect, |
| reinterpret_cast<unsigned char *>(contents), |
| static_cast<file_ptr>(0), buildid_sect_size)) { |
| string msg = "bfd_get_section_contents:get_build_id"; |
| if (errno) { |
| msg += ": "; |
| msg += strerror(errno); |
| } |
| throw op_fatal_error(msg); |
| } |
| |
| ptr = contents; |
| while (ptr < (contents + buildid_sect_size)) { |
| op_note_hdr.n_namesz = bfd_get_32(ibfd, |
| reinterpret_cast<bfd_byte *>(contents)); |
| op_note_hdr.n_descsz = bfd_get_32(ibfd, |
| reinterpret_cast<bfd_byte *>(contents + 4)); |
| op_note_hdr.n_type = bfd_get_32(ibfd, |
| reinterpret_cast<bfd_byte *>(contents + 8)); |
| ptr += sizeof(op_note_hdr); |
| if ((op_note_hdr.n_type == NT_GNU_BUILD_ID) && |
| (op_note_hdr.n_namesz == sizeof("GNU")) && |
| (strcmp("GNU", ptr ) == 0)) { |
| build_id_size = op_note_hdr.n_descsz; |
| memcpy(build_id, ptr + op_note_hdr.n_namesz, build_id_size); |
| retval = true; |
| cverb << vbfd << "Found build-id" << endl; |
| break; |
| } |
| ptr += op_note_hdr.n_namesz + op_note_hdr.n_descsz; |
| } |
| if (!retval) |
| cverb << vbfd << " No build-id found" << endl; |
| free(contents); |
| |
| return retval; |
| } |
| |
| bool get_debug_link_info(bfd * ibfd, string & filename, unsigned long & crc32) |
| { |
| asection * sect; |
| |
| cverb << vbfd << "fetching .gnu_debuglink section" << endl; |
| sect = bfd_get_section_by_name(ibfd, ".gnu_debuglink"); |
| |
| if (sect == NULL) |
| return false; |
| |
| bfd_size_type debuglink_size = bfd_section_size(ibfd, sect); |
| char * contents = (char *) xmalloc(debuglink_size); |
| cverb << vbfd |
| << ".gnu_debuglink section has size " << debuglink_size << endl; |
| |
| if (!bfd_get_section_contents(ibfd, sect, |
| reinterpret_cast<unsigned char *>(contents), |
| static_cast<file_ptr>(0), debuglink_size)) { |
| string msg = "bfd_get_section_contents:get_debug"; |
| if (errno) { |
| msg += ": "; |
| msg += strerror(errno); |
| } |
| throw op_fatal_error(msg); |
| } |
| |
| /* CRC value is stored after the filename, aligned up to 4 bytes. */ |
| size_t filename_len = strlen(contents); |
| size_t crc_offset = filename_len + 1; |
| crc_offset = (crc_offset + 3) & ~3; |
| |
| crc32 = bfd_get_32(ibfd, |
| reinterpret_cast<bfd_byte *>(contents + crc_offset)); |
| filename = string(contents, filename_len); |
| cverb << vbfd << ".gnu_debuglink filename is " << filename << endl; |
| free(contents); |
| return true; |
| } |
| |
| |
| /** |
| * With Objective C, we'll get strings like: |
| * |
| * _i_GSUnicodeString__rangeOfCharacterSetFromSet_options_range |
| * |
| * for the symbol name, and: |
| * -[GSUnicodeString rangeOfCharacterFromSet:options:range:] |
| * |
| * for the function name, so we have to do some looser matching |
| * than for other languages (unfortunately, it's not possible |
| * to demangle Objective C symbols). |
| */ |
| bool objc_match(string const & sym, string const & method) |
| { |
| if (method.length() < 3) |
| return false; |
| |
| string mangled; |
| |
| if (is_prefix(method, "-[")) { |
| mangled += "_i_"; |
| } else if (is_prefix(method, "+[")) { |
| mangled += "_c_"; |
| } else { |
| return false; |
| } |
| |
| string::const_iterator it = method.begin() + 2; |
| string::const_iterator const end = method.end(); |
| |
| bool found_paren = false; |
| |
| for (; it != end; ++it) { |
| switch (*it) { |
| case ' ': |
| mangled += '_'; |
| if (!found_paren) |
| mangled += '_'; |
| break; |
| case ':': |
| mangled += '_'; |
| break; |
| case ')': |
| case ']': |
| break; |
| case '(': |
| found_paren = true; |
| mangled += '_'; |
| break; |
| default: |
| mangled += *it; |
| } |
| } |
| |
| return sym == mangled; |
| } |
| |
| |
| /* |
| * With a binary image where some objects are missing debug |
| * info, we can end up attributing to a completely different |
| * function (#484660): bfd_nearest_line() will happily move from one |
| * symbol to the nearest one it can find with debug information. |
| * To mitigate this problem, we check that the symbol name |
| * matches the returned function name. |
| * |
| * However, this check fails in some cases it shouldn't: |
| * Objective C, and C++ static inline functions (as discussed in |
| * GCC bugzilla #11774). So, we have a looser check that |
| * accepts merely a substring, plus some magic for Objective C. |
| * |
| * If even the loose check fails, then we give up. |
| */ |
| bool is_correct_function(string const & function, string const & name) |
| { |
| if (name == function) |
| return true; |
| |
| if (objc_match(name, function)) |
| return true; |
| |
| // warn the user if we had to use the loose check |
| if (name.find(function) != string::npos) { |
| static bool warned = false; |
| if (!warned) { |
| cerr << "warning: some functions compiled without " |
| << "debug information may have incorrect source " |
| << "line attributions" << endl; |
| warned = true; |
| } |
| cverb << vbfd << "is_correct_function(" << function << ", " |
| << name << ") fuzzy match." << endl; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| /* |
| * binutils 2.12 and below have a small bug where functions without a |
| * debug entry at the prologue start do not give a useful line number |
| * from bfd_find_nearest_line(). This can happen with certain gcc |
| * versions such as 2.95. |
| * |
| * We work around this problem by scanning forward for a vma with valid |
| * linenr info, if we can't get a valid line number. Problem uncovered |
| * by Norbert Kaufmann. The work-around decreases, on the tincas |
| * application, the number of failure to retrieve linenr info from 835 |
| * to 173. Most of the remaining are c++ inline functions mainly from |
| * the STL library. Fix #529622 |
| */ |
| void fixup_linenr(bfd * abfd, asection * section, asymbol ** syms, |
| string const & name, bfd_vma pc, |
| char const ** filename, unsigned int * line) |
| { |
| char const * cfilename; |
| char const * function; |
| unsigned int linenr; |
| |
| // FIXME: looking at debug info for all gcc version shows than |
| // the same problems can -perhaps- occur for epilog code: find a |
| // samples files with samples in epilog and try opreport -l -g |
| // on it, check it also with opannotate. |
| |
| // first restrict the search on a sensible range of vma, 16 is |
| // an intuitive value based on epilog code look |
| size_t max_search = 16; |
| size_t section_size = bfd_section_size(abfd, section); |
| if (pc + max_search > section_size) |
| max_search = section_size - pc; |
| |
| for (size_t i = 1; i < max_search; ++i) { |
| bool ret = bfd_find_nearest_line(abfd, section, syms, pc + i, |
| &cfilename, &function, |
| &linenr); |
| |
| if (ret && cfilename && function && linenr != 0 |
| && is_correct_function(function, name)) { |
| *filename = cfilename; |
| *line = linenr; |
| return; |
| } |
| } |
| } |
| |
| |
| } // namespace anon |
| |
| |
| bfd * open_bfd(string const & file) |
| { |
| /* bfd keeps its own reference to the filename char *, |
| * so it must have a lifetime longer than the ibfd */ |
| bfd * ibfd = bfd_openr(file.c_str(), NULL); |
| if (!ibfd) { |
| cverb << vbfd << "bfd_openr failed for " << file << endl; |
| return NULL; |
| } |
| |
| check_format(file, &ibfd); |
| |
| return ibfd; |
| } |
| |
| |
| bfd * fdopen_bfd(string const & file, int fd) |
| { |
| /* bfd keeps its own reference to the filename char *, |
| * so it must have a lifetime longer than the ibfd */ |
| bfd * ibfd = bfd_fdopenr(file.c_str(), NULL, fd); |
| if (!ibfd) { |
| cverb << vbfd << "bfd_openr failed for " << file << endl; |
| return NULL; |
| } |
| |
| check_format(file, &ibfd); |
| |
| return ibfd; |
| } |
| |
| |
| bool find_separate_debug_file(bfd * ibfd, string const & filepath_in, |
| string & debug_filename, extra_images const & extra) |
| { |
| string filepath(filepath_in); |
| string basename; |
| unsigned long crc32 = 0; |
| // The readelf program uses a char [64], so that's what we'll use. |
| // To my knowledge, the build-id should not be bigger than 20 chars. |
| unsigned char buildid[64]; |
| |
| if (get_build_id(ibfd, buildid) && |
| find_debuginfo_file_by_buildid(buildid, debug_filename)) |
| return true; |
| |
| if (!get_debug_link_info(ibfd, basename, crc32)) |
| return false; |
| |
| /* Use old method of finding debuginfo file by comparing runtime binary's |
| * CRC with the CRC we calculate from the debuginfo file's contents. |
| * NOTE: This method breaks on systems where "MiniDebugInfo" is used |
| * since the CRC stored in the runtime binary won't match the compressed |
| * debuginfo file's CRC. But in practice, we shouldn't ever run into such |
| * a scenario since the build-id should always be available. |
| */ |
| |
| // Work out the image file's directory prefix |
| string filedir = op_dirname(filepath); |
| // Make sure it starts with / |
| if (filedir.size() > 0 && filedir.at(filedir.size() - 1) != '/') |
| filedir += '/'; |
| |
| string first_try(filedir + ".debug/" + basename); |
| string second_try(DEBUGDIR + filedir + basename); |
| string third_try(filedir + basename); |
| |
| ostringstream message; |
| message << "looking for debugging file " << basename |
| << " with crc32 = " << hex << crc32 << endl; |
| cverb << vbfd << message.str(); |
| |
| if (separate_debug_file_exists(first_try, crc32, extra)) |
| debug_filename = first_try; |
| else if (separate_debug_file_exists(second_try, crc32, extra)) |
| debug_filename = second_try; |
| else if (separate_debug_file_exists(third_try, crc32, extra)) |
| debug_filename = third_try; |
| else |
| return false; |
| |
| return true; |
| } |
| |
| |
| bool interesting_symbol(asymbol * sym) |
| { |
| // #717720 some binutils are miscompiled by gcc 2.95, one of the |
| // typical symptom can be catched here. |
| if (!sym->section) { |
| ostringstream os; |
| os << "Your version of binutils seems to have a bug.\n" |
| << "Read http://oprofile.sf.net/faq/#binutilsbug\n"; |
| throw op_runtime_error(os.str()); |
| } |
| |
| if (!(sym->section->flags & SEC_CODE)) |
| return false; |
| |
| // returning true for fix up in op_bfd_symbol() |
| if (!sym->name || sym->name[0] == '\0') |
| return true; |
| /* ARM assembler internal mapping symbols aren't interesting */ |
| if ((strcmp("$a", sym->name) == 0) || |
| (strcmp("$t", sym->name) == 0) || |
| (strcmp("$d", sym->name) == 0)) |
| return false; |
| |
| // C++ exception stuff |
| if (sym->name[0] == '.' && sym->name[1] == 'L') |
| return false; |
| |
| /* This case cannot be moved to boring_symbol(), |
| * because that's only used for duplicate VMAs, |
| * and sometimes this symbol appears at an address |
| * different from all other symbols. |
| */ |
| if (!strcmp("gcc2_compiled.", sym->name)) |
| return false; |
| |
| /* Commit ab45a0cc5d1cf522c1aef8f22ed512a9aae0dc1c removed a check for |
| * the SEC_LOAD bit. See the commit message for details why this |
| * was removed. |
| */ |
| |
| if (sym->flags & BSF_SECTION_SYM) |
| return false; |
| |
| return true; |
| } |
| |
| |
| bool boring_symbol(op_bfd_symbol const & first, op_bfd_symbol const & second) |
| { |
| if (first.name() == "Letext") |
| return true; |
| else if (second.name() == "Letext") |
| return false; |
| |
| if (first.name().substr(0, 2) == "??") |
| return true; |
| else if (second.name().substr(0, 2) == "??") |
| return false; |
| |
| if (first.hidden() && !second.hidden()) |
| return true; |
| else if (!first.hidden() && second.hidden()) |
| return false; |
| |
| if (first.name()[0] == '_' && second.name()[0] != '_') |
| return true; |
| else if (first.name()[0] != '_' && second.name()[0] == '_') |
| return false; |
| |
| if (first.weak() && !second.weak()) |
| return true; |
| else if (!first.weak() && second.weak()) |
| return false; |
| |
| return false; |
| } |
| |
| |
| bool bfd_info::has_debug_info() const |
| { |
| if (!valid()) |
| return false; |
| |
| for (asection const * sect = abfd->sections; sect; sect = sect->next) { |
| if (sect->flags & SEC_DEBUGGING) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| bfd_info::~bfd_info() |
| { |
| free(synth_syms); |
| close(); |
| } |
| |
| |
| void bfd_info::close() |
| { |
| if (abfd) |
| bfd_close(abfd); |
| } |
| |
| #if SYNTHESIZE_SYMBOLS |
| /** |
| * This function is intended solely for processing ppc64 debuginfo files. |
| * On ppc64 platforms where there is no symbol information in the image bfd, |
| * the debuginfo syms need to be mapped back to the sections of the image bfd |
| * when calling bfd_get_synthetic_symtab() to gather complete symbol information. |
| * That is the purpose of the translate_debuginfo_syms() function. |
| * |
| * This function is only called when processing symbols retrieved from a |
| * debuginfo file that is separate from the actual runtime binary image. |
| * Separate debuginfo files may be needed in two different cases: |
| * 1) the real image is completely stripped, where there is no symbol |
| information at all |
| * 2) the real image has debuginfo stripped, and the user is requesting "-g" |
| * (src file/line num info) |
| */ |
| void bfd_info::translate_debuginfo_syms(asymbol ** dbg_syms, long nr_dbg_syms) |
| { |
| unsigned int img_sect_cnt = 0; |
| bfd_vma vma_adj; |
| bfd * image_bfd = image_bfd_info->abfd; |
| multimap<string, bfd_section *> image_sections; |
| |
| for (bfd_section * sect = image_bfd->sections; |
| sect && img_sect_cnt < image_bfd->section_count; |
| sect = sect->next) { |
| // A comment section marks the end of the needed sections |
| if (strstr(sect->name, ".comment") == sect->name) |
| break; |
| image_sections.insert(pair<string, bfd_section *>(sect->name, sect)); |
| img_sect_cnt++; |
| } |
| |
| asymbol * sym = dbg_syms[0]; |
| string prev_sect_name = ""; |
| bfd_section * matched_section = NULL; |
| vma_adj = image_bfd->start_address - abfd->start_address; |
| for (int i = 0; i < nr_dbg_syms; sym = dbg_syms[++i]) { |
| bool section_switch; |
| |
| if (strcmp(prev_sect_name.c_str(), sym->section->name)) { |
| section_switch = true; |
| prev_sect_name = sym->section->name; |
| } else { |
| section_switch = false; |
| } |
| if (sym->section->owner && sym->section->owner == abfd) { |
| if (section_switch ) { |
| matched_section = NULL; |
| multimap<string, bfd_section *>::iterator it; |
| pair<multimap<string, bfd_section *>::iterator, |
| multimap<string, bfd_section *>::iterator> range; |
| |
| range = image_sections.equal_range(sym->section->name); |
| for (it = range.first; it != range.second; it++) { |
| if ((*it).second->vma == sym->section->vma + vma_adj) { |
| matched_section = (*it).second; |
| if (vma_adj) |
| section_vma_maps[(*it).second->vma] = sym->section->vma; |
| break; |
| } |
| } |
| } |
| if (matched_section) { |
| sym->section = matched_section; |
| sym->the_bfd = image_bfd; |
| } |
| } |
| } |
| } |
| |
| bool bfd_info::get_synth_symbols() |
| { |
| extern const bfd_target bfd_elf64_powerpc_vec; |
| extern const bfd_target bfd_elf64_powerpcle_vec; |
| bool is_elf64_powerpc_target = (abfd->xvec == &bfd_elf64_powerpc_vec) |
| || (abfd->xvec == &bfd_elf64_powerpcle_vec); |
| |
| if (!is_elf64_powerpc_target) |
| return false; |
| |
| void * buf; |
| uint tmp; |
| long nr_mini_syms = bfd_read_minisymbols(abfd, 0, &buf, &tmp); |
| if (nr_mini_syms < 1) |
| return false; |
| |
| asymbol ** mini_syms = (asymbol **)buf; |
| buf = NULL; |
| bfd * synth_bfd; |
| |
| /* For ppc64, a debuginfo file by itself does not hold enough symbol |
| * information for us to properly attribute samples to symbols. If |
| * the image file's bfd has no symbols (as in a super-stripped library), |
| * then we need to do the extra processing in translate_debuginfo_syms. |
| */ |
| if (image_bfd_info && image_bfd_info->nr_syms == 0) { |
| translate_debuginfo_syms(mini_syms, nr_mini_syms); |
| synth_bfd = image_bfd_info->abfd; |
| } else |
| synth_bfd = abfd; |
| |
| long nr_synth_syms = bfd_get_synthetic_symtab(synth_bfd, |
| nr_mini_syms, |
| mini_syms, 0, |
| NULL, &synth_syms); |
| |
| if (nr_synth_syms < 0) { |
| free(mini_syms); |
| return false; |
| } |
| |
| /* If we called translate_debuginfo_syms() above, then we had to map |
| * the debuginfo symbols' sections to the sections of the runtime binary. |
| * We had to twist ourselves in this knot due to the peculiar requirements |
| * of bfd_get_synthetic_symtab(). While doing this mapping, we cached |
| * the original section VMAs because we need those original values in |
| * order to properly match up sample offsets with debug data. So now that |
| * we're done with bfd_get_synthetic_symtab, we can restore these section |
| * VMAs. |
| */ |
| if (section_vma_maps.size()) { |
| unsigned int sect_count = 0; |
| for (bfd_section * sect = synth_bfd->sections; |
| sect && sect_count < synth_bfd->section_count; |
| sect = sect->next) { |
| sect->vma = section_vma_maps[sect->vma]; |
| sect_count++; |
| } |
| } |
| |
| |
| cverb << vbfd << "mini_syms: " << dec << nr_mini_syms << hex << endl; |
| cverb << vbfd << "synth_syms: " << dec << nr_synth_syms << hex << endl; |
| |
| nr_syms = nr_mini_syms + nr_synth_syms; |
| syms.reset(new asymbol *[nr_syms + 1]); |
| |
| for (size_t i = 0; i < (size_t)nr_mini_syms; ++i) |
| syms[i] = mini_syms[i]; |
| |
| |
| for (size_t i = 0; i < (size_t)nr_synth_syms; ++i) |
| syms[nr_mini_syms + i] = synth_syms + i; |
| |
| |
| free(mini_syms); |
| |
| // bfd_canonicalize_symtab does this, so shall we |
| syms[nr_syms] = NULL; |
| |
| return true; |
| } |
| #else |
| bool bfd_info::get_synth_symbols() |
| { |
| return false; |
| } |
| #endif /* SYNTHESIZE_SYMBOLS */ |
| |
| |
| void bfd_info::get_symbols() |
| { |
| if (!abfd) |
| return; |
| |
| cverb << vbfd << "bfd_info::get_symbols() for " |
| << bfd_get_filename(abfd) << endl; |
| |
| if (get_synth_symbols()) |
| return; |
| |
| if (bfd_get_file_flags(abfd) & HAS_SYMS) |
| nr_syms = bfd_get_symtab_upper_bound(abfd); |
| |
| ostringstream message; |
| message << "bfd_get_symtab_upper_bound: " << dec |
| << nr_syms << hex << endl; |
| cverb << vbfd << message.str(); |
| |
| nr_syms /= sizeof(asymbol *); |
| |
| if (nr_syms < 1) { |
| if (!image_bfd_info) |
| return; |
| syms.reset(); |
| cverb << vbfd << "Debuginfo has debug data only" << endl; |
| } else { |
| syms.reset(new asymbol *[nr_syms]); |
| nr_syms = bfd_canonicalize_symtab(abfd, syms.get()); |
| ostringstream message; |
| message << "bfd_canonicalize_symtab: " << dec |
| << nr_syms << hex << endl; |
| cverb << vbfd << message.str(); |
| } |
| } |
| |
| |
| linenr_info const |
| find_nearest_line(bfd_info const & b, op_bfd_symbol const & sym, |
| bfd_vma offset, bool anon_obj) |
| { |
| char const * function = ""; |
| char const * cfilename = ""; |
| unsigned int linenr = 0; |
| linenr_info info; |
| bfd * abfd; |
| asymbol ** syms; |
| asection * section = NULL; |
| asymbol * empty_syms[1]; |
| bfd_vma pc; |
| bool ret; |
| |
| if (!b.valid()) |
| goto fail; |
| |
| // take care about artificial symbol |
| if (!sym.symbol()) |
| goto fail; |
| |
| abfd = b.abfd; |
| syms = b.syms.get(); |
| if (!syms) { |
| // If this bfd_info object has no syms, that implies that we're |
| // using a debuginfo bfd_info object that has only debug data. |
| // This also implies that the passed sym is from the runtime binary, |
| // and thus it's section is also from the runtime binary. And |
| // since section VMA can be different for a runtime binary (prelinked) |
| // and its associated debuginfo, we need to obtain the debuginfo |
| // section to pass to the libbfd functions. |
| asection * sect_candidate; |
| bfd_vma vma_adj = b.get_image_bfd_info()->abfd->start_address - abfd->start_address; |
| if (vma_adj == 0) |
| section = sym.symbol()->section; |
| for (sect_candidate = abfd->sections; |
| (sect_candidate != NULL) && (section == NULL); |
| sect_candidate = sect_candidate->next) { |
| if (sect_candidate->vma + vma_adj == sym.symbol()->section->vma) { |
| section = sect_candidate; |
| } |
| } |
| if (section == NULL) { |
| cerr << "ERROR: Unable to find section for symbol " << sym.symbol()->name << endl; |
| goto fail; |
| } |
| syms = empty_syms; |
| syms[0] = NULL; |
| |
| } else { |
| section = sym.symbol()->section; |
| } |
| if (anon_obj) |
| pc = offset - sym.symbol()->section->vma; |
| else |
| pc = (sym.value() + offset) - sym.filepos(); |
| |
| if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) |
| goto fail; |
| |
| if (pc >= bfd_section_size(abfd, section)) |
| goto fail; |
| |
| ret = bfd_find_nearest_line(abfd, section, syms, pc, &cfilename, |
| &function, &linenr); |
| |
| if (!ret || !cfilename || !function) |
| goto fail; |
| |
| /* |
| * is_correct_function does not handle the case of static inlines, |
| * but if the linenr is non-zero in the inline case, it is the correct |
| * line number. |
| */ |
| if (linenr == 0 && !is_correct_function(function, sym.name())) |
| goto fail; |
| |
| if (linenr == 0) { |
| fixup_linenr(abfd, section, syms, sym.name(), pc, &cfilename, |
| &linenr); |
| } |
| |
| info.found = true; |
| info.filename = cfilename; |
| info.line = linenr; |
| return info; |
| |
| fail: |
| info.found = false; |
| // some stl lacks string::clear() |
| info.filename.erase(info.filename.begin(), info.filename.end()); |
| info.line = 0; |
| return info; |
| } |