Beispiel #1
0
/* Write the contents of the object attributes section (length SIZE)
   for VENDOR to CONTENTS.  */
static void
vendor_set_obj_attr_contents (bfd *abfd, bfd_byte *contents, bfd_vma size,
			      int vendor)
{
  bfd_byte *p;
  obj_attribute *attr;
  obj_attribute_list *list;
  int i;
  const char *vendor_name = vendor_obj_attr_name (abfd, vendor);
  size_t vendor_length = strlen (vendor_name) + 1;

  p = contents;
  bfd_put_32 (abfd, size, p);
  p += 4;
  memcpy (p, vendor_name, vendor_length);
  p += vendor_length;
  *(p++) = Tag_File;
  bfd_put_32 (abfd, size - 4 - vendor_length, p);
  p += 4;

  attr = elf_known_obj_attributes (abfd)[vendor];
  for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
    {
      int tag = i;
      if (get_elf_backend_data (abfd)->obj_attrs_order)
	tag = get_elf_backend_data (abfd)->obj_attrs_order (i);
      p = write_obj_attribute (p, tag, &attr[tag]);
    }

  for (list = elf_other_obj_attributes (abfd)[vendor];
       list;
       list = list->next)
    p = write_obj_attribute (p, list->tag, &list->attr);
}
Beispiel #2
0
/* Return the vendor name for a given object attributes section.  */
static const char *
vendor_obj_attr_name (bfd *abfd, int vendor)
{
  return (vendor == OBJ_ATTR_PROC
	  ? get_elf_backend_data (abfd)->obj_attrs_vendor
	  : "gnu");
}
Beispiel #3
0
bfd_boolean
_bfd_elf_merge_unknown_attribute_low (bfd *ibfd, bfd *obfd, int tag)
{
  obj_attribute *in_attr;
  obj_attribute *out_attr;
  bfd *err_bfd = NULL;
  bfd_boolean result = TRUE;

  in_attr = elf_known_obj_attributes_proc (ibfd);
  out_attr = elf_known_obj_attributes_proc (obfd);

  if (out_attr[tag].i != 0 || out_attr[tag].s != NULL)
    err_bfd = obfd;
  else if (in_attr[tag].i != 0 || in_attr[tag].s != NULL)
    err_bfd = ibfd;

  if (err_bfd != NULL)
    result
      = get_elf_backend_data (err_bfd)->obj_attrs_handle_unknown (err_bfd, tag);

  /* Only pass on attributes that match in both inputs.  */
  if (in_attr[tag].i != out_attr[tag].i
      || (in_attr[tag].s == NULL) != (out_attr[tag].s == NULL)
      || (in_attr[tag].s != NULL && out_attr[tag].s != NULL
	  && strcmp (in_attr[tag].s, out_attr[tag].s) != 0))
    {
      out_attr[tag].i = 0;
      out_attr[tag].s = NULL;
    }

  return result;
}
Beispiel #4
0
int
bfd_get_arch_size (bfd *abfd)
{
  if (abfd->xvec->flavour == bfd_target_elf_flavour)
    return get_elf_backend_data (abfd)->s->arch_size;

  return bfd_arch_bits_per_address (abfd) > 32 ? 64 : 32;
}
Beispiel #5
0
int
bfd_get_sign_extend_vma (bfd *abfd)
{
  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
    return get_elf_backend_data (abfd)->sign_extend_vma;

  bfd_set_error (bfd_error_wrong_format);
  return -1;
}
Beispiel #6
0
static bfd_boolean
is32bit (bfd *abfd)
{
  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
    {
      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
      return bed->s->elfclass == ELFCLASS32;
    }

  /* For non-ELF targets, use architecture information.  */
  return bfd_arch_bits_per_address (abfd) <= 32;
}
bfd_size_type
_bfd_elf_convert_gnu_property_size (bfd *ibfd, bfd *obfd)
{
  unsigned int align_size;
  const struct elf_backend_data *bed;
  elf_property_list *list = elf_properties (ibfd);

  bed = get_elf_backend_data (obfd);
  align_size = bed->s->elfclass == ELFCLASS64 ? 8 : 4;

  /* Get the output .note.gnu.property section size.  */
  return elf_get_gnu_property_section_size (list, align_size);
}
Beispiel #8
0
/* Determine what arguments an attribute tag takes.  */
int
_bfd_elf_obj_attrs_arg_type (bfd *abfd, int vendor, int tag)
{
  switch (vendor)
    {
    case OBJ_ATTR_PROC:
      return get_elf_backend_data (abfd)->obj_attrs_arg_type (tag);
      break;
    case OBJ_ATTR_GNU:
      return gnu_obj_attrs_arg_type (tag);
      break;
    default:
      abort ();
    }
}
Beispiel #9
0
static bfd_boolean
s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
{
  flagword flags;
  asection *s;
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  struct elf_link_hash_table *htab = elf_hash_table (info);

  if (htab->iplt != NULL)
    return TRUE;

  flags = bed->dynamic_sec_flags;

  if (bfd_link_pic (info))
    {
      s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
				       flags | SEC_READONLY);
      if (s == NULL
	  || ! bfd_set_section_alignment (abfd, s,
					  bed->s->log_file_align))
	return FALSE;
      htab->irelifunc = s;
    }

  /* Create .iplt, .rel[a].iplt, and .igot.plt.  */
  s = bfd_make_section_with_flags (abfd, ".iplt",
				   flags | SEC_CODE | SEC_READONLY);
  if (s == NULL
      || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
    return FALSE;
  htab->iplt = s;

  s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
  if (s == NULL
      || ! bfd_set_section_alignment (abfd, s,
				      bed->s->log_file_align))
    return FALSE;
  htab->irelplt = s;

  s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
  if (s == NULL
      || !bfd_set_section_alignment (abfd, s,
				     bed->s->log_file_align))
    return FALSE;
  htab->igotplt = s;

  return TRUE;
}
Beispiel #10
0
asection *
_bfd_elf_create_ifunc_dyn_reloc (bfd *abfd, struct bfd_link_info *info,
				 asection *sec, asection *sreloc,
				 struct elf_dyn_relocs **head)
{
  struct elf_dyn_relocs *p;
  struct elf_link_hash_table *htab = elf_hash_table (info);

  if (sreloc == NULL)
    {
      const struct elf_backend_data *bed = get_elf_backend_data (abfd);

      if (htab->dynobj == NULL)
	htab->dynobj = abfd;

      sreloc = _bfd_elf_make_dynamic_reloc_section (sec, htab->dynobj,
						    bed->s->log_file_align,
						    abfd,
						    bed->rela_plts_and_copies_p); 
      if (sreloc == NULL)
	return NULL;
    }
		      
  p = *head;
  if (p == NULL || p->sec != sec)
    {
      bfd_size_type amt = sizeof *p;

      p = ((struct elf_dyn_relocs *) bfd_alloc (htab->dynobj, amt));
      if (p == NULL)
	return NULL;
      p->next = *head;
      *head = p;
      p->sec = sec;
      p->count = 0;
      p->pc_count = 0;
    }
  p->count += 1;

  return sreloc;
}
static bfd_boolean
elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
			  elf_property *aprop, elf_property *bprop)
{
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  unsigned int pr_type = aprop != NULL ? aprop->pr_type : bprop->pr_type;

  if (bed->merge_gnu_properties != NULL
      && pr_type >= GNU_PROPERTY_LOPROC
      && pr_type < GNU_PROPERTY_LOUSER)
    return bed->merge_gnu_properties (info, abfd, aprop, bprop);

  switch (pr_type)
    {
    case GNU_PROPERTY_STACK_SIZE:
      if (aprop != NULL && bprop != NULL)
	{
	  if (bprop->u.number > aprop->u.number)
	    {
	      aprop->u.number = bprop->u.number;
	      return TRUE;
	    }
	  break;
	}
      /* FALLTHROUGH */

    case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
      /* Return TRUE if APROP is NULL to indicate that BPROP should
	 be added to ABFD.  */
      return aprop == NULL;

    default:
      /* Never should happen.  */
      abort ();
    }

  return FALSE;
}
bfd_boolean
_bfd_elf_convert_gnu_properties (bfd *ibfd, asection *isec,
				 bfd *obfd, bfd_byte **ptr,
				 bfd_size_type *ptr_size)
{
  unsigned int size;
  bfd_byte *contents;
  unsigned int align_shift;
  const struct elf_backend_data *bed;
  elf_property_list *list = elf_properties (ibfd);

  bed = get_elf_backend_data (obfd);
  align_shift = bed->s->elfclass == ELFCLASS64 ? 3 : 2;

  /* Get the output .note.gnu.property section size.  */
  size = bfd_get_section_size (isec->output_section);

  /* Update the output .note.gnu.property section alignment.  */
  bfd_set_section_alignment (obfd, isec->output_section, align_shift);

  if (size > bfd_get_section_size (isec))
    {
      contents = (bfd_byte *) bfd_malloc (size);
      free (*ptr);
      *ptr = contents;
    }
  else
    contents = *ptr;

  *ptr_size = size;

  /* Generate the output .note.gnu.property section.  */
  elf_write_gnu_properties (ibfd, contents, list, size, 1 << align_shift);

  return TRUE;
}
Beispiel #13
0
/* We permute the segment_map to get BFD to do the file layout we want:
   The first non-executable PT_LOAD segment appears first in the file
   and contains the ELF file header and phdrs.  */
