/* Includes code from radare2: radare - LGPL - Copyright 2014 - inisider (https://github.com/radareorg/radare2) */ /* HACK: Honestly, this whole thing is a hack: 1. radare's PDB functions aren't great for external use, 2. few if any edge cases work, 3. we pretend our PDB symbols are minisyms, and 4. no tests. */ #include "pdb.h" extern const bfd_target pdb_vec; //from targets.c //BEGIN HACK (conflicting symbols) #undef QUIT #include #undef QUIT #define QUIT maybe_quit () #undef B_SET //END HACK (conflicting symbols) #include "objfiles.h" #include "gdb/fileio.h" #include "gdbsupport/pathstuff.h" #include #include #include extern "C" { #include #include //this is really dumb but oh well #include #include #include } #include "psympriv.h" //BEGIN radare2 imports #include "pdb_types.h" typedef void (*parse_stream_) (void *stream, R_STREAM_FILE *stream_file); typedef struct { int indx; parse_stream_ parse_stream; void *stream; EStream type; free_func free; } SStreamParseFunc; //END radare2 imports struct find_section_by_name_args { const char *name; asection **resultp; }; static void find_section_by_name_filter (bfd *abfd, asection *sect, void *obj) { (void) abfd; auto *args = (struct find_section_by_name_args *) obj; if (strcmp (sect->name, args->name) == 0) *args->resultp = sect; } static struct bfd_section * section_by_name (const char *name, struct objfile *objfile) { asection *sect = nullptr; struct find_section_by_name_args args{}; args.name = name; args.resultp = § bfd_map_over_sections (objfile->obfd, find_section_by_name_filter, &args); return sect; } static std::unique_ptr get_r_pdb (const std::string & path) { R_PDB pdb = {nullptr}; if (std::ifstream (path).good ()) { if (init_pdb_parser (&pdb, path.c_str ())) return std::make_unique (pdb); } else if (path.rfind ("target:", 0) == 0) { auto target = find_target_at (process_stratum); if (!target) { return nullptr; } void *buffer; size_t length; { //begin read PDB into buffer int errno; auto fd = target->fileio_open (nullptr, path.c_str () + sizeof ("target:") - 1, FILEIO_O_RDONLY, 0 /* mode */, true /* warn_if_slow */, &errno); if (fd == -1) { return nullptr; } struct stat st{}; if (target->fileio_fstat (fd, &st, &errno) == -1) { return nullptr; } length = st.st_size; buffer = xmalloc (length); file_ptr pos = 0, bytes; while (length > pos) { bytes = target->fileio_pread (fd, (gdb_byte *) buffer + pos, length - pos, pos, &errno); if (bytes == 0) { break; } else if (bytes == -1) { xfree (buffer); return nullptr; } else { pos += bytes; } } assert (pos == length); target->fileio_close (fd, &errno); } //end read PDB into buffer auto r_buffer = r_buf_new_with_bytes ((const ut8 *) buffer, length); if (init_pdb_parser_with_buf (&pdb, r_buffer)) { xfree (buffer); return std::make_unique (pdb); } xfree (buffer); } return nullptr; } static gdb::optional get_codeview_pdb_path (objfile *objfile) { gdb::optional nullopt; bfd *abfd = objfile->obfd; pe_data_type *pe = pe_data (abfd); struct internal_extra_pe_aouthdr *extra = &pe->pe_opthdr; bfd_size_type size = extra->DataDirectory[PE_DEBUG_DATA].Size; if (size == 0) return nullopt; bfd_vma addr = extra->DataDirectory[PE_DEBUG_DATA].VirtualAddress + extra->ImageBase; asection *section; for (section = abfd->sections; section != nullptr; section = section->next) { if ((addr >= section->vma) && (addr < (section->vma + section->size))) break; } if (section == nullptr || !(section->flags & SEC_HAS_CONTENTS) || section->size < size) return nullopt; bfd_size_type data_offset; data_offset = addr - section->vma; if (size > (section->size - data_offset)) return nullopt; bfd_byte *data = nullptr; //TODO: Check if this leaks memory (data isn't freed) if (!bfd_malloc_and_get_section (abfd, section, &data)) { if (data != nullptr) free (data); return nullopt; } for (auto i = 0; i < size / sizeof (struct external_IMAGE_DEBUG_DIRECTORY); i++) { struct external_IMAGE_DEBUG_DIRECTORY *ext = &((struct external_IMAGE_DEBUG_DIRECTORY *) (data + data_offset))[i]; struct internal_IMAGE_DEBUG_DIRECTORY idd{}; _bfd_pei_swap_debugdir_in (abfd, ext, &idd); if (idd.Type == PE_IMAGE_DEBUG_TYPE_CODEVIEW) { char buffer[256 + 1] ATTRIBUTE_ALIGNED_ALIGNOF (CODEVIEW_INFO); auto *cvinfo = (CODEVIEW_INFO *) buffer; if (!_bfd_pei_slurp_codeview_record (abfd, (file_ptr) idd.PointerToRawData, idd.SizeOfData, cvinfo)) continue; return std::string (cvinfo->PdbFileName); } } return nullopt; } static std::vector get_pdb_paths (struct objfile *objfile) { std::vector paths; auto codeview_pdb_path = get_codeview_pdb_path (objfile); if (!codeview_pdb_path) return paths; paths.push_back (*codeview_pdb_path); paths.push_back ("target:" + *codeview_pdb_path); auto real_path = gdb_realpath (objfile->original_name); auto naive_pdb_path = std::regex_replace (std::string (real_path.get ()), std::regex ("\\.[^.]*$"), ".pdb"); naive_pdb_path = std::regex_replace (naive_pdb_path, std::regex ("target:"), ""); paths.push_back (naive_pdb_path); paths.push_back ("target:" + naive_pdb_path); return paths; } static std::tuple, std::string> load_pdb (objfile *objfile) { auto paths = get_pdb_paths (objfile); if (paths.empty ()) return {nullptr, std::string ()}; for (auto & path : paths) { auto p = get_r_pdb (path); if (p) return {std::move (p), path}; } return {nullptr, std::string ()}; } gdb_bfd_ref_ptr try_load_pdb_bfd (objfile *objfile) { auto paths = get_pdb_paths (objfile); if (paths.empty ()) return nullptr; for (auto & path : paths) { try { gdb_bfd_ref_ptr debug_bfd (symfile_bfd_open (path.c_str ())); if (debug_bfd.get () && debug_bfd.get ()->xvec == &pdb_vec) { return debug_bfd; } } catch (gdb_exception_error & error) { continue; } } return nullptr; } void read_pdb (struct objfile *objfile, minimal_symbol_reader & reader) { std::unique_ptr pdb; std::string pdb_path; std::tie (pdb, pdb_path) = load_pdb (objfile); if (!pdb) return; if (pdb->pdb_parse (pdb.get ())) { SStreamParseFunc *omap = nullptr, *sctns = nullptr, *sctns_orig = nullptr, *gsym = nullptr, *tmp; SIMAGE_SECTION_HEADER *sctn_header; SGDATAStream *gsym_data_stream; SPEStream *pe_stream = nullptr; SGlobal *gdata; RListIter *it; RList *l; l = pdb->pdb_streams2; it = r_list_iterator (l); while (r_list_iter_next (it)) { tmp = (SStreamParseFunc *) r_list_iter_get (it); switch (tmp->type) { case ePDB_STREAM_SECT__HDR_ORIG: sctns_orig = tmp; break; case ePDB_STREAM_SECT_HDR: sctns = tmp; break; case ePDB_STREAM_OMAP_FROM_SRC: omap = tmp; break; case ePDB_STREAM_GSYM: gsym = tmp; break; default: break; } } if (!gsym) { eprintf ("There is no global symbols in current PDB.\n"); return; } gsym_data_stream = (SGDATAStream *) gsym->stream; if ((omap != nullptr) && (sctns_orig != nullptr)) { pe_stream = (SPEStream *) sctns_orig->stream; } else if (sctns) { pe_stream = (SPEStream *) sctns->stream; } if (!pe_stream) { return; } printf_filtered (_("Reading symbols from %s...\n"), pdb_path.c_str ()); it = r_list_iterator (gsym_data_stream->globals_list); while (r_list_iter_next (it)) { gdata = (SGlobal *) r_list_iter_get (it); sctn_header = (SIMAGE_SECTION_HEADER *) r_list_get_n (pe_stream->sections_hdrs, gdata->segment - 1); if (sctn_header) { asection *sect = section_by_name (sctn_header->name, objfile); auto section = sect ? sect->index : -1; auto section_address = sect ? bfd_section_vma (sect) : 0; auto address = section_address + gdata->offset; if (address == 0) continue; //we don't want to record unresolved symbols or something? auto type = mst_text; //FIXME reader.record_with_info (gdata->name.name, address, type, section); } } } pdb->finish_pdb_parse (pdb.get ()); }