Exemple #1
0
void
minimal_symbol_reader::record (const char *name, CORE_ADDR address,
			       enum minimal_symbol_type ms_type)
{
  int section;

  switch (ms_type)
    {
    case mst_text:
    case mst_text_gnu_ifunc:
    case mst_file_text:
    case mst_solib_trampoline:
      section = SECT_OFF_TEXT (m_objfile);
      break;
    case mst_data:
    case mst_data_gnu_ifunc:
    case mst_file_data:
      section = SECT_OFF_DATA (m_objfile);
      break;
    case mst_bss:
    case mst_file_bss:
      section = SECT_OFF_BSS (m_objfile);
      break;
    default:
      section = -1;
    }

  record_with_info (name, address, ms_type, section);
}
Exemple #2
0
static gdb_byte *
find_location_expression (struct dwarf2_loclist_baton *baton,
			  size_t *locexpr_length, CORE_ADDR pc)
{
  CORE_ADDR low, high;
  gdb_byte *loc_ptr, *buf_end;
  int length;
  struct objfile *objfile = dwarf2_per_cu_objfile (baton->per_cu);
  struct gdbarch *gdbarch = get_objfile_arch (objfile);
  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
  unsigned int addr_size = dwarf2_per_cu_addr_size (baton->per_cu);
  CORE_ADDR base_mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1));
  /* Adjust base_address for relocatable objects.  */
  CORE_ADDR base_offset = ANOFFSET (objfile->section_offsets,
				    SECT_OFF_TEXT (objfile));
  CORE_ADDR base_address = baton->base_address + base_offset;

  loc_ptr = baton->data;
  buf_end = baton->data + baton->size;

  while (1)
    {
      if (buf_end - loc_ptr < 2 * addr_size)
	error (_("find_location_expression: Corrupted DWARF expression."));

      low = extract_unsigned_integer (loc_ptr, addr_size, byte_order);
      loc_ptr += addr_size;

      /* A base-address-selection entry.  */
      if (low == base_mask)
	{
	  base_address = dwarf2_read_address (gdbarch,
					      loc_ptr, buf_end, addr_size);
	  loc_ptr += addr_size;
	  continue;
	}

      high = extract_unsigned_integer (loc_ptr, addr_size, byte_order);
      loc_ptr += addr_size;

      /* An end-of-list entry.  */
      if (low == 0 && high == 0)
	return NULL;

      /* Otherwise, a location expression entry.  */
      low += base_address;
      high += base_address;

      length = extract_unsigned_integer (loc_ptr, 2, byte_order);
      loc_ptr += 2;

      if (pc >= low && pc < high)
	{
	  *locexpr_length = length;
	  return loc_ptr;
	}

      loc_ptr += length;
    }
}
static gdb_byte *
find_location_expression (struct dwarf2_loclist_baton *baton,
			  size_t *locexpr_length, CORE_ADDR pc)
{
  CORE_ADDR low, high;
  gdb_byte *loc_ptr, *buf_end;
  int length;
  unsigned int addr_size = gdbarch_addr_bit (current_gdbarch) / TARGET_CHAR_BIT;
  CORE_ADDR base_mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1));
  /* Adjust base_address for relocatable objects.  */
  CORE_ADDR base_offset = ANOFFSET (baton->objfile->section_offsets,
				    SECT_OFF_TEXT (baton->objfile));
  CORE_ADDR base_address = baton->base_address + base_offset;

  loc_ptr = baton->data;
  buf_end = baton->data + baton->size;

  while (1)
    {
      low = dwarf2_read_address (loc_ptr, buf_end, &length);
      loc_ptr += length;
      high = dwarf2_read_address (loc_ptr, buf_end, &length);
      loc_ptr += length;

      /* An end-of-list entry.  */
      if (low == 0 && high == 0)
	return NULL;

      /* A base-address-selection entry.  */
      if ((low & base_mask) == base_mask)
	{
	  base_address = high;
	  continue;
	}

      /* Otherwise, a location expression entry.  */
      low += base_address;
      high += base_address;

      length = extract_unsigned_integer (loc_ptr, 2);
      loc_ptr += 2;

      if (pc >= low && pc < high)
	{
	  *locexpr_length = length;
	  return loc_ptr;
	}

      loc_ptr += length;
    }
}
Exemple #4
0
static void
vmap_symtab (struct vmap *vp)
{
  struct objfile *objfile;
  struct section_offsets *new_offsets;
  int i;

  objfile = vp->objfile;
  if (objfile == NULL)
    {
      /* OK, it's not an objfile we opened ourselves.
         Currently, that can only happen with the exec file, so
         relocate the symbols for the symfile.  */
      if (symfile_objfile == NULL)
	return;
      objfile = symfile_objfile;
    }
  else if (!vp->loaded)
    /* If symbols are not yet loaded, offsets are not yet valid.  */
    return;

  new_offsets =
    (struct section_offsets *)
    alloca (SIZEOF_N_SECTION_OFFSETS (objfile->num_sections));

  for (i = 0; i < objfile->num_sections; ++i)
    new_offsets->offsets[i] = ANOFFSET (objfile->section_offsets, i);

  /* The symbols in the object file are linked to the VMA of the section,
     relocate them VMA relative.  */
  new_offsets->offsets[SECT_OFF_TEXT (objfile)] = vp->tstart - vp->tvma;
  new_offsets->offsets[SECT_OFF_DATA (objfile)] = vp->dstart - vp->dvma;
  new_offsets->offsets[SECT_OFF_BSS (objfile)] = vp->dstart - vp->dvma;

  /* Perform the same adjustment as the loader if the .data and
     .bss sections overlap.  */
  new_offsets->offsets[SECT_OFF_BSS (objfile)] += bss_data_overlap (objfile);

  objfile_relocate (objfile, new_offsets);
}
static int
add_pe_forwarded_sym (minimal_symbol_reader &reader,
		      const char *sym_name, const char *forward_dll_name,
		      const char *forward_func_name, int ordinal,
		      const char *dll_name, struct objfile *objfile)
{
  CORE_ADDR vma, baseaddr;
  struct bound_minimal_symbol msymbol;
  enum minimal_symbol_type msymtype;
  char *qualified_name, *bare_name;
  int forward_dll_name_len = strlen (forward_dll_name);
  int forward_func_name_len = strlen (forward_func_name);
  int forward_len = forward_dll_name_len + forward_func_name_len + 2;
  char *forward_qualified_name = (char *) alloca (forward_len);
  short section;

  xsnprintf (forward_qualified_name, forward_len, "%s!%s", forward_dll_name,
	     forward_func_name);


  msymbol = lookup_minimal_symbol_and_objfile (forward_qualified_name);

  if (!msymbol.minsym)
    {
      int i;

      for (i = 0; i < forward_dll_name_len; i++)
	forward_qualified_name[i] = tolower (forward_qualified_name[i]);
      msymbol = lookup_minimal_symbol_and_objfile (forward_qualified_name);
    }

  if (!msymbol.minsym)
    {
      if (debug_coff_pe_read)
	fprintf_unfiltered (gdb_stdlog, _("Unable to find function \"%s\" in"
			    " dll \"%s\", forward of \"%s\" in dll \"%s\"\n"),
			    forward_func_name, forward_dll_name, sym_name,
			    dll_name);
      return 0;
    }

  if (debug_coff_pe_read > 1)
    fprintf_unfiltered (gdb_stdlog, _("Adding forwarded exported symbol"
			" \"%s\" in dll \"%s\", pointing to \"%s\"\n"),
			sym_name, dll_name, forward_qualified_name);

  vma = BMSYMBOL_VALUE_ADDRESS (msymbol);
  msymtype = MSYMBOL_TYPE (msymbol.minsym);
  section = MSYMBOL_SECTION (msymbol.minsym);

  /* Generate a (hopefully unique) qualified name using the first part
     of the dll name, e.g. KERNEL32!AddAtomA.  This matches the style
     used by windbg from the "Microsoft Debugging Tools for Windows".  */

  if (sym_name == NULL || *sym_name == '\0')
    bare_name = xstrprintf ("#%d", ordinal);
  else
    bare_name = xstrdup (sym_name);

  qualified_name = xstrprintf ("%s!%s", dll_name, bare_name);

  /* Note that this code makes a minimal symbol whose value may point
     outside of any section in this objfile.  These symbols can't
     really be relocated properly, but nevertheless we make a stab at
     it, choosing an approach consistent with the history of this
     code.  */
  baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));

  reader.record_with_info (qualified_name, vma - baseaddr, msymtype, section);

  /* Enter the plain name as well, which might not be unique.  */
  reader.record_with_info (bare_name, vma - baseaddr, msymtype, section);
  xfree (qualified_name);
  xfree (bare_name);

  return 1;
}
Exemple #6
0
void
read_pe_exported_syms (struct objfile *objfile)
{
  bfd *dll = objfile->obfd;
  unsigned long nbnormal, nbforward;
  unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
  unsigned long export_opthdrrva, export_opthdrsize;
  unsigned long export_rva, export_size, nsections, secptr, expptr;
  unsigned long exp_funcbase;
  unsigned char *expdata, *erva;
  unsigned long name_rvas, ordinals, nexp, ordbase;
  char *dll_name = (char *) dll->filename;
  int otherix = PE_SECTION_TABLE_SIZE;
  int is_pe64 = 0;
  int is_pe32 = 0;

  /* Array elements are for text, data and bss in that order
     Initialization with RVA_START > RVA_END guarantees that
     unused sections won't be matched.  */
  struct read_pe_section_data *section_data;
  struct pe_sections_info pe_sections_info;

  struct cleanup *back_to = make_cleanup (null_cleanup, 0);

  char const *target = bfd_get_target (objfile->obfd);

  section_data = xzalloc (PE_SECTION_TABLE_SIZE
			 * sizeof (struct read_pe_section_data));

  make_cleanup (free_current_contents, &section_data);

  for (i=0; i < PE_SECTION_TABLE_SIZE; i++)
    {
      section_data[i].vma_offset = 0;
      section_data[i].rva_start = 1;
      section_data[i].rva_end = 0;
    };
  section_data[PE_SECTION_INDEX_TEXT].ms_type = mst_text;
  section_data[PE_SECTION_INDEX_TEXT].section_name = ".text";
  section_data[PE_SECTION_INDEX_DATA].ms_type = mst_data;
  section_data[PE_SECTION_INDEX_DATA].section_name = ".data";
  section_data[PE_SECTION_INDEX_BSS].ms_type = mst_bss;
  section_data[PE_SECTION_INDEX_BSS].section_name = ".bss";

  is_pe64 = (strcmp (target, "pe-x86-64") == 0
	     || strcmp (target, "pei-x86-64") == 0);
  is_pe32 = (strcmp (target, "pe-i386") == 0
	     || strcmp (target, "pei-i386") == 0
	     || strcmp (target, "pe-arm-wince-little") == 0
	     || strcmp (target, "pei-arm-wince-little") == 0);
  if (!is_pe32 && !is_pe64)
    {
      /* This is not a recognized PE format file.  Abort now, because
	 the code is untested on anything else.  *FIXME* test on
	 further architectures and loosen or remove this test.  */
      do_cleanups (back_to);
      return;
    }

  /* Get pe_header, optional header and numbers of export entries.  */
  pe_header_offset = pe_get32 (dll, 0x3c);
  opthdr_ofs = pe_header_offset + 4 + 20;
  if (is_pe64)
    num_entries = pe_get32 (dll, opthdr_ofs + 108);
  else
    num_entries = pe_get32 (dll, opthdr_ofs + 92);

  if (num_entries < 1)		/* No exports.  */
    {
      do_cleanups (back_to);
      return;
    }
  if (is_pe64)
    {
      export_opthdrrva = pe_get32 (dll, opthdr_ofs + 112);
      export_opthdrsize = pe_get32 (dll, opthdr_ofs + 116);
    }
  else
    {
      export_opthdrrva = pe_get32 (dll, opthdr_ofs + 96);
      export_opthdrsize = pe_get32 (dll, opthdr_ofs + 100);
    }
  nsections = pe_get16 (dll, pe_header_offset + 4 + 2);
  secptr = (pe_header_offset + 4 + 20 +
	    pe_get16 (dll, pe_header_offset + 4 + 16));
  expptr = 0;
  export_size = 0;

  /* Get the rva and size of the export section.  */
  for (i = 0; i < nsections; i++)
    {
      char sname[8];
      unsigned long secptr1 = secptr + 40 * i;
      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
      unsigned long vsize = pe_get32 (dll, secptr1 + 16);
      unsigned long fptr = pe_get32 (dll, secptr1 + 20);

      bfd_seek (dll, (file_ptr) secptr1, SEEK_SET);
      bfd_bread (sname, (bfd_size_type) sizeof (sname), dll);

      if ((strcmp (sname, ".edata") == 0)
	  || (vaddr <= export_opthdrrva && export_opthdrrva < vaddr + vsize))
	{
	  if (strcmp (sname, ".edata") != 0)
	    {
	      if (debug_coff_pe_read)
		fprintf_unfiltered (gdb_stdlog, _("Export RVA for dll "
				    "\"%s\" is in section \"%s\"\n"),
				    dll_name, sname);
	    }
	  else if (export_opthdrrva != vaddr && debug_coff_pe_read)
	    fprintf_unfiltered (gdb_stdlog, _("Wrong value of export RVA"
				" for dll \"%s\": 0x%lx instead of 0x%lx\n"),
				dll_name, export_opthdrrva, vaddr);
	  expptr = fptr + (export_opthdrrva - vaddr);
	  break;
	}
    }

  export_rva = export_opthdrrva;
  export_size = export_opthdrsize;

  if (export_size == 0)
    {
      /* Empty export table.  */
      do_cleanups (back_to);
      return;
    }

  /* Scan sections and store the base and size of the relevant
     sections.  */
  for (i = 0; i < nsections; i++)
    {
      unsigned long secptr1 = secptr + 40 * i;
      unsigned long vsize = pe_get32 (dll, secptr1 + 8);
      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
      unsigned long characteristics = pe_get32 (dll, secptr1 + 36);
      char sec_name[SCNNMLEN + 1];
      int sectix;

      bfd_seek (dll, (file_ptr) secptr1 + 0, SEEK_SET);
      bfd_bread (sec_name, (bfd_size_type) SCNNMLEN, dll);
      sec_name[SCNNMLEN] = '\0';

      sectix = read_pe_section_index (sec_name);

      if (sectix != PE_SECTION_INDEX_INVALID)
	{
	  section_data[sectix].rva_start = vaddr;
	  section_data[sectix].rva_end = vaddr + vsize;
	}
      else
	{
	  char *name;

	  section_data = xrealloc (section_data, (otherix + 1)
				   * sizeof (struct read_pe_section_data));
	  name = xstrdup (sec_name);
	  section_data[otherix].section_name = name;
	  make_cleanup (xfree, name);
	  section_data[otherix].rva_start = vaddr;
	  section_data[otherix].rva_end = vaddr + vsize;
	  section_data[otherix].vma_offset = 0;
	  if (characteristics & IMAGE_SCN_CNT_CODE)
	    section_data[otherix].ms_type = mst_text;
	  else if (characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
	    section_data[otherix].ms_type = mst_data;
	  else if (characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
	    section_data[otherix].ms_type = mst_bss;
	  else
	    section_data[otherix].ms_type = mst_unknown;
	  otherix++;
	}
    }

  expdata = (unsigned char *) xmalloc (export_size);
  make_cleanup (xfree, expdata);

  bfd_seek (dll, (file_ptr) expptr, SEEK_SET);
  bfd_bread (expdata, (bfd_size_type) export_size, dll);
  erva = expdata - export_rva;

  nexp = pe_as32 (expdata + 24);
  name_rvas = pe_as32 (expdata + 32);
  ordinals = pe_as32 (expdata + 36);
  ordbase = pe_as32 (expdata + 16);
  exp_funcbase = pe_as32 (expdata + 28);

  /* Use internal dll name instead of full pathname.  */
  dll_name = pe_as32 (expdata + 12) + erva;

  pe_sections_info.nb_sections = otherix;
  pe_sections_info.sections = section_data;

  bfd_map_over_sections (dll, get_section_vmas, &pe_sections_info);

  /* Adjust the vma_offsets in case this PE got relocated. This
     assumes that *all* sections share the same relocation offset
     as the text section.  */
  for (i = 0; i < otherix; i++)
    {
      section_data[i].vma_offset
	+= ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
    }

  /* Truncate name at first dot. Should maybe also convert to all
     lower case for convenience on Windows.  */
  read_pe_truncate_name (dll_name);

  if (debug_coff_pe_read)
    fprintf_unfiltered (gdb_stdlog, _("DLL \"%s\" has %ld export entries,"
			" base=%ld\n"), dll_name, nexp, ordbase);
  nbforward = 0;
  nbnormal = 0;
  /* Iterate through the list of symbols.  */
  for (i = 0; i < nexp; i++)
    {
      /* Pointer to the names vector.  */
      unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4);
      /* Retrieve ordinal value.  */

      unsigned long ordinal = pe_as16 (erva + ordinals + i * 2);


      /* Pointer to the function address vector.  */
      /* This is relatived to ordinal value. */
      unsigned long func_rva = pe_as32 (erva + exp_funcbase +
                                        ordinal * 4);

      /* Find this symbol's section in our own array.  */
      int sectix = 0;
      int section_found = 0;

      /* First handle forward cases.  */
      if (func_rva >= export_rva && func_rva < export_rva + export_size)
	{
	  char *forward_name = (char *) (erva + func_rva);
	  char *funcname = (char *) (erva + name_rva);
	  char *forward_dll_name = forward_name;
	  char *forward_func_name = forward_name;
	  char *sep = strrchr (forward_name, '.');

	  if (sep)
	    {
	      int len = (int) (sep - forward_name);

	      forward_dll_name = alloca (len + 1);
	      strncpy (forward_dll_name, forward_name, len);
	      forward_dll_name[len] = '\0';
	      forward_func_name = ++sep;
	    }
	  if (add_pe_forwarded_sym (funcname, forward_dll_name,
				    forward_func_name, ordinal,
				    dll_name, objfile) != 0)
	    ++nbforward;
	  continue;
	}

      for (sectix = 0; sectix < otherix; ++sectix)
	{
	  if ((func_rva >= section_data[sectix].rva_start)
	      && (func_rva < section_data[sectix].rva_end))
	    {
	      section_found = 1;
	      add_pe_exported_sym (erva + name_rva,
				   func_rva, ordinal,
				   section_data + sectix, dll_name, objfile);
	      ++nbnormal;
	      break;
	    }
	}
      if (!section_found)
	{
	  char *funcname = (char *) (erva + name_rva);

	  if (name_rva == 0)
	    {
	      add_pe_exported_sym (NULL, func_rva, ordinal,
				   section_data, dll_name, objfile);
	      ++nbnormal;
	    }
	  else if (debug_coff_pe_read)
	    fprintf_unfiltered (gdb_stdlog, _("Export name \"%s\" ord. %lu,"
				" RVA 0x%lx in dll \"%s\" not handled\n"),
				funcname, ordinal, func_rva, dll_name);
	}
    }

  if (debug_coff_pe_read)
    fprintf_unfiltered (gdb_stdlog, _("Finished reading \"%s\", exports %ld,"
			" forwards %ld, total %ld/%ld.\n"), dll_name, nbnormal,
			nbforward, nbnormal + nbforward, nexp);
  /* Discard expdata and section_data.  */
  do_cleanups (back_to);
}
Exemple #7
0
void
read_pe_exported_syms (struct objfile *objfile)
{
  bfd *dll = objfile->obfd;
  unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
  unsigned long export_rva, export_size, nsections, secptr, expptr;
  unsigned long exp_funcbase;
  unsigned char *expdata, *erva;
  unsigned long name_rvas, ordinals, nexp, ordbase;
  char *dll_name;

  /* Array elements are for text, data and bss in that order
     Initialization with start_rva > end_rva guarantees that
     unused sections won't be matched. */
  struct read_pe_section_data section_data[PE_SECTION_TABLE_SIZE]
    = { {0, 1, 0, mst_text},
  {0, 1, 0, mst_data},
  {0, 1, 0, mst_bss}
  };

  struct cleanup *back_to = 0;

  char const *target = bfd_get_target (objfile->obfd);

  if ((strcmp (target, "pe-i386") != 0) && (strcmp (target, "pei-i386") != 0))
    {
      /* This is not an i386 format file. Abort now, because the code
         is untested on anything else. *FIXME* test on further
         architectures and loosen or remove this test. */
      return;
    }

  /* Get pe_header, optional header and numbers of export entries.  */
  pe_header_offset = pe_get32 (dll, 0x3c);
  opthdr_ofs = pe_header_offset + 4 + 20;
  num_entries = pe_get32 (dll, opthdr_ofs + 92);

  if (num_entries < 1)		/* No exports.  */
    {
      return;
    }

  export_rva = pe_get32 (dll, opthdr_ofs + 96);
  export_size = pe_get32 (dll, opthdr_ofs + 100);
  nsections = pe_get16 (dll, pe_header_offset + 4 + 2);
  secptr = (pe_header_offset + 4 + 20 +
	    pe_get16 (dll, pe_header_offset + 4 + 16));
  expptr = 0;

  /* Get the rva and size of the export section.  */
  for (i = 0; i < nsections; i++)
    {
      char sname[8];
      unsigned long secptr1 = secptr + 40 * i;
      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
      unsigned long vsize = pe_get32 (dll, secptr1 + 16);
      unsigned long fptr = pe_get32 (dll, secptr1 + 20);

      bfd_seek (dll, (file_ptr) secptr1, SEEK_SET);
      bfd_bread (sname, (bfd_size_type) 8, dll);

      if (vaddr <= export_rva && vaddr + vsize > export_rva)
	{
	  expptr = fptr + (export_rva - vaddr);
	  if (export_rva + export_size > vaddr + vsize)
	    export_size = vsize - (export_rva - vaddr);
	  break;
	}
    }

  if (export_size == 0)
    {
      /* Empty export table. */
      return;
    }

  /* Scan sections and store the base and size of the relevant sections. */
  for (i = 0; i < nsections; i++)
    {
      unsigned long secptr1 = secptr + 40 * i;
      unsigned long vsize = pe_get32 (dll, secptr1 + 8);
      unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
      unsigned long flags = pe_get32 (dll, secptr1 + 36);
      char sec_name[9];
      int sectix;

      sec_name[8] = '\0';
      bfd_seek (dll, (file_ptr) secptr1 + 0, SEEK_SET);
      bfd_bread (sec_name, (bfd_size_type) 8, dll);

      sectix = read_pe_section_index (sec_name);

      if (sectix != PE_SECTION_INDEX_INVALID)
	{
	  section_data[sectix].rva_start = vaddr;
	  section_data[sectix].rva_end = vaddr + vsize;
	}
    }

  expdata = (unsigned char *) xmalloc (export_size);
  back_to = make_cleanup (xfree, expdata);

  bfd_seek (dll, (file_ptr) expptr, SEEK_SET);
  bfd_bread (expdata, (bfd_size_type) export_size, dll);
  erva = expdata - export_rva;

  nexp = pe_as32 (expdata + 24);
  name_rvas = pe_as32 (expdata + 32);
  ordinals = pe_as32 (expdata + 36);
  ordbase = pe_as32 (expdata + 16);
  exp_funcbase = pe_as32 (expdata + 28);

  /* Use internal dll name instead of full pathname. */
  dll_name = pe_as32 (expdata + 12) + erva;

  bfd_map_over_sections (dll, get_section_vmas, section_data);

  /* Adjust the vma_offsets in case this PE got relocated. This
     assumes that *all* sections share the same relocation offset
     as the text section. */
  for (i = 0; i < PE_SECTION_TABLE_SIZE; i++)
    {
      section_data[i].vma_offset
	+= ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
    }

  printf_filtered ("Minimal symbols from %s...", dll_name);
  wrap_here ("");

  /* Truncate name at first dot. Should maybe also convert to all
     lower case for convenience on Windows. */
  read_pe_truncate_name (dll_name);

  /* Iterate through the list of symbols.  */
  for (i = 0; i < nexp; i++)
    {
      /* Pointer to the names vector.  */
      unsigned long name_rva = pe_as32 (erva + name_rvas + i * 4);

      /* Pointer to the function address vector.  */
      unsigned long func_rva = pe_as32 (erva + exp_funcbase + i * 4);

      /* Find this symbol's section in our own array. */
      int sectix = 0;

      for (sectix = 0; sectix < PE_SECTION_TABLE_SIZE; ++sectix)
	{
	  if ((func_rva >= section_data[sectix].rva_start)
	      && (func_rva < section_data[sectix].rva_end))
	    {
	      add_pe_exported_sym (erva + name_rva,
				   func_rva,
				   section_data + sectix, dll_name, objfile);
	      break;
	    }
	}
    }

  /* discard expdata. */
  do_cleanups (back_to);
}
Exemple #8
0
static struct mdebug_extra_func_info *
non_heuristic_proc_desc (CORE_ADDR pc, CORE_ADDR *addrptr)
{
    CORE_ADDR startaddr;
    struct mdebug_extra_func_info *proc_desc;
    struct block *b = block_for_pc (pc);
    struct symbol *sym;
    struct obj_section *sec;
    struct mips_objfile_private *priv;

    find_pc_partial_function (pc, NULL, &startaddr, NULL);
    if (addrptr)
        *addrptr = startaddr;

    priv = NULL;

    sec = find_pc_section (pc);
    if (sec != NULL)
    {
        priv = (struct mips_objfile_private *) objfile_data (sec->objfile, mips_pdr_data);

        /* Search the ".pdr" section generated by GAS.  This includes most of
           the information normally found in ECOFF PDRs.  */

        the_bfd = sec->objfile->obfd;
        if (priv == NULL
                && (the_bfd->format == bfd_object
                    && bfd_get_flavour (the_bfd) == bfd_target_elf_flavour
                    && elf_elfheader (the_bfd)->e_ident[EI_CLASS] == ELFCLASS64))
        {
            /* Right now GAS only outputs the address as a four-byte sequence.
               This means that we should not bother with this method on 64-bit
               targets (until that is fixed).  */

            priv = obstack_alloc (&sec->objfile->objfile_obstack,
                                  sizeof (struct mips_objfile_private));
            priv->size = 0;
            set_objfile_data (sec->objfile, mips_pdr_data, priv);
        }
        else if (priv == NULL)
        {
            asection *bfdsec;

            priv = obstack_alloc (&sec->objfile->objfile_obstack,
                                  sizeof (struct mips_objfile_private));

            bfdsec = bfd_get_section_by_name (sec->objfile->obfd, ".pdr");
            if (bfdsec != NULL)
            {
                priv->size = bfd_section_size (sec->objfile->obfd, bfdsec);
                priv->contents = obstack_alloc (&sec->objfile->objfile_obstack,
                                                priv->size);
                bfd_get_section_contents (sec->objfile->obfd, bfdsec,
                                          priv->contents, 0, priv->size);

                /* In general, the .pdr section is sorted.  However, in the
                   presence of multiple code sections (and other corner cases)
                   it can become unsorted.  Sort it so that we can use a faster
                   binary search.  */
                qsort (priv->contents, priv->size / 32, 32,
                       compare_pdr_entries);
            }
            else
                priv->size = 0;

            set_objfile_data (sec->objfile, mips_pdr_data, priv);
        }
        the_bfd = NULL;

        if (priv->size != 0)
        {
            int low, mid, high;
            char *ptr;
            CORE_ADDR pdr_pc;

            low = 0;
            high = priv->size / 32;

            /* We've found a .pdr section describing this objfile.  We want to
               find the entry which describes this code address.  The .pdr
               information is not very descriptive; we have only a function
               start address.  We have to look for the closest entry, because
               the local symbol at the beginning of this function may have
               been stripped - so if we ask the symbol table for the start
               address we may get a preceding global function.  */

            /* First, find the last .pdr entry starting at or before PC.  */
            do
            {
                mid = (low + high) / 2;

                ptr = priv->contents + mid * 32;
                pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
                pdr_pc += ANOFFSET (sec->objfile->section_offsets,
                                    SECT_OFF_TEXT (sec->objfile));

                if (pdr_pc > pc)
                    high = mid;
                else
                    low = mid + 1;
            }
            while (low != high);

            /* Both low and high point one past the PDR of interest.  If
               both are zero, that means this PC is before any region
               covered by a PDR, i.e. pdr_pc for the first PDR entry is
               greater than PC.  */
            if (low > 0)
            {
                ptr = priv->contents + (low - 1) * 32;
                pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
                pdr_pc += ANOFFSET (sec->objfile->section_offsets,
                                    SECT_OFF_TEXT (sec->objfile));
            }

            /* We don't have a range, so we have no way to know for sure
               whether we're in the correct PDR or a PDR for a preceding
               function and the current function was a stripped local
               symbol.  But if the PDR's PC is at least as great as the
               best guess from the symbol table, assume that it does cover
               the right area; if a .pdr section is present at all then
               nearly every function will have an entry.  The biggest exception
               will be the dynamic linker stubs; conveniently these are
               placed before .text instead of after.  */

            if (pc >= pdr_pc && pdr_pc >= startaddr)
            {
                struct symbol *sym = find_pc_function (pc);

                if (addrptr)
                    *addrptr = pdr_pc;

                /* Fill in what we need of the proc_desc.  */
                proc_desc = (struct mdebug_extra_func_info *)
                            obstack_alloc (&sec->objfile->objfile_obstack,
                                           sizeof (struct mdebug_extra_func_info));
                PROC_LOW_ADDR (proc_desc) = pdr_pc;

                PROC_FRAME_OFFSET (proc_desc)
                    = bfd_get_signed_32 (sec->objfile->obfd, ptr + 20);
                PROC_FRAME_REG (proc_desc) = bfd_get_32 (sec->objfile->obfd,
                                             ptr + 24);
                PROC_REG_MASK (proc_desc) = bfd_get_32 (sec->objfile->obfd,
                                                        ptr + 4);
                PROC_FREG_MASK (proc_desc) = bfd_get_32 (sec->objfile->obfd,
                                             ptr + 12);
                PROC_REG_OFFSET (proc_desc)
                    = bfd_get_signed_32 (sec->objfile->obfd, ptr + 8);
                PROC_FREG_OFFSET (proc_desc)
                    = bfd_get_signed_32 (sec->objfile->obfd, ptr + 16);
                PROC_PC_REG (proc_desc) = bfd_get_32 (sec->objfile->obfd,
                                                      ptr + 28);
                proc_desc->pdr.isym = (long) sym;

                return proc_desc;
            }
        }
    }

    if (b == NULL)
        return NULL;

    if (startaddr > BLOCK_START (b))
    {
        /* This is the "pathological" case referred to in a comment in
           print_frame_info.  It might be better to move this check into
           symbol reading.  */
        return NULL;
    }

    sym = lookup_symbol (MDEBUG_EFI_SYMBOL_NAME, b, LABEL_DOMAIN, 0);

    /* If we never found a PDR for this function in symbol reading, then
       examine prologues to find the information.  */
    if (sym)
    {
        proc_desc = (struct mdebug_extra_func_info *) SYMBOL_VALUE (sym);
        if (PROC_FRAME_REG (proc_desc) == -1)
            return NULL;
        else
            return proc_desc;
    }
    else
        return NULL;
}
Exemple #9
0
static void
read_alphacoff_dynamic_symtab (struct section_offsets *section_offsets,
			       struct objfile *objfile)
{
  bfd *abfd = objfile->obfd;
  struct alphacoff_dynsecinfo si;
  char *sym_secptr;
  char *str_secptr;
  char *dyninfo_secptr;
  char *got_secptr;
  bfd_size_type sym_secsize;
  bfd_size_type str_secsize;
  bfd_size_type dyninfo_secsize;
  bfd_size_type got_secsize;
  int sym_count;
  int i;
  int stripped;
  Elfalpha_External_Sym *x_symp;
  char *dyninfo_p;
  char *dyninfo_end;
  int got_entry_size = 8;
  int dt_mips_local_gotno = -1;
  int dt_mips_gotsym = -1;
  struct cleanup *cleanups;

  /* We currently only know how to handle alpha dynamic symbols.  */
  if (bfd_get_arch (abfd) != bfd_arch_alpha)
    return;

  /* Locate the dynamic symbols sections and read them in.  */
  memset ((char *) &si, 0, sizeof (si));
  bfd_map_over_sections (abfd, alphacoff_locate_sections, (void *) & si);
  if (si.sym_sect == NULL || si.str_sect == NULL
      || si.dyninfo_sect == NULL || si.got_sect == NULL)
    return;

  sym_secsize = bfd_get_section_size (si.sym_sect);
  str_secsize = bfd_get_section_size (si.str_sect);
  dyninfo_secsize = bfd_get_section_size (si.dyninfo_sect);
  got_secsize = bfd_get_section_size (si.got_sect);
  sym_secptr = xmalloc (sym_secsize);
  cleanups = make_cleanup (xfree, sym_secptr);
  str_secptr = xmalloc (str_secsize);
  make_cleanup (xfree, str_secptr);
  dyninfo_secptr = xmalloc (dyninfo_secsize);
  make_cleanup (xfree, dyninfo_secptr);
  got_secptr = xmalloc (got_secsize);
  make_cleanup (xfree, got_secptr);

  if (!bfd_get_section_contents (abfd, si.sym_sect, sym_secptr,
				 (file_ptr) 0, sym_secsize))
    return;
  if (!bfd_get_section_contents (abfd, si.str_sect, str_secptr,
				 (file_ptr) 0, str_secsize))
    return;
  if (!bfd_get_section_contents (abfd, si.dyninfo_sect, dyninfo_secptr,
				 (file_ptr) 0, dyninfo_secsize))
    return;
  if (!bfd_get_section_contents (abfd, si.got_sect, got_secptr,
				 (file_ptr) 0, got_secsize))
    return;

  /* Find the number of local GOT entries and the index for the the
     first dynamic symbol in the GOT.  */
  for (dyninfo_p = dyninfo_secptr, dyninfo_end = dyninfo_p + dyninfo_secsize;
       dyninfo_p < dyninfo_end;
       dyninfo_p += sizeof (Elfalpha_External_Dyn))
    {
      Elfalpha_External_Dyn *x_dynp = (Elfalpha_External_Dyn *) dyninfo_p;
      long dyn_tag;

      dyn_tag = bfd_h_get_32 (abfd, (bfd_byte *) x_dynp->d_tag);
      if (dyn_tag == DT_NULL)
	break;
      else if (dyn_tag == DT_MIPS_LOCAL_GOTNO)
	{
	  if (dt_mips_local_gotno < 0)
	    dt_mips_local_gotno
	      = bfd_h_get_32 (abfd, (bfd_byte *) x_dynp->d_un.d_val);
	}
      else if (dyn_tag == DT_MIPS_GOTSYM)
	{
	  if (dt_mips_gotsym < 0)
	    dt_mips_gotsym
	      = bfd_h_get_32 (abfd, (bfd_byte *) x_dynp->d_un.d_val);
	}
    }
  if (dt_mips_local_gotno < 0 || dt_mips_gotsym < 0)
    return;

  /* Scan all dynamic symbols and enter them into the minimal symbol
     table if appropriate.  */
  sym_count = sym_secsize / sizeof (Elfalpha_External_Sym);
  stripped = (bfd_get_symcount (abfd) == 0);

  /* Skip first symbol, which is a null dummy.  */
  for (i = 1, x_symp = (Elfalpha_External_Sym *) sym_secptr + 1;
       i < sym_count;
       i++, x_symp++)
    {
      unsigned long strx;
      char *name;
      bfd_vma sym_value;
      unsigned char sym_info;
      unsigned int sym_shndx;
      int isglobal;
      enum minimal_symbol_type ms_type;

      strx = bfd_h_get_32 (abfd, (bfd_byte *) x_symp->st_name);
      if (strx >= str_secsize)
	continue;
      name = str_secptr + strx;
      if (*name == '\0' || *name == '.')
	continue;

      sym_value = bfd_h_get_64 (abfd, (bfd_byte *) x_symp->st_value);
      sym_info = bfd_h_get_8 (abfd, (bfd_byte *) x_symp->st_info);
      sym_shndx = bfd_h_get_16 (abfd, (bfd_byte *) x_symp->st_shndx);
      if (sym_shndx >= (SHN_LORESERVE & 0xffff))
	sym_shndx += SHN_LORESERVE - (SHN_LORESERVE & 0xffff);
      isglobal = (ELF_ST_BIND (sym_info) == STB_GLOBAL);

      if (sym_shndx == SHN_UNDEF)
	{
	  /* Handle undefined functions which are defined in a shared
	     library.  */
	  if (ELF_ST_TYPE (sym_info) != STT_FUNC
	      || ELF_ST_BIND (sym_info) != STB_GLOBAL)
	    continue;

	  ms_type = mst_solib_trampoline;

	  /* If sym_value is nonzero, it points to the shared library
	     trampoline entry, which is what we are looking for.

	     If sym_value is zero, then we have to get the GOT entry
	     for the symbol.

	     If the GOT entry is nonzero, it represents the quickstart
	     address of the function and we use that as the symbol
	     value.

	     If the GOT entry is zero, the function address has to be
	     resolved by the runtime loader before the executable is
	     started.  We are unable to find any meaningful address
	     for these functions in the executable file, so we skip
	     them.  */
	  if (sym_value == 0)
	    {
	      int got_entry_offset =
		(i - dt_mips_gotsym + dt_mips_local_gotno) * got_entry_size;

	      if (got_entry_offset < 0 || got_entry_offset >= got_secsize)
		continue;
	      sym_value =
		bfd_h_get_64 (abfd,
			      (bfd_byte *) (got_secptr + got_entry_offset));
	      if (sym_value == 0)
		continue;
	    }
	}
      else
	{
	  /* Symbols defined in the executable itself. We only care
	     about them if this is a stripped executable, otherwise
	     they have been retrieved from the normal symbol table
	     already.  */
	  if (!stripped)
	    continue;

	  if (sym_shndx == SHN_MIPS_TEXT)
	    {
	      if (isglobal)
		ms_type = mst_text;
	      else
		ms_type = mst_file_text;
	      sym_value += ANOFFSET (section_offsets, SECT_OFF_TEXT (objfile));
	    }
	  else if (sym_shndx == SHN_MIPS_DATA)
	    {
	      if (isglobal)
		ms_type = mst_data;
	      else
		ms_type = mst_file_data;
	      sym_value += ANOFFSET (section_offsets, SECT_OFF_DATA (objfile));
	    }
	  else if (sym_shndx == SHN_MIPS_ACOMMON)
	    {
	      if (isglobal)
		ms_type = mst_bss;
	      else
		ms_type = mst_file_bss;
	      sym_value += ANOFFSET (section_offsets, SECT_OFF_BSS (objfile));
	    }
	  else if (sym_shndx == SHN_ABS)
	    {
	      ms_type = mst_abs;
	    }
	  else
	    {
	      continue;
	    }
	}

      prim_record_minimal_symbol (name, sym_value, ms_type, objfile);
    }

  do_cleanups (cleanups);
}
static void
som_symtab_read (bfd *abfd, struct objfile *objfile,
                 struct section_offsets *section_offsets)
{
    struct cleanup *cleanup;
    struct gdbarch *gdbarch = get_objfile_arch (objfile);
    unsigned int number_of_symbols;
    int val, dynamic;
    char *stringtab;
    asection *shlib_info;
    struct som_external_symbol_dictionary_record *buf, *bufp, *endbufp;
    char *symname;
    const int symsize = sizeof (struct som_external_symbol_dictionary_record);


    number_of_symbols = bfd_get_symcount (abfd);

    /* Allocate a buffer to read in the debug info.
       We avoid using alloca because the memory size could be so large
       that we could hit the stack size limit.  */
    buf = xmalloc (symsize * number_of_symbols);
    cleanup = make_cleanup (xfree, buf);
    bfd_seek (abfd, obj_som_sym_filepos (abfd), SEEK_SET);
    val = bfd_bread (buf, symsize * number_of_symbols, abfd);
    if (val != symsize * number_of_symbols)
        error (_("Couldn't read symbol dictionary!"));

    /* Allocate a buffer to read in the som stringtab section of
       the debugging info.  Again, we avoid using alloca because
       the data could be so large that we could potentially hit
       the stack size limitat.  */
    stringtab = xmalloc (obj_som_stringtab_size (abfd));
    make_cleanup (xfree, stringtab);
    bfd_seek (abfd, obj_som_str_filepos (abfd), SEEK_SET);
    val = bfd_bread (stringtab, obj_som_stringtab_size (abfd), abfd);
    if (val != obj_som_stringtab_size (abfd))
        error (_("Can't read in HP string table."));

    /* We need to determine if objfile is a dynamic executable (so we
       can do the right thing for ST_ENTRY vs ST_CODE symbols).

       There's nothing in the header which easily allows us to do
       this.

       This code used to rely upon the existence of a $SHLIB_INFO$
       section to make this determination.  HP claims that it is
       more accurate to check for a nonzero text offset, but they
       have not provided any information about why that test is
       more accurate.  */
    dynamic = (ANOFFSET (section_offsets, SECT_OFF_TEXT (objfile)) != 0);

    endbufp = buf + number_of_symbols;
    for (bufp = buf; bufp < endbufp; ++bufp)
    {
        enum minimal_symbol_type ms_type;
        unsigned int flags = bfd_getb32 (bufp->flags);
        unsigned int symbol_type
            = (flags >> SOM_SYMBOL_TYPE_SH) & SOM_SYMBOL_TYPE_MASK;
        unsigned int symbol_scope
            = (flags >> SOM_SYMBOL_SCOPE_SH) & SOM_SYMBOL_SCOPE_MASK;
        CORE_ADDR symbol_value = bfd_getb32 (bufp->symbol_value);
        asection *section = NULL;

        QUIT;

        /* Compute the section.  */
        switch (symbol_scope)
        {
        case SS_EXTERNAL:
            if (symbol_type != ST_STORAGE)
                section = bfd_und_section_ptr;
            else
                section = bfd_com_section_ptr;
            break;

        case SS_UNSAT:
            if (symbol_type != ST_STORAGE)
                section = bfd_und_section_ptr;
            else
                section = bfd_com_section_ptr;
            break;

        case SS_UNIVERSAL:
            section = bfd_section_from_som_symbol (abfd, bufp);
            break;

        case SS_LOCAL:
            section = bfd_section_from_som_symbol (abfd, bufp);
            break;
        }

        switch (symbol_scope)
        {
        case SS_UNIVERSAL:
        case SS_EXTERNAL:
            switch (symbol_type)
            {
            case ST_SYM_EXT:
            case ST_ARG_EXT:
                continue;

            case ST_CODE:
            case ST_PRI_PROG:
            case ST_SEC_PROG:
            case ST_MILLICODE:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_text;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);
                break;

            case ST_ENTRY:
                symname = bfd_getb32 (bufp->name) + stringtab;
                /* For a dynamic executable, ST_ENTRY symbols are
                   the stubs, while the ST_CODE symbol is the real
                   function.  */
                if (dynamic)
                    ms_type = mst_solib_trampoline;
                else
                    ms_type = mst_text;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);
                break;

            case ST_STUB:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_solib_trampoline;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);
                break;

            case ST_DATA:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_data;
                break;
            default:
                continue;
            }
            break;

#if 0
        /* SS_GLOBAL and SS_LOCAL are two names for the same thing (!).  */
        case SS_GLOBAL:
#endif
        case SS_LOCAL:
            switch (symbol_type)
            {
            case ST_SYM_EXT:
            case ST_ARG_EXT:
                continue;

            case ST_CODE:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_file_text;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);

check_strange_names:
                /* Utah GCC 2.5, FSF GCC 2.6 and later generate correct local
                   label prefixes for stabs, constant data, etc.  So we need
                   only filter out L$ symbols which are left in due to
                   limitations in how GAS generates SOM relocations.

                   When linking in the HPUX C-library the HP linker has
                   the nasty habit of placing section symbols from the literal
                   subspaces in the middle of the program's text.  Filter
                   those out as best we can.  Check for first and last character
                   being '$'.

                   And finally, the newer HP compilers emit crud like $PIC_foo$N
                   in some circumstance (PIC code I guess).  It's also claimed
                   that they emit D$ symbols too.  What stupidity.  */
                if ((symname[0] == 'L' && symname[1] == '$')
                        || (symname[0] == '$' && symname[strlen (symname) - 1] == '$')
                        || (symname[0] == 'D' && symname[1] == '$')
                        || (strncmp (symname, "L0\001", 3) == 0)
                        || (strncmp (symname, "$PIC", 4) == 0))
                    continue;
                break;

            case ST_PRI_PROG:
            case ST_SEC_PROG:
            case ST_MILLICODE:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_file_text;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);
                break;

            case ST_ENTRY:
                symname = bfd_getb32 (bufp->name) + stringtab;
                /* SS_LOCAL symbols in a shared library do not have
                export stubs, so we do not have to worry about
                 using mst_file_text vs mst_solib_trampoline here like
                 we do for SS_UNIVERSAL and SS_EXTERNAL symbols above.  */
                ms_type = mst_file_text;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);
                break;

            case ST_STUB:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_solib_trampoline;
                symbol_value = gdbarch_addr_bits_remove (gdbarch, symbol_value);
                break;


            case ST_DATA:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_file_data;
                goto check_strange_names;

            default:
                continue;
            }
            break;

        /* This can happen for common symbols when -E is passed to the
           final link.  No idea _why_ that would make the linker force
           common symbols to have an SS_UNSAT scope, but it does.

           This also happens for weak symbols, but their type is
           ST_DATA.  */
        case SS_UNSAT:
            switch (symbol_type)
            {
            case ST_STORAGE:
            case ST_DATA:
                symname = bfd_getb32 (bufp->name) + stringtab;
                ms_type = mst_data;
                break;

            default:
                continue;
            }
            break;

        default:
            continue;
        }

        if (bfd_getb32 (bufp->name) > obj_som_stringtab_size (abfd))
            error (_("Invalid symbol data; bad HP string table offset: %s"),
                   plongest (bfd_getb32 (bufp->name)));

        if (bfd_is_const_section (section))
        {
            struct obj_section *iter;

            ALL_OBJFILE_OSECTIONS (objfile, iter)
            {
                CORE_ADDR start;
                CORE_ADDR len;

                if (bfd_is_const_section (iter->the_bfd_section))
                    continue;

                start = bfd_get_section_vma (iter->objfile->obfd,
                                             iter->the_bfd_section);
                len = bfd_get_section_size (iter->the_bfd_section);
                if (start <= symbol_value && symbol_value < start + len)
                {
                    section = iter->the_bfd_section;
                    break;
                }
            }
        }

        prim_record_minimal_symbol_and_info (symname, symbol_value, ms_type,
                                             gdb_bfd_section_index (objfile->obfd,
                                                     section),
                                             objfile);
    }