bfd_boolean
nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
{
    const struct elf_backend_data *const bed = get_elf_backend_data (abfd);
    struct elf_segment_map **m = &elf_seg_map (abfd);
    struct elf_segment_map **first_load = NULL;
    struct elf_segment_map **last_load = NULL;
    bfd_boolean moved_headers = FALSE;
    int sizeof_headers;

    if (info != NULL && info->user_phdrs)
        /* The linker script used PHDRS explicitly, so don't change what the
           user asked for.  */
        return TRUE;

    if (info != NULL)
        /* We're doing linking, so evalute SIZEOF_HEADERS as in a linker script.  */
        sizeof_headers = bfd_sizeof_headers (abfd, info);
    else
    {
        /* We're not doing linking, so this is objcopy or suchlike.
        We just need to collect the size of the existing headers.  */
        struct elf_segment_map *seg;
        sizeof_headers = bed->s->sizeof_ehdr;
        for (seg = *m; seg != NULL; seg = seg->next)
            sizeof_headers += bed->s->sizeof_phdr;
    }

    while (*m != NULL)
    {
        struct elf_segment_map *seg = *m;

        if (seg->p_type == PT_LOAD)
        {
            bfd_boolean executable = segment_executable (seg);

            if (executable
                    && seg->count > 0
                    && seg->sections[0]->vma % bed->minpagesize == 0)
            {
                asection *lastsec = seg->sections[seg->count - 1];
                bfd_vma end = lastsec->vma + lastsec->size;
                if (end % bed->minpagesize != 0)
                {
                    /* This is an executable segment that starts on a page
                       boundary but does not end on a page boundary.  Fill
                       it out to a whole page with code fill (the tail of
                       the segment will not be within any section).  Thus
                       the entire code segment can be mapped from the file
                       as whole pages and that mapping will contain only
                       valid instructions.

                       To accomplish this, we must fake out the code in
                       assign_file_positions_for_load_sections (elf.c) so
                       that it advances past the rest of the final page,
                       rather than trying to put the next (unaligned, or
                       unallocated) section.  We do this by appending a
                       dummy section record to this element in the segment
                       map.  No such output section ever actually exists,
                       but this gets the layout logic to advance the file
                       positions past this partial page.  Since we are
                       lying to BFD like this, nothing will ever know to
                       write the section contents.  So we do that by hand
                       after the fact, in nacl_final_write_processing, below.  */

                    struct elf_segment_map *newseg;
                    asection *sec;
                    struct bfd_elf_section_data *secdata;

                    BFD_ASSERT (!seg->p_size_valid);

                    secdata = bfd_zalloc (abfd, sizeof *secdata);
                    if (secdata == NULL)
                        return FALSE;

                    sec = bfd_zalloc (abfd, sizeof *sec);
                    if (sec == NULL)
                        return FALSE;

                    /* Fill in only the fields that actually affect the logic
                       in assign_file_positions_for_load_sections.  */
                    sec->vma = end;
                    sec->lma = lastsec->lma + lastsec->size;
                    sec->size = bed->minpagesize - (end % bed->minpagesize);
                    sec->flags = (SEC_ALLOC | SEC_LOAD
                                  | SEC_READONLY | SEC_CODE | SEC_LINKER_CREATED);
                    sec->used_by_bfd = secdata;

                    secdata->this_hdr.sh_type = SHT_PROGBITS;
                    secdata->this_hdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR;
                    secdata->this_hdr.sh_addr = sec->vma;
                    secdata->this_hdr.sh_size = sec->size;

                    newseg = bfd_alloc (abfd,
                                        sizeof *newseg + ((seg->count + 1)
                                                          * sizeof (asection *)));
                    if (newseg == NULL)
                        return FALSE;
                    memcpy (newseg, seg,
                            sizeof *newseg + (seg->count * sizeof (asection *)));
                    newseg->sections[newseg->count++] = sec;
                    *m = seg = newseg;
                }
            }

            /* First, we're just finding the earliest PT_LOAD.
               By the normal rules, this will be the lowest-addressed one.
               We only have anything interesting to do if it's executable.  */
            last_load = m;
            if (first_load == NULL)
            {
                if (!executable)
                    goto next;
                first_load = m;
            }
            /* Now that we've noted the first PT_LOAD, we're looking for
               the first non-executable PT_LOAD with a nonempty p_filesz.  */
            else if (!moved_headers
                     && segment_eligible_for_headers (seg, bed->minpagesize,
                             sizeof_headers))
            {
                /* This is the one we were looking for!

                First, clear the flags on previous segments that
                 say they include the file header and phdrs.  */
                struct elf_segment_map *prevseg;
                for (prevseg = *first_load;
                        prevseg != seg;
                        prevseg = prevseg->next)
                    if (prevseg->p_type == PT_LOAD)
                    {
                        prevseg->includes_filehdr = 0;
                        prevseg->includes_phdrs = 0;
                    }

                /* This segment will include those headers instead.  */
                seg->includes_filehdr = 1;
                seg->includes_phdrs = 1;

                moved_headers = TRUE;
            }
        }

next:
        m = &seg->next;
    }

    if (first_load != last_load && moved_headers)
    {
        /* Now swap the first and last PT_LOAD segments'
        positions in segment_map.  */
        struct elf_segment_map *first = *first_load;
        struct elf_segment_map *last = *last_load;
        *first_load = first->next;
        first->next = last->next;
        last->next = first;
    }

    return TRUE;
}
Beispiel #14
0
bfd_boolean
_bfd_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
{
  flagword flags, pltflags;
  asection *s;
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  struct elf_link_hash_table *htab = elf_hash_table (info);

  if (htab->irelifunc != NULL || htab->iplt != NULL)
    return TRUE;

  flags = bed->dynamic_sec_flags;
  pltflags = flags;
  if (bed->plt_not_loaded)
    /* We do not clear SEC_ALLOC here because we still want the OS to
       allocate space for the section; it's just that there's nothing
       to read in from the object file.  */
    pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
  else
    pltflags |= SEC_ALLOC | SEC_CODE | SEC_LOAD;
  if (bed->plt_readonly)
    pltflags |= SEC_READONLY;

  if (info->shared)
    {
      /* We need to create .rel[a].ifunc for shared objects.  */
      const char *rel_sec = (bed->rela_plts_and_copies_p
			     ? ".rela.ifunc" : ".rel.ifunc");

      s = bfd_make_section_with_flags (abfd, rel_sec,
				       flags | SEC_READONLY);
      if (s == NULL
	  || ! bfd_set_section_alignment (abfd, s,
					  bed->s->log_file_align))
	return FALSE;
      htab->irelifunc = s;
    }
  else
    {
      /* We need to create .iplt, .rel[a].iplt, .igot and .igot.plt
	 for static executables.   */
      s = bfd_make_section_with_flags (abfd, ".iplt", pltflags);
      if (s == NULL
	  || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
	return FALSE;
      htab->iplt = s;

      s = bfd_make_section_with_flags (abfd,
				       (bed->rela_plts_and_copies_p
					? ".rela.iplt" : ".rel.iplt"),
				       flags | SEC_READONLY);
      if (s == NULL
	  || ! bfd_set_section_alignment (abfd, s,
					  bed->s->log_file_align))
	return FALSE;
      htab->irelplt = s;

      /* We don't need the .igot section if we have the .igot.plt
	 section.  */
      if (bed->want_got_plt)
	s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
      else
	s = bfd_make_section_with_flags (abfd, ".igot", flags);
      if (s == NULL
	  || !bfd_set_section_alignment (abfd, s,
					 bed->s->log_file_align))
	return FALSE;
      htab->igotplt = s;
    }

  return TRUE;
}
Beispiel #15
0
bfd_boolean
_bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
				    struct elf_link_hash_entry *h,
				    struct elf_dyn_relocs **head,
				    unsigned int plt_entry_size,
				    unsigned int got_entry_size)
{
  asection *plt, *gotplt, *relplt;
  struct elf_dyn_relocs *p;
  unsigned int sizeof_reloc;
  const struct elf_backend_data *bed;
  struct elf_link_hash_table *htab;

  /* When a shared library references a STT_GNU_IFUNC symbol defined
     in executable, the address of the resolved function may be used.
     But in non-shared executable, the address of its .plt slot may
     be used.  Pointer equality may not work correctly.  PIE should
     be used if pointer equality is required here.  */
  if (!info->shared
      && (h->dynindx != -1
	  || info->export_dynamic)
      && h->pointer_equality_needed)
    {
      info->callbacks->einfo 
	(_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
	   "equality in `%B' can not be used when making an "
	   "executable; recompile with -fPIE and relink with -pie\n"),
	 h->root.root.string,
	 h->root.u.def.section->owner);
      bfd_set_error (bfd_error_bad_value);
      return FALSE;
    }

  htab = elf_hash_table (info);

  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
    {
      /* When building shared library, we need to handle the case
         where it is marked with regular reference, but not non-GOT
	 reference.  It may happen if we didn't see STT_GNU_IFUNC
	 symbol at the time when checking relocations.  */
      bfd_size_type count = 0;

      if (info->shared
	  && !h->non_got_ref
	  && h->ref_regular)
	{
	  for (p = *head; p != NULL; p = p->next)
	    count += p->count;
	  if (count != 0)
	    h->non_got_ref = 1;
	}

      if (count == 0)
	{
	  h->got = htab->init_got_offset;
	  h->plt = htab->init_plt_offset;
	  *head = NULL;
	  return TRUE;
	}
    }

  /* Return and discard space for dynamic relocations against it if
     it is never referenced in a non-shared object.  */
  if (!h->ref_regular)
    {
      if (h->plt.refcount > 0
	  || h->got.refcount > 0)
	abort ();
      h->got = htab->init_got_offset;
      h->plt = htab->init_plt_offset;
      *head = NULL;
      return TRUE;
    }

  bed = get_elf_backend_data (info->output_bfd);
  if (bed->rela_plts_and_copies_p)
    sizeof_reloc = bed->s->sizeof_rela;
  else
    sizeof_reloc = bed->s->sizeof_rel;

  /* When building a static executable, use .iplt, .igot.plt and
     .rel[a].iplt sections for STT_GNU_IFUNC symbols.  */
  if (htab->splt != NULL)
    {
      plt = htab->splt;
      gotplt = htab->sgotplt;
      relplt = htab->srelplt;

      /* If this is the first .plt entry, make room for the special
	 first entry.  */
      if (plt->size == 0)
	plt->size += plt_entry_size;
    }
  else
    {
      plt = htab->iplt;
      gotplt = htab->igotplt;
      relplt = htab->irelplt;
    }

  /* Don't update value of STT_GNU_IFUNC symbol to PLT.  We need
     the original value for R_*_IRELATIVE.  */  
  h->plt.offset = plt->size;

  /* Make room for this entry in the .plt/.iplt section.  */
  plt->size += plt_entry_size;

  /* We also need to make an entry in the .got.plt/.got.iplt section,
     which will be placed in the .got section by the linker script.  */
  gotplt->size += got_entry_size;

  /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt
     section.  */
  relplt->size += sizeof_reloc;
  relplt->reloc_count++;

  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
     there is a non-GOT reference in a shared object.  */
  if (!info->shared
      || !h->non_got_ref)
    *head = NULL;

  /* Finally, allocate space.  */
  for (p = *head; p != NULL; p = p->next)
    htab->irelifunc->size += p->count * sizeof_reloc;

  /* For STT_GNU_IFUNC symbol, .got.plt has the real function addres
     and .got has the PLT entry adddress.  We will load the GOT entry
     with the PLT entry in finish_dynamic_symbol if it is used.  For
     branch, it uses .got.plt.  For symbol value,
     1. Use .got.plt in a shared object if it is forced local or not
     dynamic.
     2. Use .got.plt in a non-shared object if pointer equality isn't
     needed.
     3. Use .got.plt in PIE.
     4. Use .got.plt if .got isn't used.
     5. Otherwise use .got so that it can be shared among different
     objects at run-time.
     We only need to relocate .got entry in shared object.  */
  if ((info->shared
       && (h->dynindx == -1
	   || h->forced_local))
      || (!info->shared
	  && !h->pointer_equality_needed)
      || (info->executable && info->shared)
      || htab->sgot == NULL)
    {
      /* Use .got.plt.  */
      h->got.offset = (bfd_vma) -1;
    }
  else
    {
      h->got.offset = htab->sgot->size;
      htab->sgot->size += got_entry_size;
      if (info->shared)
	htab->srelgot->size += sizeof_reloc;
    }

  return TRUE;
}
Beispiel #16
0
bfd_boolean
_bfd_elf_discard_section_eh_frame
   (bfd *abfd, struct bfd_link_info *info, asection *sec,
    bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
    struct elf_reloc_cookie *cookie)
{
#define REQUIRE(COND)					\
  do							\
    if (!(COND))					\
      goto free_no_table;				\
  while (0)

  bfd_byte *ehbuf = NULL, *buf;
  bfd_byte *last_fde;
  struct eh_cie_fde *ent, *this_inf;
  unsigned int hdr_length, hdr_id;
  struct extended_cie
    {
      struct cie cie;
      unsigned int offset;
      unsigned int usage_count;
      unsigned int entry;
    } *ecies = NULL, *ecie;
  unsigned int ecie_count = 0, ecie_alloced = 0;
  struct cie *cie;
  struct elf_link_hash_table *htab;
  struct eh_frame_hdr_info *hdr_info;
  struct eh_frame_sec_info *sec_info = NULL;
  unsigned int offset;
  unsigned int ptr_size;
  unsigned int entry_alloced;

  if (sec->size == 0)
    {
      /* This file does not contain .eh_frame information.  */
      return FALSE;
    }

  if (bfd_is_abs_section (sec->output_section))
    {
      /* At least one of the sections is being discarded from the
	 link, so we should just ignore them.  */
      return FALSE;
    }

  htab = elf_hash_table (info);
  hdr_info = &htab->eh_info;

  if (hdr_info->cies == NULL && !info->relocatable)
    hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free);

  /* Read the frame unwind information from abfd.  */

  REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));

  if (sec->size >= 4
      && bfd_get_32 (abfd, ehbuf) == 0
      && cookie->rel == cookie->relend)
    {
      /* Empty .eh_frame section.  */
      free (ehbuf);
      return FALSE;
    }

  /* If .eh_frame section size doesn't fit into int, we cannot handle
     it (it would need to use 64-bit .eh_frame format anyway).  */
  REQUIRE (sec->size == (unsigned int) sec->size);

  ptr_size = (get_elf_backend_data (abfd)
	      ->elf_backend_eh_frame_address_size (abfd, sec));
  REQUIRE (ptr_size != 0);

  buf = ehbuf;
  sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
			  + 99 * sizeof (struct eh_cie_fde));
  REQUIRE (sec_info);

  entry_alloced = 100;

#define ENSURE_NO_RELOCS(buf)				\
  REQUIRE (!(cookie->rel < cookie->relend		\
	     && (cookie->rel->r_offset			\
		 < (bfd_size_type) ((buf) - ehbuf))	\
	     && cookie->rel->r_info != 0))

#define SKIP_RELOCS(buf)				\
  while (cookie->rel < cookie->relend			\
	 && (cookie->rel->r_offset			\
	     < (bfd_size_type) ((buf) - ehbuf)))	\
    cookie->rel++

#define GET_RELOC(buf)					\
  ((cookie->rel < cookie->relend			\
    && (cookie->rel->r_offset				\
	== (bfd_size_type) ((buf) - ehbuf)))		\
   ? cookie->rel : NULL)

  for (;;)
    {
      char *aug;
      bfd_byte *start, *end, *insns, *insns_end;
      bfd_size_type length;
      unsigned int set_loc_count;

      if (sec_info->count == entry_alloced)
	{
	  sec_info = bfd_realloc (sec_info,
				  sizeof (struct eh_frame_sec_info)
				  + ((entry_alloced + 99)
				     * sizeof (struct eh_cie_fde)));
	  REQUIRE (sec_info);

	  memset (&sec_info->entry[entry_alloced], 0,
		  100 * sizeof (struct eh_cie_fde));
	  entry_alloced += 100;
	}

      this_inf = sec_info->entry + sec_info->count;
      last_fde = buf;

      if ((bfd_size_type) (buf - ehbuf) == sec->size)
	break;

      /* Read the length of the entry.  */
      REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
      hdr_length = bfd_get_32 (abfd, buf - 4);

      /* 64-bit .eh_frame is not supported.  */
      REQUIRE (hdr_length != 0xffffffff);

      /* The CIE/FDE must be fully contained in this input section.  */
      REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr_length <= sec->size);
      end = buf + hdr_length;

      this_inf->offset = last_fde - ehbuf;
      this_inf->size = 4 + hdr_length;

      if (hdr_length == 0)
	{
	  /* A zero-length CIE should only be found at the end of
	     the section.  */
	  REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
	  ENSURE_NO_RELOCS (buf);
	  sec_info->count++;
	  break;
	}

      REQUIRE (skip_bytes (&buf, end, 4));
      hdr_id = bfd_get_32 (abfd, buf - 4);

      if (hdr_id == 0)
	{
	  unsigned int initial_insn_length;

	  /* CIE  */
	  this_inf->cie = 1;

	  if (ecie_count == ecie_alloced)
	    {
	      ecies = bfd_realloc (ecies,
				   (ecie_alloced + 20) * sizeof (*ecies));
	      REQUIRE (ecies);
	      memset (&ecies[ecie_alloced], 0, 20 * sizeof (*ecies));
	      ecie_alloced += 20;
	    }

	  cie = &ecies[ecie_count].cie;
	  ecies[ecie_count].offset = this_inf->offset;
	  ecies[ecie_count++].entry = sec_info->count;
	  cie->length = hdr_length;
	  start = buf;
	  REQUIRE (read_byte (&buf, end, &cie->version));

	  /* Cannot handle unknown versions.  */
	  REQUIRE (cie->version == 1 || cie->version == 3);
	  REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));

	  strcpy (cie->augmentation, (char *) buf);
	  buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
	  ENSURE_NO_RELOCS (buf);
	  if (buf[0] == 'e' && buf[1] == 'h')
	    {
	      /* GCC < 3.0 .eh_frame CIE */
	      /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
		 is private to each CIE, so we don't need it for anything.
		 Just skip it.  */
	      REQUIRE (skip_bytes (&buf, end, ptr_size));
	      SKIP_RELOCS (buf);
	    }
	  REQUIRE (read_uleb128 (&buf, end, &cie->code_align));
	  REQUIRE (read_sleb128 (&buf, end, &cie->data_align));
	  if (cie->version == 1)
	    {
	      REQUIRE (buf < end);
	      cie->ra_column = *buf++;
	    }
	  else
	    REQUIRE (read_uleb128 (&buf, end, &cie->ra_column));
	  ENSURE_NO_RELOCS (buf);
	  cie->lsda_encoding = DW_EH_PE_omit;
	  cie->fde_encoding = DW_EH_PE_omit;
	  cie->per_encoding = DW_EH_PE_omit;
	  aug = cie->augmentation;
	  if (aug[0] != 'e' || aug[1] != 'h')
	    {
	      if (*aug == 'z')
		{
		  aug++;
		  REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
	  	  ENSURE_NO_RELOCS (buf);
		}

	      while (*aug != '\0')
		switch (*aug++)
		  {
		  case 'L':
		    REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
		    ENSURE_NO_RELOCS (buf);
		    REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size));
		    break;
		  case 'R':
		    REQUIRE (read_byte (&buf, end, &cie->fde_encoding));
		    ENSURE_NO_RELOCS (buf);
		    REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size));
		    break;
		  case 'S':
		    break;
		  case 'P':
		    {
		      int per_width;

		      REQUIRE (read_byte (&buf, end, &cie->per_encoding));
		      per_width = get_DW_EH_PE_width (cie->per_encoding,
						      ptr_size);
		      REQUIRE (per_width);
		      if ((cie->per_encoding & 0xf0) == DW_EH_PE_aligned)
			{
			  length = -(buf - ehbuf) & (per_width - 1);
			  REQUIRE (skip_bytes (&buf, end, length));
			}
		      ENSURE_NO_RELOCS (buf);
		      /* Ensure we have a reloc here.  */
		      if (GET_RELOC (buf) != NULL)
			{
			  unsigned long r_symndx;

#ifdef BFD64
			  if (ptr_size == 8)
			    r_symndx = ELF64_R_SYM (cookie->rel->r_info);
			  else
#endif
			    r_symndx = ELF32_R_SYM (cookie->rel->r_info);
			  if (r_symndx >= cookie->locsymcount
			      || ELF_ST_BIND (cookie->locsyms[r_symndx]
					      .st_info) != STB_LOCAL)
			    {
			      struct elf_link_hash_entry *h;

			      r_symndx -= cookie->extsymoff;
			      h = cookie->sym_hashes[r_symndx];

			      while (h->root.type == bfd_link_hash_indirect
				     || h->root.type == bfd_link_hash_warning)
				h = (struct elf_link_hash_entry *)
				    h->root.u.i.link;

			      cie->personality.h = h;
			    }
			  else
			    {
			      Elf_Internal_Sym *sym;
			      asection *sym_sec;
			      bfd_vma val;

			      sym = &cookie->locsyms[r_symndx];
			      sym_sec = (bfd_section_from_elf_index
					 (abfd, sym->st_shndx));
			      if (sym_sec != NULL)
				{
				  if (sym_sec->kept_section != NULL)
				    sym_sec = sym_sec->kept_section;
				  if (sym_sec->output_section != NULL)
				    {
				      val = (sym->st_value
					     + sym_sec->output_offset
					     + sym_sec->output_section->vma);
				      cie->personality.val = val;
				      cie->local_personality = 1;
				    }
				}
			    }

			  /* Cope with MIPS-style composite relocations.  */
			  do
			    cookie->rel++;
			  while (GET_RELOC (buf) != NULL);
			}
		      REQUIRE (skip_bytes (&buf, end, per_width));
		      REQUIRE (cie->local_personality || cie->personality.h);
		    }
		    break;
		  default:
		    /* Unrecognized augmentation. Better bail out.  */
		    goto free_no_table;
		  }
	    }

	  /* For shared libraries, try to get rid of as many RELATIVE relocs
	     as possible.  */
	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_relative_eh_frame
		  (abfd, info, sec)))
	    {
	      if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr)
		cie->make_relative = 1;
	      /* If the CIE doesn't already have an 'R' entry, it's fairly
		 easy to add one, provided that there's no aligned data
		 after the augmentation string.  */
	      else if (cie->fde_encoding == DW_EH_PE_omit
		       && (cie->per_encoding & 0xf0) != DW_EH_PE_aligned)
		{
		  if (*cie->augmentation == 0)
		    this_inf->add_augmentation_size = 1;
		  this_inf->add_fde_encoding = 1;
		  cie->make_relative = 1;
		}
	    }

	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_lsda_relative_eh_frame
		  (abfd, info, sec))
	      && (cie->lsda_encoding & 0xf0) == DW_EH_PE_absptr)
	    cie->make_lsda_relative = 1;

	  /* If FDE encoding was not specified, it defaults to
	     DW_EH_absptr.  */
	  if (cie->fde_encoding == DW_EH_PE_omit)
	    cie->fde_encoding = DW_EH_PE_absptr;

	  initial_insn_length = end - buf;
	  if (initial_insn_length <= sizeof (cie->initial_instructions))
	    {
	      cie->initial_insn_length = initial_insn_length;
	      memcpy (cie->initial_instructions, buf, initial_insn_length);
	    }
	  insns = buf;
	  buf += initial_insn_length;
	  ENSURE_NO_RELOCS (buf);
	}
      else
	{
	  /* Find the corresponding CIE.  */
	  unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
	  for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
	    if (cie_offset == ecie->offset)
	      break;

	  /* Ensure this FDE references one of the CIEs in this input
	     section.  */
	  REQUIRE (ecie != ecies + ecie_count);
	  cie = &ecie->cie;

	  ENSURE_NO_RELOCS (buf);
	  REQUIRE (GET_RELOC (buf));

	  if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
	    /* This is a FDE against a discarded section.  It should
	       be deleted.  */
	    this_inf->removed = 1;
	  else
	    {
	      if (info->shared
		  && (((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr
		       && cie->make_relative == 0)
		      || (cie->fde_encoding & 0xf0) == DW_EH_PE_aligned))
		{
		  /* If a shared library uses absolute pointers
		     which we cannot turn into PC relative,
		     don't create the binary search table,
		     since it is affected by runtime relocations.  */
		  hdr_info->table = FALSE;
		  (*info->callbacks->einfo)
		    (_("%P: fde encoding in %B(%A) prevents .eh_frame_hdr"
		       " table being created.\n"), abfd, sec);
		}
	      ecie->usage_count++;
	      hdr_info->fde_count++;
	      this_inf->cie_inf = (void *) (ecie - ecies);
	    }

	  /* Skip the initial location and address range.  */
	  start = buf;
	  length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
	  REQUIRE (skip_bytes (&buf, end, 2 * length));

	  /* Skip the augmentation size, if present.  */
	  if (cie->augmentation[0] == 'z')
	    REQUIRE (read_uleb128 (&buf, end, &length));
	  else
	    length = 0;

	  /* Of the supported augmentation characters above, only 'L'
	     adds augmentation data to the FDE.  This code would need to
	     be adjusted if any future augmentations do the same thing.  */
	  if (cie->lsda_encoding != DW_EH_PE_omit)
	    {
	      this_inf->lsda_offset = buf - start;
	      /* If there's no 'z' augmentation, we don't know where the
		 CFA insns begin.  Assume no padding.  */
	      if (cie->augmentation[0] != 'z')
		length = end - buf;
	    }

	  /* Skip over the augmentation data.  */
	  REQUIRE (skip_bytes (&buf, end, length));
	  insns = buf;

	  buf = last_fde + 4 + hdr_length;
	  SKIP_RELOCS (buf);
	}

      /* Try to interpret the CFA instructions and find the first
	 padding nop.  Shrink this_inf's size so that it doesn't
	 include the padding.  */
      length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
      set_loc_count = 0;
      insns_end = skip_non_nops (insns, end, length, &set_loc_count);
      /* If we don't understand the CFA instructions, we can't know
	 what needs to be adjusted there.  */
      if (insns_end == NULL
	  /* For the time being we don't support DW_CFA_set_loc in
	     CIE instructions.  */
	  || (set_loc_count && this_inf->cie))
	goto free_no_table;
      this_inf->size -= end - insns_end;
      if (insns_end != end && this_inf->cie)
	{
	  cie->initial_insn_length -= end - insns_end;
	  cie->length -= end - insns_end;
	}
      if (set_loc_count
	  && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel
	      || cie->make_relative))
	{
	  unsigned int cnt;
	  bfd_byte *p;

	  this_inf->set_loc = bfd_malloc ((set_loc_count + 1)
					  * sizeof (unsigned int));
	  REQUIRE (this_inf->set_loc);
	  this_inf->set_loc[0] = set_loc_count;
	  p = insns;
	  cnt = 0;
	  while (p < end)
	    {
	      if (*p == DW_CFA_set_loc)
		this_inf->set_loc[++cnt] = p + 1 - start;
	      REQUIRE (skip_cfa_op (&p, end, length));
	    }
	}

      this_inf->fde_encoding = cie->fde_encoding;
      this_inf->lsda_encoding = cie->lsda_encoding;
      sec_info->count++;
    }

  elf_section_data (sec)->sec_info = sec_info;
  sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;

  /* Look at all CIEs in this section and determine which can be
     removed as unused, which can be merged with previous duplicate
     CIEs and which need to be kept.  */
  for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
    {
      if (ecie->usage_count == 0)
	{
	  sec_info->entry[ecie->entry].removed = 1;
	  continue;
	}
      ecie->cie.output_sec = sec->output_section;
      ecie->cie.cie_inf = sec_info->entry + ecie->entry;
      cie_compute_hash (&ecie->cie);
      if (hdr_info->cies != NULL)
	{
	  void **loc = htab_find_slot_with_hash (hdr_info->cies, &ecie->cie,
						 ecie->cie.hash, INSERT);
	  if (loc != NULL)
	    {
	      if (*loc != HTAB_EMPTY_ENTRY)
		{
		  sec_info->entry[ecie->entry].removed = 1;
		  ecie->cie.cie_inf = ((struct cie *) *loc)->cie_inf;
		  continue;
		}

	      *loc = malloc (sizeof (struct cie));
	      if (*loc == NULL)
		*loc = HTAB_DELETED_ENTRY;
	      else
		memcpy (*loc, &ecie->cie, sizeof (struct cie));
	    }
	}
      ecie->cie.cie_inf->make_relative = ecie->cie.make_relative;
      ecie->cie.cie_inf->make_lsda_relative = ecie->cie.make_lsda_relative;
      ecie->cie.cie_inf->per_encoding_relative
	= (ecie->cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
    }

  /* Ok, now we can assign new offsets.  */
  offset = 0;
  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
    if (!ent->removed)
      {
	if (!ent->cie)
	  {
	    ecie = ecies + (bfd_hostptr_t) ent->cie_inf;
	    ent->cie_inf = ecie->cie.cie_inf;
	  }
	ent->new_offset = offset;
	offset += size_of_output_cie_fde (ent, ptr_size);
      }

  /* Resize the sec as needed.  */
  sec->rawsize = sec->size;
  sec->size = offset;

  free (ehbuf);
  if (ecies)
    free (ecies);
  return offset != sec->rawsize;

free_no_table:
  (*info->callbacks->einfo)
    (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
     abfd, sec);
  if (ehbuf)
    free (ehbuf);
  if (sec_info)
    free (sec_info);
  if (ecies)
    free (ecies);
  hdr_info->table = FALSE;
  return FALSE;

#undef REQUIRE
}
bfd_boolean
_bfd_elf_discard_section_eh_frame
   (bfd *abfd, struct bfd_link_info *info, asection *sec,
    bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
    struct elf_reloc_cookie *cookie)
{
  struct eh_cie_fde *ent;
  struct eh_frame_sec_info *sec_info;
  struct eh_frame_hdr_info *hdr_info;
  unsigned int ptr_size, offset;

  sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
  if (sec_info == NULL)
    return FALSE;