Exemple #11
0
static int
amd64_windows_find_unwind_info (struct gdbarch *gdbarch, CORE_ADDR pc,
				CORE_ADDR *unwind_info,
				CORE_ADDR *image_base,
				CORE_ADDR *start_rva,
				CORE_ADDR *end_rva)
{
  struct obj_section *sec;
  pe_data_type *pe;
  IMAGE_DATA_DIRECTORY *dir;
  struct objfile *objfile;
  unsigned long lo, hi;
  CORE_ADDR base;
  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

  /* Get the corresponding exception directory.  */
  sec = find_pc_section (pc);
  if (sec == NULL)
    return -1;
  objfile = sec->objfile;
  pe = pe_data (sec->objfile->obfd);
  dir = &pe->pe_opthdr.DataDirectory[PE_EXCEPTION_TABLE];

  base = pe->pe_opthdr.ImageBase
    + ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
  *image_base = base;

  /* Find the entry.

     Note: This does not handle dynamically added entries (for JIT
     engines).  For this, we would need to ask the kernel directly,
     which means getting some info from the native layer.  For the
     rest of the code, however, it's probably faster to search
     the entry ourselves.  */
  lo = 0;
  hi = dir->Size / sizeof (struct external_pex64_runtime_function);
  *unwind_info = 0;
  while (lo <= hi)
    {
      unsigned long mid = lo + (hi - lo) / 2;
      struct external_pex64_runtime_function d;
      CORE_ADDR sa, ea;

      if (target_read_memory (base + dir->VirtualAddress + mid * sizeof (d),
			      (gdb_byte *) &d, sizeof (d)) != 0)
	return -1;

      sa = extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
      ea = extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
      if (pc < base + sa)
	hi = mid - 1;
      else if (pc >= base + ea)
	lo = mid + 1;
      else if (pc >= base + sa && pc < base + ea)
	{
	  /* Got it.  */
	  *start_rva = sa;
	  *end_rva = ea;
	  *unwind_info =
	    extract_unsigned_integer (d.rva_UnwindData, 4, byte_order);
	  break;
	}
      else
	break;
    }

  if (frame_debug)
    fprintf_unfiltered
      (gdb_stdlog,
       "amd64_windows_find_unwind_data:  image_base=%s, unwind_data=%s\n",
       paddress (gdbarch, base), paddress (gdbarch, *unwind_info));

  if (*unwind_info & 1)
    {
      /* Unofficially documented unwind info redirection, when UNWIND_INFO
	 address is odd (http://www.codemachine.com/article_x64deepdive.html).
      */
      struct external_pex64_runtime_function d;
      CORE_ADDR sa, ea;

      if (target_read_memory (base + (*unwind_info & ~1),
			      (gdb_byte *) &d, sizeof (d)) != 0)
	return -1;

      *start_rva =
	extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
      *end_rva = extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
      *unwind_info =
	extract_unsigned_integer (d.rva_UnwindData, 4, byte_order);

    }
  return 0;
}