  hdr_info = &elf_hash_table (info)->eh_info;
  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
    if (ent->size == 4)
      /* There should only be one zero terminator, on the last input
	 file supplying .eh_frame (crtend.o).  Remove any others.  */
      ent->removed = sec->map_head.s != NULL;
    else if (!ent->cie)
      {
	cookie->rel = cookie->rels + ent->reloc_index;
	BFD_ASSERT (cookie->rel < cookie->relend
		    && cookie->rel->r_offset == ent->offset + 8);
	if (!(*reloc_symbol_deleted_p) (ent->offset + 8, cookie))
	  {
	    if (info->shared
		&& (((ent->fde_encoding & 0xf0) == DW_EH_PE_absptr
		     && ent->make_relative == 0)
		    || (ent->fde_encoding & 0xf0) == DW_EH_PE_aligned))
	      {
		/* If a shared library uses absolute pointers
		   which we cannot turn into PC relative,
		   don't create the binary search table,
		   since it is affected by runtime relocations.  */
		hdr_info->table = FALSE;
		(*info->callbacks->einfo)
		  (_("%P: fde encoding in %B(%A) prevents .eh_frame_hdr"
		     " table being created.\n"), abfd, sec);
	      }
	    ent->removed = 0;
	    hdr_info->fde_count++;
	    ent->u.fde.cie_inf = find_merged_cie (abfd, sec, hdr_info, cookie,
						  ent->u.fde.cie_inf);
	  }
      }

  if (sec_info->cies)
    {
      free (sec_info->cies);
      sec_info->cies = NULL;
    }

  ptr_size = (get_elf_backend_data (sec->owner)
	      ->elf_backend_eh_frame_address_size (sec->owner, sec));
  offset = 0;
  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
    if (!ent->removed)
      {
	ent->new_offset = offset;
	offset += size_of_output_cie_fde (ent, ptr_size);
      }

  sec->rawsize = sec->size;
  sec->size = offset;
  return offset != sec->rawsize;
}
Beispiel #18
0
static void
elf_symfile_read (struct objfile *objfile, int mainline)
{
  bfd *abfd = objfile->obfd;
  struct elfinfo ei;
  struct cleanup *back_to;
  CORE_ADDR offset;

  init_minimal_symbol_collection ();
  back_to = make_cleanup_discard_minimal_symbols ();

  memset ((char *) &ei, 0, sizeof (ei));

  /* Allocate struct to keep track of the symfile */
  objfile->sym_stab_info = (struct dbx_symfile_info *)
    xmmalloc (objfile->md, sizeof (struct dbx_symfile_info));
  memset ((char *) objfile->sym_stab_info, 0, sizeof (struct dbx_symfile_info));
  make_cleanup (free_elfinfo, (void *) objfile);

  /* Process the normal ELF symbol table first.  This may write some 
     chain of info into the dbx_symfile_info in objfile->sym_stab_info,
     which can later be used by elfstab_offset_sections.  */

  elf_symtab_read (objfile, 0);

  /* Add the dynamic symbols.  */

  elf_symtab_read (objfile, 1);

  /* Now process debugging information, which is contained in
     special ELF sections. */

  /* If we are reinitializing, or if we have never loaded syms yet,
     set table to empty.  MAINLINE is cleared so that *_read_psymtab
     functions do not all also re-initialize the psymbol table. */
  if (mainline)
    {
      init_psymbol_list (objfile, 0);
      mainline = 0;
    }

  /* We first have to find them... */
  bfd_map_over_sections (abfd, elf_locate_sections, (void *) & ei);

  /* ELF debugging information is inserted into the psymtab in the
     order of least informative first - most informative last.  Since
     the psymtab table is searched `most recent insertion first' this
     increases the probability that more detailed debug information
     for a section is found.

     For instance, an object file might contain both .mdebug (XCOFF)
     and .debug_info (DWARF2) sections then .mdebug is inserted first
     (searched last) and DWARF2 is inserted last (searched first).  If
     we don't do this then the XCOFF info is found first - for code in
     an included file XCOFF info is useless. */

  if (ei.mdebugsect)
    {
      const struct ecoff_debug_swap *swap;

      /* .mdebug section, presumably holding ECOFF debugging
         information.  */
      swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
      if (swap)
	elfmdebug_build_psymtabs (objfile, swap, ei.mdebugsect);
    }
  if (ei.stabsect)
    {
      asection *str_sect;

      /* Stab sections have an associated string table that looks like
         a separate section.  */
      str_sect = bfd_get_section_by_name (abfd, ".stabstr");

      /* FIXME should probably warn about a stab section without a stabstr.  */
      if (str_sect)
	elfstab_build_psymtabs (objfile,
				mainline,
				ei.stabsect->filepos,
				bfd_section_size (abfd, ei.stabsect),
				str_sect->filepos,
				bfd_section_size (abfd, str_sect));
    }
  if (dwarf2_has_info (abfd))
    {
      /* DWARF 2 sections */
      dwarf2_build_psymtabs (objfile, mainline);
    }
  else if (ei.dboffset && ei.lnoffset)
    {
      /* DWARF sections */
      dwarf_build_psymtabs (objfile,
			    mainline,
			    ei.dboffset, ei.dbsize,
			    ei.lnoffset, ei.lnsize);
    }

  if (DWARF2_BUILD_FRAME_INFO_P ())
    DWARF2_BUILD_FRAME_INFO(objfile);

  /* Install any minimal symbols that have been collected as the current
     minimal symbols for this objfile. */

  install_minimal_symbols (objfile);

  do_cleanups (back_to);
}
Beispiel #19
0
/* Parse an object attributes section.  */
void
_bfd_elf_parse_attributes (bfd *abfd, Elf_Internal_Shdr * hdr)
{
  bfd_byte *contents;
  bfd_byte *p;
  bfd_vma len;
  const char *std_sec;

  contents = (bfd_byte *) bfd_malloc (hdr->sh_size);
  if (!contents)
    return;
  if (!bfd_get_section_contents (abfd, hdr->bfd_section, contents, 0,
				 hdr->sh_size))
    {
      free (contents);
      return;
    }
  p = contents;
  std_sec = get_elf_backend_data (abfd)->obj_attrs_vendor;
  if (*(p++) == 'A')
    {
      len = hdr->sh_size - 1;
      while (len > 0)
	{
	  int namelen;
	  bfd_vma section_len;
	  int vendor;

	  section_len = bfd_get_32 (abfd, p);
	  p += 4;
	  if (section_len > len)
	    section_len = len;
	  len -= section_len;
	  namelen = strlen ((char *) p) + 1;
	  section_len -= namelen + 4;
	  if (std_sec && strcmp ((char *) p, std_sec) == 0)
	    vendor = OBJ_ATTR_PROC;
	  else if (strcmp ((char *) p, "gnu") == 0)
	    vendor = OBJ_ATTR_GNU;
	  else
	    {
	      /* Other vendor section.  Ignore it.  */
	      p += namelen + section_len;
	      continue;
	    }

	  p += namelen;
	  while (section_len > 0)
	    {
	      int tag;
	      unsigned int n;
	      unsigned int val;
	      bfd_vma subsection_len;
	      bfd_byte *end;

	      tag = read_unsigned_leb128 (abfd, p, &n);
	      p += n;
	      subsection_len = bfd_get_32 (abfd, p);
	      p += 4;
	      if (subsection_len > section_len)
		subsection_len = section_len;
	      section_len -= subsection_len;
	      subsection_len -= n + 4;
	      end = p + subsection_len;
	      switch (tag)
		{
		case Tag_File:
		  while (p < end)
		    {
		      int type;

		      tag = read_unsigned_leb128 (abfd, p, &n);
		      p += n;
		      type = _bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
		      switch (type & (ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL))
			{
			case ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL:
			  val = read_unsigned_leb128 (abfd, p, &n);
			  p += n;
			  bfd_elf_add_obj_attr_int_string (abfd, vendor, tag,
							   val, (char *)p);
			  p += strlen ((char *)p) + 1;
			  break;
			case ATTR_TYPE_FLAG_STR_VAL:
			  bfd_elf_add_obj_attr_string (abfd, vendor, tag,
						       (char *)p);
			  p += strlen ((char *)p) + 1;
			  break;
			case ATTR_TYPE_FLAG_INT_VAL:
			  val = read_unsigned_leb128 (abfd, p, &n);
			  p += n;
			  bfd_elf_add_obj_attr_int (abfd, vendor, tag, val);
			  break;
			default:
			  abort ();
			}
		    }
		  break;
		case Tag_Section:
		case Tag_Symbol:
		  /* Don't have anywhere convenient to attach these.
		     Fall through for now.  */
		default:
		  /* Ignore things we don't kow about.  */
		  p += subsection_len;
		  subsection_len = 0;
		  break;
		}
	    }
	}
    }
  free (contents);
}
void
_bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
			 asection *sec, struct elf_reloc_cookie *cookie)
{
#define REQUIRE(COND)					\
  do							\
    if (!(COND))					\
      goto free_no_table;				\
  while (0)

  bfd_byte *ehbuf = NULL, *buf, *end;
  bfd_byte *last_fde;
  struct eh_cie_fde *this_inf;
  unsigned int hdr_length, hdr_id;
  unsigned int cie_count;
  struct cie *cie, *local_cies = NULL;
  struct elf_link_hash_table *htab;
  struct eh_frame_hdr_info *hdr_info;
  struct eh_frame_sec_info *sec_info = NULL;
  unsigned int ptr_size;
  unsigned int num_cies;
  unsigned int num_entries;
  elf_gc_mark_hook_fn gc_mark_hook;

  htab = elf_hash_table (info);
  hdr_info = &htab->eh_info;
  if (hdr_info->parsed_eh_frames)
    return;

  if (sec->size == 0)
    {
      /* This file does not contain .eh_frame information.  */
      return;
    }

  if (bfd_is_abs_section (sec->output_section))
    {
      /* At least one of the sections is being discarded from the
	 link, so we should just ignore them.  */
      return;
    }

  /* Read the frame unwind information from abfd.  */

  REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));

  if (sec->size >= 4
      && bfd_get_32 (abfd, ehbuf) == 0
      && cookie->rel == cookie->relend)
    {
      /* Empty .eh_frame section.  */
      free (ehbuf);
      return;
    }

  /* If .eh_frame section size doesn't fit into int, we cannot handle
     it (it would need to use 64-bit .eh_frame format anyway).  */
  REQUIRE (sec->size == (unsigned int) sec->size);

  ptr_size = (get_elf_backend_data (abfd)
	      ->elf_backend_eh_frame_address_size (abfd, sec));
  REQUIRE (ptr_size != 0);

  /* Go through the section contents and work out how many FDEs and
     CIEs there are.  */
  buf = ehbuf;
  end = ehbuf + sec->size;
  num_cies = 0;
  num_entries = 0;
  while (buf != end)
    {
      num_entries++;

      /* Read the length of the entry.  */
      REQUIRE (skip_bytes (&buf, end, 4));
      hdr_length = bfd_get_32 (abfd, buf - 4);

      /* 64-bit .eh_frame is not supported.  */
      REQUIRE (hdr_length != 0xffffffff);
      if (hdr_length == 0)
	break;

      REQUIRE (skip_bytes (&buf, end, 4));
      hdr_id = bfd_get_32 (abfd, buf - 4);
      if (hdr_id == 0)
	num_cies++;

      REQUIRE (skip_bytes (&buf, end, hdr_length - 4));
    }

  sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
			  + (num_entries - 1) * sizeof (struct eh_cie_fde));
  REQUIRE (sec_info);

  /* We need to have a "struct cie" for each CIE in this section.  */
  local_cies = bfd_zmalloc (num_cies * sizeof (*local_cies));
  REQUIRE (local_cies);

#define ENSURE_NO_RELOCS(buf)				\
  REQUIRE (!(cookie->rel < cookie->relend		\
	     && (cookie->rel->r_offset			\
		 < (bfd_size_type) ((buf) - ehbuf))	\
	     && cookie->rel->r_info != 0))

#define SKIP_RELOCS(buf)				\
  while (cookie->rel < cookie->relend			\
	 && (cookie->rel->r_offset			\
	     < (bfd_size_type) ((buf) - ehbuf)))	\
    cookie->rel++

#define GET_RELOC(buf)					\
  ((cookie->rel < cookie->relend			\
    && (cookie->rel->r_offset				\
	== (bfd_size_type) ((buf) - ehbuf)))		\
   ? cookie->rel : NULL)

  buf = ehbuf;
  cie_count = 0;
  gc_mark_hook = get_elf_backend_data (abfd)->gc_mark_hook;
  while ((bfd_size_type) (buf - ehbuf) != sec->size)
    {
      char *aug;
      bfd_byte *start, *insns, *insns_end;
      bfd_size_type length;
      unsigned int set_loc_count;

      this_inf = sec_info->entry + sec_info->count;
      last_fde = buf;

      /* Read the length of the entry.  */
      REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
      hdr_length = bfd_get_32 (abfd, buf - 4);

      /* The CIE/FDE must be fully contained in this input section.  */
      REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr_length <= sec->size);
      end = buf + hdr_length;

      this_inf->offset = last_fde - ehbuf;
      this_inf->size = 4 + hdr_length;
      this_inf->reloc_index = cookie->rel - cookie->rels;

      if (hdr_length == 0)
	{
	  /* A zero-length CIE should only be found at the end of
	     the section.  */
	  REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
	  ENSURE_NO_RELOCS (buf);
	  sec_info->count++;
	  break;
	}

      REQUIRE (skip_bytes (&buf, end, 4));
      hdr_id = bfd_get_32 (abfd, buf - 4);

      if (hdr_id == 0)
	{
	  unsigned int initial_insn_length;

	  /* CIE  */
	  this_inf->cie = 1;

	  /* Point CIE to one of the section-local cie structures.  */
	  cie = local_cies + cie_count++;

	  cie->cie_inf = this_inf;
	  cie->length = hdr_length;
	  cie->output_sec = sec->output_section;
	  start = buf;
	  REQUIRE (read_byte (&buf, end, &cie->version));

	  /* Cannot handle unknown versions.  */
	  REQUIRE (cie->version == 1 || cie->version == 3);
	  REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));

	  strcpy (cie->augmentation, (char *) buf);
	  buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
	  ENSURE_NO_RELOCS (buf);
	  if (buf[0] == 'e' && buf[1] == 'h')
	    {
	      /* GCC < 3.0 .eh_frame CIE */
	      /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
		 is private to each CIE, so we don't need it for anything.
		 Just skip it.  */
	      REQUIRE (skip_bytes (&buf, end, ptr_size));
	      SKIP_RELOCS (buf);
	    }
	  REQUIRE (read_uleb128 (&buf, end, &cie->code_align));
	  REQUIRE (read_sleb128 (&buf, end, &cie->data_align));
	  if (cie->version == 1)
	    {
	      REQUIRE (buf < end);
	      cie->ra_column = *buf++;
	    }
	  else
	    REQUIRE (read_uleb128 (&buf, end, &cie->ra_column));
	  ENSURE_NO_RELOCS (buf);
	  cie->lsda_encoding = DW_EH_PE_omit;
	  cie->fde_encoding = DW_EH_PE_omit;
	  cie->per_encoding = DW_EH_PE_omit;
	  aug = cie->augmentation;
	  if (aug[0] != 'e' || aug[1] != 'h')
	    {
	      if (*aug == 'z')
		{
		  aug++;
		  REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
	  	  ENSURE_NO_RELOCS (buf);
		}

	      while (*aug != '\0')
		switch (*aug++)
		  {
		  case 'L':
		    REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
		    ENSURE_NO_RELOCS (buf);
		    REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size));
		    break;
		  case 'R':
		    REQUIRE (read_byte (&buf, end, &cie->fde_encoding));
		    ENSURE_NO_RELOCS (buf);
		    REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size));
		    break;
		  case 'S':
		    break;
		  case 'P':
		    {
		      int per_width;

		      REQUIRE (read_byte (&buf, end, &cie->per_encoding));
		      per_width = get_DW_EH_PE_width (cie->per_encoding,
						      ptr_size);
		      REQUIRE (per_width);
		      if ((cie->per_encoding & 0xf0) == DW_EH_PE_aligned)
			{
			  length = -(buf - ehbuf) & (per_width - 1);
			  REQUIRE (skip_bytes (&buf, end, length));
			}
		      ENSURE_NO_RELOCS (buf);
		      /* Ensure we have a reloc here.  */
		      REQUIRE (GET_RELOC (buf));
		      cie->personality.reloc_index
			= cookie->rel - cookie->rels;
		      /* Cope with MIPS-style composite relocations.  */
		      do
			cookie->rel++;
		      while (GET_RELOC (buf) != NULL);
		      REQUIRE (skip_bytes (&buf, end, per_width));
		    }
		    break;
		  default:
		    /* Unrecognized augmentation. Better bail out.  */
		    goto free_no_table;
		  }
	    }

	  /* For shared libraries, try to get rid of as many RELATIVE relocs
	     as possible.  */
	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_relative_eh_frame
		  (abfd, info, sec)))
	    {
	      if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr)
		this_inf->make_relative = 1;
	      /* If the CIE doesn't already have an 'R' entry, it's fairly
		 easy to add one, provided that there's no aligned data
		 after the augmentation string.  */
	      else if (cie->fde_encoding == DW_EH_PE_omit
		       && (cie->per_encoding & 0xf0) != DW_EH_PE_aligned)
		{
		  if (*cie->augmentation == 0)
		    this_inf->add_augmentation_size = 1;
		  this_inf->u.cie.add_fde_encoding = 1;
		  this_inf->make_relative = 1;
		}
	    }

	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_lsda_relative_eh_frame
		  (abfd, info, sec))
	      && (cie->lsda_encoding & 0xf0) == DW_EH_PE_absptr)
	    cie->can_make_lsda_relative = 1;

	  /* If FDE encoding was not specified, it defaults to
	     DW_EH_absptr.  */
	  if (cie->fde_encoding == DW_EH_PE_omit)
	    cie->fde_encoding = DW_EH_PE_absptr;

	  initial_insn_length = end - buf;
	  if (initial_insn_length <= sizeof (cie->initial_instructions))
	    {
	      cie->initial_insn_length = initial_insn_length;
	      memcpy (cie->initial_instructions, buf, initial_insn_length);
	    }
	  insns = buf;
	  buf += initial_insn_length;
	  ENSURE_NO_RELOCS (buf);

	  if (hdr_info->merge_cies)
	    this_inf->u.cie.u.full_cie = cie;
	  this_inf->u.cie.per_encoding_relative
	    = (cie->per_encoding & 0x70) == DW_EH_PE_pcrel;
	}
      else
	{
	  asection *rsec;

	  /* Find the corresponding CIE.  */
	  unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
	  for (cie = local_cies; cie < local_cies + cie_count; cie++)
	    if (cie_offset == cie->cie_inf->offset)
	      break;

	  /* Ensure this FDE references one of the CIEs in this input
	     section.  */
	  REQUIRE (cie != local_cies + cie_count);
	  this_inf->u.fde.cie_inf = cie->cie_inf;
	  this_inf->make_relative = cie->cie_inf->make_relative;
	  this_inf->add_augmentation_size
	    = cie->cie_inf->add_augmentation_size;

	  ENSURE_NO_RELOCS (buf);
	  REQUIRE (GET_RELOC (buf));

	  /* Chain together the FDEs for each section.  */
	  rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
	  /* RSEC will be NULL if FDE was cleared out as it was belonging to
	     a discarded SHT_GROUP.  */
	  if (rsec)
	    {
	      REQUIRE (rsec->owner == abfd);
	      this_inf->u.fde.next_for_section = elf_fde_list (rsec);
	      elf_fde_list (rsec) = this_inf;
	    }

	  /* Skip the initial location and address range.  */
	  start = buf;
	  length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
	  REQUIRE (skip_bytes (&buf, end, 2 * length));

	  /* Skip the augmentation size, if present.  */
	  if (cie->augmentation[0] == 'z')
	    REQUIRE (read_uleb128 (&buf, end, &length));
	  else
	    length = 0;

	  /* Of the supported augmentation characters above, only 'L'
	     adds augmentation data to the FDE.  This code would need to
	     be adjusted if any future augmentations do the same thing.  */
	  if (cie->lsda_encoding != DW_EH_PE_omit)
	    {
	      SKIP_RELOCS (buf);
	      if (cie->can_make_lsda_relative && GET_RELOC (buf))
		cie->cie_inf->u.cie.make_lsda_relative = 1;
	      this_inf->lsda_offset = buf - start;
	      /* If there's no 'z' augmentation, we don't know where the
		 CFA insns begin.  Assume no padding.  */
	      if (cie->augmentation[0] != 'z')
		length = end - buf;
	    }

	  /* Skip over the augmentation data.  */
	  REQUIRE (skip_bytes (&buf, end, length));
	  insns = buf;

	  buf = last_fde + 4 + hdr_length;

	  /* For NULL RSEC (cleared FDE belonging to a discarded section)
	     the relocations are commonly cleared.  We do not sanity check if
	     all these relocations are cleared as (1) relocations to
	     .gcc_except_table will remain uncleared (they will get dropped
	     with the drop of this unused FDE) and (2) BFD already safely drops
	     relocations of any type to .eh_frame by
	     elf_section_ignore_discarded_relocs.
	     TODO: The .gcc_except_table entries should be also filtered as
	     .eh_frame entries; or GCC could rather use COMDAT for them.  */
	  SKIP_RELOCS (buf);
	}

      /* Try to interpret the CFA instructions and find the first
	 padding nop.  Shrink this_inf's size so that it doesn't
	 include the padding.  */
      length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
      set_loc_count = 0;
      insns_end = skip_non_nops (insns, end, length, &set_loc_count);
      /* If we don't understand the CFA instructions, we can't know
	 what needs to be adjusted there.  */
      if (insns_end == NULL
	  /* For the time being we don't support DW_CFA_set_loc in
	     CIE instructions.  */
	  || (set_loc_count && this_inf->cie))
	goto free_no_table;
      this_inf->size -= end - insns_end;
      if (insns_end != end && this_inf->cie)
	{
	  cie->initial_insn_length -= end - insns_end;
	  cie->length -= end - insns_end;
	}
      if (set_loc_count
	  && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel
	      || this_inf->make_relative))
	{
	  unsigned int cnt;
	  bfd_byte *p;

	  this_inf->set_loc = bfd_malloc ((set_loc_count + 1)
					  * sizeof (unsigned int));
	  REQUIRE (this_inf->set_loc);
	  this_inf->set_loc[0] = set_loc_count;
	  p = insns;
	  cnt = 0;
	  while (p < end)
	    {
	      if (*p == DW_CFA_set_loc)
		this_inf->set_loc[++cnt] = p + 1 - start;
	      REQUIRE (skip_cfa_op (&p, end, length));
	    }
	}

      this_inf->removed = 1;
      this_inf->fde_encoding = cie->fde_encoding;
      this_inf->lsda_encoding = cie->lsda_encoding;
      sec_info->count++;
    }
  BFD_ASSERT (sec_info->count == num_entries);
  BFD_ASSERT (cie_count == num_cies);

  elf_section_data (sec)->sec_info = sec_info;
  sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
  if (hdr_info->merge_cies)
    {
      sec_info->cies = local_cies;
      local_cies = NULL;
    }
  goto success;

 free_no_table:
  (*info->callbacks->einfo)
    (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
     abfd, sec);
  hdr_info->table = FALSE;
  if (sec_info)
    free (sec_info);
 success:
  if (ehbuf)
    free (ehbuf);
  if (local_cies)
    free (local_cies);
#undef REQUIRE
}
bfd *
_bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
{
  bfd *abfd, *first_pbfd = NULL;
  elf_property_list *list;
  asection *sec;
  bfd_boolean has_properties = FALSE;
  const struct elf_backend_data *bed
    = get_elf_backend_data (info->output_bfd);
  unsigned int elfclass = bed->s->elfclass;
  int elf_machine_code = bed->elf_machine_code;

  /* Find the first relocatable ELF input with GNU properties.  */
  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
    if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
	&& (abfd->flags & DYNAMIC) == 0
	&& elf_properties (abfd) != NULL)
      {
	has_properties = TRUE;

	/* Ignore GNU properties from ELF objects with different machine
	   code or class.  Also skip objects without a GNU_PROPERTY note
	   section.  */
	if ((elf_machine_code
	     == get_elf_backend_data (abfd)->elf_machine_code)
	    && (elfclass
		== get_elf_backend_data (abfd)->s->elfclass)
	    && bfd_get_section_by_name (abfd,
					NOTE_GNU_PROPERTY_SECTION_NAME) != NULL
	    )
	  {
	    /* Keep .note.gnu.property section in FIRST_PBFD.  */
	    first_pbfd = abfd;
	    break;
	  }
      }

  /* Do nothing if there is no .note.gnu.property section.  */
  if (!has_properties)
    return NULL;

  /* Merge .note.gnu.property sections.  */
  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
    if (abfd != first_pbfd && (abfd->flags & DYNAMIC) == 0)
      {
	elf_property_list *null_ptr = NULL;
	elf_property_list **listp = &null_ptr;

	/* Merge .note.gnu.property section in relocatable ELF input.  */
	if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
	  {
	    list = elf_properties (abfd);

	    /* Ignore GNU properties from ELF objects with different
	       machine code.  */
	    if (list != NULL
		&& (elf_machine_code
		    == get_elf_backend_data (abfd)->elf_machine_code))
	      listp = &elf_properties (abfd);
	  }
	else
	  list = NULL;

	/* Merge properties with FIRST_PBFD.  FIRST_PBFD can be NULL
	   when all properties are from ELF objects with different
	   machine code or class.  */
	if (first_pbfd != NULL)
	  elf_merge_gnu_property_list (info, first_pbfd, listp);

	if (list != NULL)
	  {
	    /* Discard the .note.gnu.property section in this bfd.  */
	    sec = bfd_get_section_by_name (abfd,
					   NOTE_GNU_PROPERTY_SECTION_NAME);
	    if (sec != NULL)
	      sec->output_section = bfd_abs_section_ptr;
	  }
      }

  /* Rewrite .note.gnu.property section so that GNU properties are
     always sorted by type even if input GNU properties aren't sorted.  */
  if (first_pbfd != NULL)
    {
      bfd_size_type size;
      bfd_byte *contents;
      unsigned int align_size = elfclass == ELFCLASS64 ? 8 : 4;

      sec = bfd_get_section_by_name (first_pbfd,
				     NOTE_GNU_PROPERTY_SECTION_NAME);
      BFD_ASSERT (sec != NULL);

      /* Update stack size in .note.gnu.property with -z stack-size=N
	 if N > 0.  */
      if (info->stacksize > 0)
	{
	  elf_property *p;
	  bfd_vma stacksize = info->stacksize;

	  p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_STACK_SIZE,
				     align_size);
	  if (p->pr_kind == property_unknown)
	    {
	      /* Create GNU_PROPERTY_STACK_SIZE.  */
	      p->u.number = stacksize;
	      p->pr_kind = property_number;
	    }
	  else if (stacksize > p->u.number)
	    p->u.number = stacksize;
	}
      else if (elf_properties (first_pbfd) == NULL)
	{
	  /* Discard .note.gnu.property section if all properties have
	     been removed.  */
	  sec->output_section = bfd_abs_section_ptr;
	  return NULL;
	}

      /* Fix up GNU properties.  */
      if (bed->fixup_gnu_properties)
	bed->fixup_gnu_properties (info, &elf_properties (first_pbfd));

      if (elf_properties (first_pbfd) == NULL)
	{
	  /* Discard .note.gnu.property section if all properties have
	     been removed.  */
	  sec->output_section = bfd_abs_section_ptr;
	  return NULL;
	}

      /* Compute the section size.  */
      list = elf_properties (first_pbfd);
      size = elf_get_gnu_property_section_size (list, align_size);

      /* Update .note.gnu.property section now.  */
      sec->size = size;
      contents = (bfd_byte *) bfd_zalloc (first_pbfd, size);

      elf_write_gnu_properties (first_pbfd, contents, list, size,
				align_size);

      /* Cache the section contents for elf_link_input_bfd.  */
      elf_section_data (sec)->this_hdr.contents = contents;

      /* If GNU_PROPERTY_NO_COPY_ON_PROTECTED is set, protected data
	 symbol is defined in the shared object.  */
      if (elf_has_no_copy_on_protected (first_pbfd))
	info->extern_protected_data = FALSE;
    }

  return first_pbfd;
}
Beispiel #22
0
  return TRUE;
}

/* Similar to _bfd_elf_get_synthetic_symtab, optimized for unsorted PLT
   entries.  PLT is the PLT section.  PLT_SYM_VAL is a function pointer
   which returns an array of PLT entry symbol values.  */

long
_bfd_elf_ifunc_get_synthetic_symtab
  (bfd *abfd, long symcount ATTRIBUTE_UNUSED,
   asymbol **syms ATTRIBUTE_UNUSED, long dynsymcount, asymbol **dynsyms,
   asymbol **ret, asection *plt,
   bfd_vma *(*get_plt_sym_val) (bfd *, asymbol **, asection *, asection *))
{
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  asection *relplt;
  asymbol *s;
  const char *relplt_name;
  bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
  arelent *p;
  long count, i, n;
  size_t size;
  Elf_Internal_Shdr *hdr;
  char *names;
  bfd_vma *plt_sym_val;

  *ret = NULL;

  if (plt == NULL)
    return 0;
Beispiel #23
0
bfd_boolean
_bfd_elf_merge_unknown_attribute_list (bfd *ibfd, bfd *obfd)
{
  obj_attribute_list *in_list;
  obj_attribute_list *out_list;
  obj_attribute_list **out_listp;
  bfd_boolean result = TRUE;

  in_list = elf_other_obj_attributes_proc (ibfd);
  out_listp = &elf_other_obj_attributes_proc (obfd);
  out_list = *out_listp;

  for (; in_list || out_list; )
    {
      bfd *err_bfd = NULL;
      int err_tag = 0;

      /* The tags for each list are in numerical order.  */
      /* If the tags are equal, then merge.  */
      if (out_list && (!in_list || in_list->tag > out_list->tag))
	{
	  /* This attribute only exists in obfd.  We can't merge, and we don't
	     know what the tag means, so delete it.  */
	  err_bfd = obfd;
	  err_tag = out_list->tag;
	  *out_listp = out_list->next;
	  out_list = *out_listp;
	}
      else if (in_list && (!out_list || in_list->tag < out_list->tag))
	{
	  /* This attribute only exists in ibfd. We can't merge, and we don't
	     know what the tag means, so ignore it.  */
	  err_bfd = ibfd;
	  err_tag = in_list->tag;
	  in_list = in_list->next;
	}
      else /* The tags are equal.  */
	{
	  /* As present, all attributes in the list are unknown, and
	     therefore can't be merged meaningfully.  */
	  err_bfd = obfd;
	  err_tag = out_list->tag;

	  /*  Only pass on attributes that match in both inputs.  */
	  if (in_list->attr.i != out_list->attr.i
	      || (in_list->attr.s == NULL) != (out_list->attr.s == NULL)
	      || (in_list->attr.s && out_list->attr.s
		  && strcmp (in_list->attr.s, out_list->attr.s) != 0))
	    {
	      /* No match.  Delete the attribute.  */
	      *out_listp = out_list->next;
	      out_list = *out_listp;
	    }
	  else
	    {
	      /* Matched.  Keep the attribute and move to the next.  */
	      out_list = out_list->next;
	      in_list = in_list->next;
	    }
	}

      if (err_bfd)
	result = result
	  && get_elf_backend_data (err_bfd)->obj_attrs_handle_unknown (err_bfd,
								       err_tag);
    }

  return result;
}
Beispiel #24
0
bfd_boolean
_bfd_elf_discard_section_eh_frame
   (bfd *abfd, struct bfd_link_info *info, asection *sec,
    bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
    struct elf_reloc_cookie *cookie)
{
#define REQUIRE(COND)					\
  do							\
    if (!(COND))					\
      goto free_no_table;				\
  while (0)

  bfd_byte *ehbuf = NULL, *buf;
  bfd_byte *last_cie, *last_fde;
  struct eh_cie_fde *ent, *last_cie_inf, *this_inf;
  struct cie_header hdr;
  struct cie cie;
  struct elf_link_hash_table *htab;
  struct eh_frame_hdr_info *hdr_info;
  struct eh_frame_sec_info *sec_info = NULL;
  unsigned int cie_usage_count, offset;
  unsigned int ptr_size;

  if (sec->size == 0)
    {
      /* This file does not contain .eh_frame information.  */
      return FALSE;
    }

  if ((sec->output_section != NULL
       && bfd_is_abs_section (sec->output_section)))
    {
      /* At least one of the sections is being discarded from the
	 link, so we should just ignore them.  */
      return FALSE;
    }

  htab = elf_hash_table (info);
  hdr_info = &htab->eh_info;

  /* Read the frame unwind information from abfd.  */

  REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));

  if (sec->size >= 4
      && bfd_get_32 (abfd, ehbuf) == 0
      && cookie->rel == cookie->relend)
    {
      /* Empty .eh_frame section.  */
      free (ehbuf);
      return FALSE;
    }

  /* If .eh_frame section size doesn't fit into int, we cannot handle
     it (it would need to use 64-bit .eh_frame format anyway).  */
  REQUIRE (sec->size == (unsigned int) sec->size);

  ptr_size = (get_elf_backend_data (abfd)
	      ->elf_backend_eh_frame_address_size (abfd, sec));
  REQUIRE (ptr_size != 0);

  buf = ehbuf;
  last_cie = NULL;
  last_cie_inf = NULL;
  memset (&cie, 0, sizeof (cie));
  cie_usage_count = 0;
  sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
			  + 99 * sizeof (struct eh_cie_fde));
  REQUIRE (sec_info);

  sec_info->alloced = 100;

#define ENSURE_NO_RELOCS(buf)				\
  REQUIRE (!(cookie->rel < cookie->relend		\
	     && (cookie->rel->r_offset			\
		 < (bfd_size_type) ((buf) - ehbuf))	\
	     && cookie->rel->r_info != 0))

#define SKIP_RELOCS(buf)				\
  while (cookie->rel < cookie->relend			\
	 && (cookie->rel->r_offset			\
	     < (bfd_size_type) ((buf) - ehbuf)))	\
    cookie->rel++

#define GET_RELOC(buf)					\
  ((cookie->rel < cookie->relend			\
    && (cookie->rel->r_offset				\
	== (bfd_size_type) ((buf) - ehbuf)))		\
   ? cookie->rel : NULL)

  for (;;)
    {
      char *aug;
      bfd_byte *start, *end, *insns;
      bfd_size_type length;

      if (sec_info->count == sec_info->alloced)
	{
	  struct eh_cie_fde *old_entry = sec_info->entry;
	  sec_info = bfd_realloc (sec_info,
				  sizeof (struct eh_frame_sec_info)
				  + ((sec_info->alloced + 99)
				     * sizeof (struct eh_cie_fde)));
	  REQUIRE (sec_info);

	  memset (&sec_info->entry[sec_info->alloced], 0,
		  100 * sizeof (struct eh_cie_fde));
	  sec_info->alloced += 100;

	  /* Now fix any pointers into the array.  */
	  if (last_cie_inf >= old_entry
	      && last_cie_inf < old_entry + sec_info->count)
	    last_cie_inf = sec_info->entry + (last_cie_inf - old_entry);
	}

      this_inf = sec_info->entry + sec_info->count;
      last_fde = buf;
      /* If we are at the end of the section, we still need to decide
	 on whether to output or discard last encountered CIE (if any).  */
      if ((bfd_size_type) (buf - ehbuf) == sec->size)
	{
	  hdr.length = 0;
	  hdr.id = (unsigned int) -1;
	  end = buf;
	}
      else
	{
	  /* Read the length of the entry.  */
	  REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
	  hdr.length = bfd_get_32 (abfd, buf - 4);

	  /* 64-bit .eh_frame is not supported.  */
	  REQUIRE (hdr.length != 0xffffffff);

	  /* The CIE/FDE must be fully contained in this input section.  */
	  REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr.length <= sec->size);
	  end = buf + hdr.length;

	  this_inf->offset = last_fde - ehbuf;
	  this_inf->size = 4 + hdr.length;

	  if (hdr.length == 0)
	    {
	      /* A zero-length CIE should only be found at the end of
		 the section.  */
	      REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
	      ENSURE_NO_RELOCS (buf);
	      sec_info->count++;
	      /* Now just finish last encountered CIE processing and break
		 the loop.  */
	      hdr.id = (unsigned int) -1;
	    }
	  else
	    {
	      REQUIRE (skip_bytes (&buf, end, 4));
	      hdr.id = bfd_get_32 (abfd, buf - 4);
	      REQUIRE (hdr.id != (unsigned int) -1);
	    }
	}

      if (hdr.id == 0 || hdr.id == (unsigned int) -1)
	{
	  unsigned int initial_insn_length;

	  /* CIE  */
	  if (last_cie != NULL)
	    {
	      /* Now check if this CIE is identical to the last CIE,
		 in which case we can remove it provided we adjust
		 all FDEs.  Also, it can be removed if we have removed
		 all FDEs using it.  */
	      if ((!info->relocatable
		   && hdr_info->last_cie_sec
		   && (sec->output_section
		       == hdr_info->last_cie_sec->output_section)
		   && cie_compare (&cie, &hdr_info->last_cie) == 0)
		  || cie_usage_count == 0)
		last_cie_inf->removed = 1;
	      else
		{
		  hdr_info->last_cie = cie;
		  hdr_info->last_cie_sec = sec;
		  last_cie_inf->make_relative = cie.make_relative;
		  last_cie_inf->make_lsda_relative = cie.make_lsda_relative;
		  last_cie_inf->per_encoding_relative
		    = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
		}
	    }

	  if (hdr.id == (unsigned int) -1)
	    break;

	  last_cie_inf = this_inf;
	  this_inf->cie = 1;

	  cie_usage_count = 0;
	  memset (&cie, 0, sizeof (cie));
	  cie.hdr = hdr;
	  REQUIRE (read_byte (&buf, end, &cie.version));

	  /* Cannot handle unknown versions.  */
	  REQUIRE (cie.version == 1 || cie.version == 3);
	  REQUIRE (strlen ((char *) buf) < sizeof (cie.augmentation));

	  strcpy (cie.augmentation, (char *) buf);
	  buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
	  ENSURE_NO_RELOCS (buf);
	  if (buf[0] == 'e' && buf[1] == 'h')
	    {
	      /* GCC < 3.0 .eh_frame CIE */
	      /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
		 is private to each CIE, so we don't need it for anything.
		 Just skip it.  */
	      REQUIRE (skip_bytes (&buf, end, ptr_size));
	      SKIP_RELOCS (buf);
	    }
	  REQUIRE (read_uleb128 (&buf, end, &cie.code_align));
	  REQUIRE (read_sleb128 (&buf, end, &cie.data_align));
	  if (cie.version == 1)
	    {
	      REQUIRE (buf < end);
	      cie.ra_column = *buf++;
	    }
	  else
	    REQUIRE (read_uleb128 (&buf, end, &cie.ra_column));
	  ENSURE_NO_RELOCS (buf);
	  cie.lsda_encoding = DW_EH_PE_omit;
	  cie.fde_encoding = DW_EH_PE_omit;
	  cie.per_encoding = DW_EH_PE_omit;
	  aug = cie.augmentation;
	  if (aug[0] != 'e' || aug[1] != 'h')
	    {
	      if (*aug == 'z')
		{
		  aug++;
		  REQUIRE (read_uleb128 (&buf, end, &cie.augmentation_size));
	  	  ENSURE_NO_RELOCS (buf);
		}

	      while (*aug != '\0')
		switch (*aug++)
		  {
		  case 'L':
		    REQUIRE (read_byte (&buf, end, &cie.lsda_encoding));
		    ENSURE_NO_RELOCS (buf);
		    REQUIRE (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size));
		    break;
		  case 'R':
		    REQUIRE (read_byte (&buf, end, &cie.fde_encoding));
		    ENSURE_NO_RELOCS (buf);
		    REQUIRE (get_DW_EH_PE_width (cie.fde_encoding, ptr_size));
		    break;
		  case 'P':
		    {
		      int per_width;

		      REQUIRE (read_byte (&buf, end, &cie.per_encoding));
		      per_width = get_DW_EH_PE_width (cie.per_encoding,
						      ptr_size);
		      REQUIRE (per_width);
		      if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
			{
			  length = -(buf - ehbuf) & (per_width - 1);
			  REQUIRE (skip_bytes (&buf, end, length));
			}
		      ENSURE_NO_RELOCS (buf);
		      /* Ensure we have a reloc here, against
			 a global symbol.  */
		      if (GET_RELOC (buf) != NULL)
			{
			  unsigned long r_symndx;

#ifdef BFD64
			  if (ptr_size == 8)
			    r_symndx = ELF64_R_SYM (cookie->rel->r_info);
			  else
#endif
			    r_symndx = ELF32_R_SYM (cookie->rel->r_info);
			  if (r_symndx >= cookie->locsymcount)
			    {
			      struct elf_link_hash_entry *h;

			      r_symndx -= cookie->extsymoff;
			      h = cookie->sym_hashes[r_symndx];

			      while (h->root.type == bfd_link_hash_indirect
				     || h->root.type == bfd_link_hash_warning)
				h = (struct elf_link_hash_entry *)
				    h->root.u.i.link;

			      cie.personality = h;
			    }
			  /* Cope with MIPS-style composite relocations.  */
			  do
			    cookie->rel++;
			  while (GET_RELOC (buf) != NULL);
			}
		      REQUIRE (skip_bytes (&buf, end, per_width));
		    }
		    break;
		  default:
		    /* Unrecognized augmentation. Better bail out.  */
		    goto free_no_table;
		  }
	    }

	  /* For shared libraries, try to get rid of as many RELATIVE relocs
	     as possible.  */
	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_relative_eh_frame
		  (abfd, info, sec)))
	    {
	      if ((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
		cie.make_relative = 1;
	      /* If the CIE doesn't already have an 'R' entry, it's fairly
		 easy to add one, provided that there's no aligned data
		 after the augmentation string.  */
	      else if (cie.fde_encoding == DW_EH_PE_omit
		       && (cie.per_encoding & 0xf0) != DW_EH_PE_aligned)
		{
		  if (*cie.augmentation == 0)
		    this_inf->add_augmentation_size = 1;
		  this_inf->add_fde_encoding = 1;
		  cie.make_relative = 1;
		}
	    }

	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_lsda_relative_eh_frame
		  (abfd, info, sec))
	      && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
	    cie.make_lsda_relative = 1;

	  /* If FDE encoding was not specified, it defaults to
	     DW_EH_absptr.  */
	  if (cie.fde_encoding == DW_EH_PE_omit)
	    cie.fde_encoding = DW_EH_PE_absptr;

	  initial_insn_length = end - buf;
	  if (initial_insn_length <= 50)
	    {
	      cie.initial_insn_length = initial_insn_length;
	      memcpy (cie.initial_instructions, buf, initial_insn_length);
	    }
	  insns = buf;
	  buf += initial_insn_length;
	  ENSURE_NO_RELOCS (buf);
	  last_cie = last_fde;
	}
      else
	{
	  /* Ensure this FDE uses the last CIE encountered.  */
	  REQUIRE (last_cie);
	  REQUIRE (hdr.id == (unsigned int) (buf - 4 - last_cie));

	  ENSURE_NO_RELOCS (buf);
	  REQUIRE (GET_RELOC (buf));

	  if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
	    /* This is a FDE against a discarded section.  It should
	       be deleted.  */
	    this_inf->removed = 1;
	  else
	    {
	      if (info->shared
		  && (((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
		       && cie.make_relative == 0)
		      || (cie.fde_encoding & 0xf0) == DW_EH_PE_aligned))
		{
		  /* If a shared library uses absolute pointers
		     which we cannot turn into PC relative,
		     don't create the binary search table,
		     since it is affected by runtime relocations.  */
		  hdr_info->table = FALSE;
		}
	      cie_usage_count++;
	      hdr_info->fde_count++;
	    }
	  /* Skip the initial location and address range.  */
	  start = buf;
	  length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
	  REQUIRE (skip_bytes (&buf, end, 2 * length));

	  /* Skip the augmentation size, if present.  */
	  if (cie.augmentation[0] == 'z')
	    REQUIRE (read_uleb128 (&buf, end, &length));
	  else
	    length = 0;

	  /* Of the supported augmentation characters above, only 'L'
	     adds augmentation data to the FDE.  This code would need to
	     be adjusted if any future augmentations do the same thing.  */
	  if (cie.lsda_encoding != DW_EH_PE_omit)
	    {
	      this_inf->lsda_offset = buf - start;
	      /* If there's no 'z' augmentation, we don't know where the
		 CFA insns begin.  Assume no padding.  */
	      if (cie.augmentation[0] != 'z')
		length = end - buf;
	    }

	  /* Skip over the augmentation data.  */
	  REQUIRE (skip_bytes (&buf, end, length));
	  insns = buf;

	  buf = last_fde + 4 + hdr.length;
	  SKIP_RELOCS (buf);
	}

      /* Try to interpret the CFA instructions and find the first
	 padding nop.  Shrink this_inf's size so that it doesn't
	 including the padding.  */
      length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
      insns = skip_non_nops (insns, end, length);
      if (insns != 0)
	this_inf->size -= end - insns;

      this_inf->fde_encoding = cie.fde_encoding;
      this_inf->lsda_encoding = cie.lsda_encoding;
      sec_info->count++;
    }

  elf_section_data (sec)->sec_info = sec_info;
  sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;

  /* Ok, now we can assign new offsets.  */
  offset = 0;
  last_cie_inf = hdr_info->last_cie_inf;
  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
    if (!ent->removed)
      {
	if (ent->cie)
	  last_cie_inf = ent;
	else
	  ent->cie_inf = last_cie_inf;
	ent->new_offset = offset;
	offset += size_of_output_cie_fde (ent, ptr_size);
      }
  hdr_info->last_cie_inf = last_cie_inf;

  /* Resize the sec as needed.  */
  sec->rawsize = sec->size;
  sec->size = offset;
  if (sec->size == 0)
    sec->flags |= SEC_EXCLUDE;

  free (ehbuf);
  return offset != sec->rawsize;

free_no_table:
  if (ehbuf)
    free (ehbuf);
  if (sec_info)
    free (sec_info);
  hdr_info->table = FALSE;
  hdr_info->last_cie.hdr.length = 0;
  return FALSE;

#undef REQUIRE
}
bfd_boolean
_bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
{
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  unsigned int align_size = bed->s->elfclass == ELFCLASS64 ? 8 : 4;
  bfd_byte *ptr = (bfd_byte *) note->descdata;
  bfd_byte *ptr_end = ptr + note->descsz;

  if (note->descsz < 8 || (note->descsz % align_size) != 0)
    {
bad_size:
      _bfd_error_handler
	(_("warning: %pB: corrupt GNU_PROPERTY_TYPE (%ld) size: %#lx"),
	 abfd, note->type, note->descsz);
      return FALSE;
    }

  while (ptr != ptr_end)
    {
      unsigned int type;
      unsigned int datasz;
      elf_property *prop;

      if ((size_t) (ptr_end - ptr) < 8)
	goto bad_size;

      type = bfd_h_get_32 (abfd, ptr);
      datasz = bfd_h_get_32 (abfd, ptr + 4);
      ptr += 8;

      if (datasz > (size_t) (ptr_end - ptr))
	{
	  _bfd_error_handler
	    (_("warning: %pB: corrupt GNU_PROPERTY_TYPE (%ld) type (0x%x) datasz: 0x%x"),
	     abfd, note->type, type, datasz);
	  /* Clear all properties.  */
	  elf_properties (abfd) = NULL;
	  return FALSE;
	}

      if (type >= GNU_PROPERTY_LOPROC)
	{
	  if (bed->elf_machine_code == EM_NONE)
	    {
	      /* Ignore processor-specific properties with generic ELF
		 target vector.  They should be handled by the matching
		 ELF target vector.  */
	      goto next;
	    }
	  else if (type < GNU_PROPERTY_LOUSER
		   && bed->parse_gnu_properties)
	    {
	      enum elf_property_kind kind
		= bed->parse_gnu_properties (abfd, type, ptr, datasz);
	      if (kind == property_corrupt)
		{
		  /* Clear all properties.  */
		  elf_properties (abfd) = NULL;
		  return FALSE;
		}
	      else if (kind != property_ignored)
		goto next;
	    }
	}
      else
	{
	  switch (type)
	    {
	    case GNU_PROPERTY_STACK_SIZE:
	      if (datasz != align_size)
		{
		  _bfd_error_handler
		    (_("warning: %pB: corrupt stack size: 0x%x"),
		     abfd, datasz);
		  /* Clear all properties.  */
		  elf_properties (abfd) = NULL;
		  return FALSE;
		}
	      prop = _bfd_elf_get_property (abfd, type, datasz);
	      if (datasz == 8)
		prop->u.number = bfd_h_get_64 (abfd, ptr);
	      else
		prop->u.number = bfd_h_get_32 (abfd, ptr);
	      prop->pr_kind = property_number;
	      goto next;

	    case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
	      if (datasz != 0)
		{
		  _bfd_error_handler
		    (_("warning: %pB: corrupt no copy on protected size: 0x%x"),
		     abfd, datasz);
		  /* Clear all properties.  */
		  elf_properties (abfd) = NULL;
		  return FALSE;
		}
	      prop = _bfd_elf_get_property (abfd, type, datasz);
	      elf_has_no_copy_on_protected (abfd) = TRUE;
	      prop->pr_kind = property_number;
	      goto next;

	    default:
	      break;
	    }
	}

      _bfd_error_handler
	(_("warning: %pB: unsupported GNU_PROPERTY_TYPE (%ld) type: 0x%x"),
	 abfd, note->type, type);

next:
      ptr += (datasz + (align_size - 1)) & ~ (align_size - 1);
    }

  return TRUE;
}
bfd_boolean
_bfd_elf_discard_section_eh_frame
   (bfd *abfd, struct bfd_link_info *info, asection *sec,
    bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
    struct elf_reloc_cookie *cookie)
{
  bfd_byte *ehbuf = NULL, *buf;
  bfd_byte *last_cie, *last_fde;
  struct cie_header hdr;
  struct cie cie;
  struct elf_link_hash_table *htab;
  struct eh_frame_hdr_info *hdr_info;
  struct eh_frame_sec_info *sec_info = NULL;
  unsigned int leb128_tmp;
  unsigned int cie_usage_count, last_cie_ndx, i, offset;
  unsigned int make_relative, make_lsda_relative;
  bfd_size_type new_size;
  unsigned int ptr_size;

  if (sec->_raw_size == 0)
    {
      /* This file does not contain .eh_frame information.  */
      return FALSE;
    }

  if ((sec->output_section != NULL
       && bfd_is_abs_section (sec->output_section)))
    {
      /* At least one of the sections is being discarded from the
         link, so we should just ignore them.  */
      return FALSE;
    }

  htab = elf_hash_table (info);
  hdr_info = &htab->eh_info;

  /* Read the frame unwind information from abfd.  */

  ehbuf = bfd_malloc (sec->_raw_size);
  if (ehbuf == NULL)
    goto free_no_table;

  if (! bfd_get_section_contents (abfd, sec, ehbuf, 0, sec->_raw_size))
    goto free_no_table;

  if (sec->_raw_size >= 4
      && bfd_get_32 (abfd, ehbuf) == 0
      && cookie->rel == cookie->relend)
    {
      /* Empty .eh_frame section.  */
      free (ehbuf);
      return FALSE;
    }

  /* If .eh_frame section size doesn't fit into int, we cannot handle
     it (it would need to use 64-bit .eh_frame format anyway).  */
  if (sec->_raw_size != (unsigned int) sec->_raw_size)
    goto free_no_table;

  ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
	      == ELFCLASS64) ? 8 : 4;
  buf = ehbuf;
  last_cie = NULL;
  last_cie_ndx = 0;
  memset (&cie, 0, sizeof (cie));
  cie_usage_count = 0;
  new_size = sec->_raw_size;
  make_relative = hdr_info->last_cie.make_relative;
  make_lsda_relative = hdr_info->last_cie.make_lsda_relative;
  sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
			  + 99 * sizeof (struct eh_cie_fde));
  if (sec_info == NULL)
    goto free_no_table;
  sec_info->alloced = 100;

#define ENSURE_NO_RELOCS(buf)				\
  if (cookie->rel < cookie->relend			\
      && (cookie->rel->r_offset				\
	  < (bfd_size_type) ((buf) - ehbuf))		\
      && cookie->rel->r_info != 0)			\
    goto free_no_table

#define SKIP_RELOCS(buf)				\
  while (cookie->rel < cookie->relend			\
         && (cookie->rel->r_offset			\
	     < (bfd_size_type) ((buf) - ehbuf)))	\
    cookie->rel++

#define GET_RELOC(buf)					\
  ((cookie->rel < cookie->relend			\
    && (cookie->rel->r_offset				\
        == (bfd_size_type) ((buf) - ehbuf)))		\
   ? cookie->rel : NULL)

  for (;;)
    {
      unsigned char *aug;

      if (sec_info->count == sec_info->alloced)
	{
	  sec_info = bfd_realloc (sec_info,
				  sizeof (struct eh_frame_sec_info)
				  + (sec_info->alloced + 99)
				     * sizeof (struct eh_cie_fde));
	  if (sec_info == NULL)
	    goto free_no_table;

	  memset (&sec_info->entry[sec_info->alloced], 0,
		  100 * sizeof (struct eh_cie_fde));
	  sec_info->alloced += 100;
	}

      last_fde = buf;
      /* If we are at the end of the section, we still need to decide
	 on whether to output or discard last encountered CIE (if any).  */
      if ((bfd_size_type) (buf - ehbuf) == sec->_raw_size)
	hdr.id = (unsigned int) -1;
      else
	{
	  if ((bfd_size_type) (buf + 4 - ehbuf) > sec->_raw_size)
	    /* No space for CIE/FDE header length.  */
	    goto free_no_table;

	  hdr.length = bfd_get_32 (abfd, buf);
	  if (hdr.length == 0xffffffff)
	    /* 64-bit .eh_frame is not supported.  */
	    goto free_no_table;
	  buf += 4;
	  if ((bfd_size_type) (buf - ehbuf) + hdr.length > sec->_raw_size)
	    /* CIE/FDE not contained fully in this .eh_frame input section.  */
	    goto free_no_table;

	  sec_info->entry[sec_info->count].offset = last_fde - ehbuf;
	  sec_info->entry[sec_info->count].size = 4 + hdr.length;

	  if (hdr.length == 0)
	    {
	      /* CIE with length 0 must be only the last in the section.  */
	      if ((bfd_size_type) (buf - ehbuf) < sec->_raw_size)
		goto free_no_table;
	      ENSURE_NO_RELOCS (buf);
	      sec_info->count++;
	      /* Now just finish last encountered CIE processing and break
		 the loop.  */
	      hdr.id = (unsigned int) -1;
	    }
	  else
	    {
	      hdr.id = bfd_get_32 (abfd, buf);
	      buf += 4;
	      if (hdr.id == (unsigned int) -1)
		goto free_no_table;
	    }
	}

      if (hdr.id == 0 || hdr.id == (unsigned int) -1)
	{
	  unsigned int initial_insn_length;

	  /* CIE  */
	  if (last_cie != NULL)
	    {
	      /* Now check if this CIE is identical to the last CIE,
		 in which case we can remove it provided we adjust
		 all FDEs.  Also, it can be removed if we have removed
		 all FDEs using it.  */
	      if ((!info->relocatable
		   && hdr_info->last_cie_sec
		   && (sec->output_section
		       == hdr_info->last_cie_sec->output_section)
		   && cie_compare (&cie, &hdr_info->last_cie) == 0)
		  || cie_usage_count == 0)
		{
		  new_size -= cie.hdr.length + 4;
		  sec_info->entry[last_cie_ndx].removed = 1;
		  sec_info->entry[last_cie_ndx].sec = hdr_info->last_cie_sec;
		  sec_info->entry[last_cie_ndx].new_offset
		    = hdr_info->last_cie_offset;
		}
	      else
		{
		  hdr_info->last_cie = cie;
		  hdr_info->last_cie_sec = sec;
		  hdr_info->last_cie_offset = last_cie - ehbuf;
		  sec_info->entry[last_cie_ndx].make_relative
		    = cie.make_relative;
		  sec_info->entry[last_cie_ndx].make_lsda_relative
		    = cie.make_lsda_relative;
		  sec_info->entry[last_cie_ndx].per_encoding_relative
		    = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
		}
	    }

	  if (hdr.id == (unsigned int) -1)
	    break;

	  last_cie_ndx = sec_info->count;
	  sec_info->entry[sec_info->count].cie = 1;

	  cie_usage_count = 0;
	  memset (&cie, 0, sizeof (cie));
	  cie.hdr = hdr;
	  cie.version = *buf++;

	  /* Cannot handle unknown versions.  */
	  if (cie.version != 1)
	    goto free_no_table;
	  if (strlen (buf) > sizeof (cie.augmentation) - 1)
	    goto free_no_table;

	  strcpy (cie.augmentation, buf);
	  buf = strchr (buf, '\0') + 1;
	  ENSURE_NO_RELOCS (buf);
	  if (buf[0] == 'e' && buf[1] == 'h')
	    {
	      /* GCC < 3.0 .eh_frame CIE */
	      /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
		 is private to each CIE, so we don't need it for anything.
		 Just skip it.  */
	      buf += ptr_size;
	      SKIP_RELOCS (buf);
	    }
	  read_uleb128 (cie.code_align, buf);
	  read_sleb128 (cie.data_align, buf);
	  /* Note - in DWARF2 the return address column is an unsigned byte.
	     In DWARF3 it is a ULEB128.  We are following DWARF3.  For most
	     ports this will not matter as the value will be less than 128.
	     For the others (eg FRV, SH, MMIX, IA64) they need a fixed GCC
	     which conforms to the DWARF3 standard.  */
	  read_uleb128 (cie.ra_column, buf);
	  ENSURE_NO_RELOCS (buf);
	  cie.lsda_encoding = DW_EH_PE_omit;
	  cie.fde_encoding = DW_EH_PE_omit;
	  cie.per_encoding = DW_EH_PE_omit;
	  aug = cie.augmentation;
	  if (aug[0] != 'e' || aug[1] != 'h')
	    {
	      if (*aug == 'z')
		{
		  aug++;
		  read_uleb128 (cie.augmentation_size, buf);
	  	  ENSURE_NO_RELOCS (buf);
		}

	      while (*aug != '\0')
		switch (*aug++)
		  {
		  case 'L':
		    cie.lsda_encoding = *buf++;
		    ENSURE_NO_RELOCS (buf);
		    if (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size) == 0)
		      goto free_no_table;
		    break;
		  case 'R':
		    cie.fde_encoding = *buf++;
		    ENSURE_NO_RELOCS (buf);
		    if (get_DW_EH_PE_width (cie.fde_encoding, ptr_size) == 0)
		      goto free_no_table;
		    break;
		  case 'P':
		    {
		      int per_width;

		      cie.per_encoding = *buf++;
		      per_width = get_DW_EH_PE_width (cie.per_encoding,
						      ptr_size);
		      if (per_width == 0)
			goto free_no_table;
		      if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
			buf = (ehbuf
			       + ((buf - ehbuf + per_width - 1)
				  & ~((bfd_size_type) per_width - 1)));
		      ENSURE_NO_RELOCS (buf);
		      /* Ensure we have a reloc here, against
			 a global symbol.  */
		      if (GET_RELOC (buf) != NULL)
			{
			  unsigned long r_symndx;

#ifdef BFD64
			  if (ptr_size == 8)
			    r_symndx = ELF64_R_SYM (cookie->rel->r_info);
			  else
#endif
			    r_symndx = ELF32_R_SYM (cookie->rel->r_info);
			  if (r_symndx >= cookie->locsymcount)
			    {
			      struct elf_link_hash_entry *h;

			      r_symndx -= cookie->extsymoff;
			      h = cookie->sym_hashes[r_symndx];

			      while (h->root.type == bfd_link_hash_indirect
				     || h->root.type == bfd_link_hash_warning)
				h = (struct elf_link_hash_entry *)
				    h->root.u.i.link;

			      cie.personality = h;
			    }
			  cookie->rel++;
			}
		      buf += per_width;
		    }
		    break;
		  default:
		    /* Unrecognized augmentation. Better bail out.  */
		    goto free_no_table;
		  }
	    }

	  /* For shared libraries, try to get rid of as many RELATIVE relocs
	     as possible.  */
          if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_relative_eh_frame
		  (abfd, info, sec))
	      && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
	    cie.make_relative = 1;

	  if (info->shared
	      && (get_elf_backend_data (abfd)
		  ->elf_backend_can_make_lsda_relative_eh_frame
		  (abfd, info, sec))
	      && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
	    cie.make_lsda_relative = 1;

	  /* If FDE encoding was not specified, it defaults to
	     DW_EH_absptr.  */
	  if (cie.fde_encoding == DW_EH_PE_omit)
	    cie.fde_encoding = DW_EH_PE_absptr;

	  initial_insn_length = cie.hdr.length - (buf - last_fde - 4);
	  if (initial_insn_length <= 50)
	    {
	      cie.initial_insn_length = initial_insn_length;
	      memcpy (cie.initial_instructions, buf, initial_insn_length);
	    }
	  buf += initial_insn_length;
	  ENSURE_NO_RELOCS (buf);
	  last_cie = last_fde;
	}
      else
	{
	  /* Ensure this FDE uses the last CIE encountered.  */
	  if (last_cie == NULL
	      || hdr.id != (unsigned int) (buf - 4 - last_cie))
	    goto free_no_table;

	  ENSURE_NO_RELOCS (buf);
	  if (GET_RELOC (buf) == NULL)
	    /* This should not happen.  */
	    goto free_no_table;
	  if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
	    {
	      /* This is a FDE against a discarded section.  It should
		 be deleted.  */
	      new_size -= hdr.length + 4;
	      sec_info->entry[sec_info->count].removed = 1;
	    }
	  else
	    {
	      if (info->shared
		  && (((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
		       && cie.make_relative == 0)
		      || (cie.fde_encoding & 0xf0) == DW_EH_PE_aligned))
		{
		  /* If a shared library uses absolute pointers
		     which we cannot turn into PC relative,
		     don't create the binary search table,
		     since it is affected by runtime relocations.  */
		  hdr_info->table = FALSE;
		}
	      cie_usage_count++;
	      hdr_info->fde_count++;
	    }
	  if (cie.lsda_encoding != DW_EH_PE_omit)
	    {
	      unsigned int dummy;

	      aug = buf;
	      buf += 2 * get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
	      if (cie.augmentation[0] == 'z')
		read_uleb128 (dummy, buf);
	      /* If some new augmentation data is added before LSDA
		 in FDE augmentation area, this need to be adjusted.  */
	      sec_info->entry[sec_info->count].lsda_offset = (buf - aug);
	    }
	  buf = last_fde + 4 + hdr.length;
	  SKIP_RELOCS (buf);
	}

      sec_info->entry[sec_info->count].fde_encoding = cie.fde_encoding;
      sec_info->entry[sec_info->count].lsda_encoding = cie.lsda_encoding;
      sec_info->count++;
    }

  elf_section_data (sec)->sec_info = sec_info;
  sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;

  /* Ok, now we can assign new offsets.  */
  offset = 0;
  last_cie_ndx = 0;
  for (i = 0; i < sec_info->count; i++)
    {
      if (! sec_info->entry[i].removed)
	{
	  sec_info->entry[i].new_offset = offset;
	  offset += sec_info->entry[i].size;
	  if (sec_info->entry[i].cie)
	    {
	      last_cie_ndx = i;
	      make_relative = sec_info->entry[i].make_relative;
	      make_lsda_relative = sec_info->entry[i].make_lsda_relative;
	    }
	  else
	    {
	      sec_info->entry[i].make_relative = make_relative;
	      sec_info->entry[i].make_lsda_relative = make_lsda_relative;
	      sec_info->entry[i].per_encoding_relative = 0;
	    }
	}
      else if (sec_info->entry[i].cie && sec_info->entry[i].sec == sec)
	{
	  /* Need to adjust new_offset too.  */
	  BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
		      == sec_info->entry[i].new_offset);
	  sec_info->entry[i].new_offset
	    = sec_info->entry[last_cie_ndx].new_offset;
	}
    }
  if (hdr_info->last_cie_sec == sec)
    {
      BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
		  == hdr_info->last_cie_offset);
      hdr_info->last_cie_offset = sec_info->entry[last_cie_ndx].new_offset;
    }

  /* FIXME: Currently it is not possible to shrink sections to zero size at
     this point, so build a fake minimal CIE.  */
  if (new_size == 0)
    new_size = 16;

  /* Shrink the sec as needed.  */
  sec->_cooked_size = new_size;
  if (sec->_cooked_size == 0)
    sec->flags |= SEC_EXCLUDE;

  free (ehbuf);
  return new_size != sec->_raw_size;

free_no_table:
  if (ehbuf)
    free (ehbuf);
  if (sec_info)
    free (sec_info);
  hdr_info->table = FALSE;
  hdr_info->last_cie.hdr.length = 0;
  return FALSE;
}