/* .eh_frame section optimization.
- Copyright (C) 2001-2017 Free Software Foundation, Inc.
+ Copyright (C) 2001-2020 Free Software Foundation, Inc.
Written by Jakub Jelinek <jakub@redhat.com>.
This file is part of BFD, the Binary File Descriptor library.
/* Return the offset of the FDE or CIE after ENT. */
static unsigned int
-next_cie_fde_offset (struct eh_cie_fde *ent,
- struct eh_cie_fde *last,
- asection *sec)
+next_cie_fde_offset (const struct eh_cie_fde *ent,
+ const struct eh_cie_fde *last,
+ const asection *sec)
{
while (++ent < last)
{
hdr_info->array_count--;
hdr_info->u.compact.entries[hdr_info->array_count] = NULL;
i--;
- }
+ }
}
}
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);
sec_info = (struct eh_frame_sec_info *)
bfd_zmalloc (sizeof (struct eh_frame_sec_info)
- + (num_entries - 1) * sizeof (struct eh_cie_fde));
+ + (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 = (struct cie *) bfd_zmalloc (num_cies * sizeof (*local_cies));
- REQUIRE (local_cies);
+ if (num_cies)
+ {
+ local_cies = (struct cie *) bfd_zmalloc (num_cies * sizeof (*local_cies));
+ REQUIRE (local_cies);
+ }
/* FIXME: octets_per_byte. */
#define ENSURE_NO_RELOCS(buf) \
if (hdr_length == 0)
{
/* A zero-length CIE should only be found at the end of
- the section. */
+ the section, but allow multiple terminators. */
+ while (skip_bytes (&buf, ehbuf + sec->size, 4))
+ REQUIRE (bfd_get_32 (abfd, buf - 4) == 0);
REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
ENSURE_NO_RELOCS (buf);
sec_info->count++;
strcpy (cie->augmentation, (char *) buf);
buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
+ this_inf->u.cie.aug_str_len = buf - start - 1;
ENSURE_NO_RELOCS (buf);
if (buf[0] == 'e' && buf[1] == 'h')
{
{
aug++;
REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
- ENSURE_NO_RELOCS (buf);
+ ENSURE_NO_RELOCS (buf);
}
while (*aug != '\0')
switch (*aug++)
{
+ case 'B':
+ break;
case 'L':
REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
ENSURE_NO_RELOCS (buf);
goto free_no_table;
}
}
+ this_inf->u.cie.aug_data_len
+ = buf - start - 1 - this_inf->u.cie.aug_str_len;
/* For shared libraries, try to get rid of as many RELATIVE relocs
as possible. */
{
(*info->callbacks->minfo)
/* xgettext:c-format */
- (_("discarding zero address range FDE in %B(%A).\n"),
+ (_("discarding zero address range FDE in %pB(%pA).\n"),
abfd, sec);
this_inf->u.fde.cie_inf = NULL;
}
bfd_byte *p;
this_inf->set_loc = (unsigned int *)
- bfd_malloc ((set_loc_count + 1) * sizeof (unsigned int));
+ bfd_malloc ((set_loc_count + 1) * sizeof (unsigned int));
REQUIRE (this_inf->set_loc);
this_inf->set_loc[0] = set_loc_count;
p = insns;
goto success;
free_no_table:
- (*info->callbacks->einfo)
+ _bfd_error_handler
/* xgettext:c-format */
- (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
+ (_("error in %pB(%pA); no .eh_frame_hdr table will be created"),
abfd, sec);
hdr_info->u.dwarf.table = FALSE;
- if (sec_info)
- free (sec_info);
+ free (sec_info);
success:
- if (ehbuf)
- free (ehbuf);
- if (local_cies)
- free (local_cies);
+ free (ehbuf);
+ free (local_cies);
#undef REQUIRE
}
if (!sec->rawsize)
sec->rawsize = sec->size;
- bfd_set_section_size (sec->owner, sec, sec->size + 8);
+ bfd_set_section_size (sec, sec->size + 8);
}
/* Finish a pass over all .eh_frame_entry sections. */
return new_cie->cie_inf;
}
+/* For a given OFFSET in SEC, return the delta to the new location
+ after .eh_frame editing. */
+
+static bfd_signed_vma
+offset_adjust (bfd_vma offset, const asection *sec)
+{
+ struct eh_frame_sec_info *sec_info
+ = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
+ unsigned int lo, hi, mid;
+ struct eh_cie_fde *ent = NULL;
+ bfd_signed_vma delta;
+
+ lo = 0;
+ hi = sec_info->count;
+ if (hi == 0)
+ return 0;
+
+ while (lo < hi)
+ {
+ mid = (lo + hi) / 2;
+ ent = &sec_info->entry[mid];
+ if (offset < ent->offset)
+ hi = mid;
+ else if (mid + 1 >= hi)
+ break;
+ else if (offset >= ent[1].offset)
+ lo = mid + 1;
+ else
+ break;
+ }
+
+ if (!ent->removed)
+ delta = (bfd_vma) ent->new_offset - (bfd_vma) ent->offset;
+ else if (ent->cie && ent->u.cie.merged)
+ {
+ struct eh_cie_fde *cie = ent->u.cie.u.merged_with;
+ delta = ((bfd_vma) cie->new_offset + cie->u.cie.u.sec->output_offset
+ - (bfd_vma) ent->offset - sec->output_offset);
+ }
+ else
+ {
+ /* Is putting the symbol on the next entry best for a deleted
+ CIE/FDE? */
+ struct eh_cie_fde *last = sec_info->entry + sec_info->count;
+ delta = ((bfd_vma) next_cie_fde_offset (ent, last, sec)
+ - (bfd_vma) ent->offset);
+ return delta;
+ }
+
+ /* Account for editing within this CIE/FDE. */
+ offset -= ent->offset;
+ if (ent->cie)
+ {
+ unsigned int extra
+ = ent->add_augmentation_size + ent->u.cie.add_fde_encoding;
+ if (extra == 0
+ || offset <= 9u + ent->u.cie.aug_str_len)
+ return delta;
+ delta += extra;
+ if (offset <= 9u + ent->u.cie.aug_str_len + ent->u.cie.aug_data_len)
+ return delta;
+ delta += extra;
+ }
+ else
+ {
+ unsigned int ptr_size, width, extra = ent->add_augmentation_size;
+ if (offset <= 12 || extra == 0)
+ return delta;
+ ptr_size = (get_elf_backend_data (sec->owner)
+ ->elf_backend_eh_frame_address_size (sec->owner, sec));
+ width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ if (offset <= 8 + 2 * width)
+ return delta;
+ delta += extra;
+ }
+
+ return delta;
+}
+
+/* Adjust a global symbol defined in .eh_frame, so that it stays
+ relative to its original CIE/FDE. It is assumed that a symbol
+ defined at the beginning of a CIE/FDE belongs to that CIE/FDE
+ rather than marking the end of the previous CIE/FDE. This matters
+ when a CIE is merged with a previous CIE, since the symbol is
+ moved to the merged CIE. */
+
+bfd_boolean
+_bfd_elf_adjust_eh_frame_global_symbol (struct elf_link_hash_entry *h,
+ void *arg ATTRIBUTE_UNUSED)
+{
+ asection *sym_sec;
+ bfd_signed_vma delta;
+
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ return TRUE;
+
+ sym_sec = h->root.u.def.section;
+ if (sym_sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME
+ || elf_section_data (sym_sec)->sec_info == NULL)
+ return TRUE;
+
+ delta = offset_adjust (h->root.u.def.value, sym_sec);
+ h->root.u.def.value += delta;
+
+ return TRUE;
+}
+
+/* The same for all local symbols defined in .eh_frame. Returns true
+ if any symbol was changed. */
+
+static int
+adjust_eh_frame_local_symbols (const asection *sec,
+ struct elf_reloc_cookie *cookie)
+{
+ unsigned int shndx;
+ Elf_Internal_Sym *sym;
+ Elf_Internal_Sym *end_sym;
+ int adjusted = 0;
+
+ shndx = elf_section_data (sec)->this_idx;
+ end_sym = cookie->locsyms + cookie->locsymcount;
+ for (sym = cookie->locsyms + 1; sym < end_sym; ++sym)
+ if (sym->st_info <= ELF_ST_INFO (STB_LOCAL, STT_OBJECT)
+ && sym->st_shndx == shndx)
+ {
+ bfd_signed_vma delta = offset_adjust (sym->st_value, sec);
+
+ if (delta != 0)
+ {
+ adjusted = 1;
+ sym->st_value += delta;
+ }
+ }
+ return adjusted;
+}
+
/* This function is called for each input file before the .eh_frame
section is relocated. It discards duplicate CIEs and FDEs for discarded
functions. The function returns TRUE iff any entries have been
struct eh_frame_sec_info *sec_info;
struct eh_frame_hdr_info *hdr_info;
unsigned int ptr_size, offset, eh_alignment;
+ int changed;
if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
return FALSE;
don't create the binary search table,
since it is affected by runtime relocations. */
hdr_info->u.dwarf.table = FALSE;
- if (num_warnings_issued < 10)
- {
- (*info->callbacks->einfo)
- /* xgettext:c-format */
- (_("%P: FDE encoding in %B(%A) prevents .eh_frame_hdr"
- " table being created.\n"), abfd, sec);
- num_warnings_issued ++;
- }
- else if (num_warnings_issued == 10)
+ /* Only warn if --eh-frame-hdr was specified. */
+ if (info->eh_frame_hdr_type != 0)
{
- (*info->callbacks->einfo)
- (_("%P: Further warnings about FDE encoding preventing .eh_frame_hdr generation dropped.\n"));
- num_warnings_issued ++;
+ if (num_warnings_issued < 10)
+ {
+ _bfd_error_handler
+ /* xgettext:c-format */
+ (_("FDE encoding in %pB(%pA) prevents .eh_frame_hdr"
+ " table being created"), abfd, sec);
+ num_warnings_issued ++;
+ }
+ else if (num_warnings_issued == 10)
+ {
+ _bfd_error_handler
+ (_("further warnings about FDE encoding preventing .eh_frame_hdr generation dropped"));
+ num_warnings_issued ++;
+ }
}
}
ent->removed = 0;
}
}
- if (sec_info->cies)
- {
- free (sec_info->cies);
- sec_info->cies = NULL;
- }
+ free (sec_info->cies);
+ sec_info->cies = NULL;
/* It may be that some .eh_frame input section has greater alignment
than other .eh_frame sections. In that case we run the risk of
last FDE instead. For other FDEs we align according to their
encoding, in order to align FDE address range entries naturally. */
offset = 0;
+ changed = 0;
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
if (!ent->removed)
{
}
offset = (offset + eh_alignment - 1) & -eh_alignment;
ent->new_offset = offset;
+ if (ent->new_offset != ent->offset)
+ changed = 1;
offset += size_of_output_cie_fde (ent);
}
- /* Pad the last FDE out to the output section alignment if there are
- following sections, in order to ensure no padding between this
- section and the next. (Relies on the output section alignment
- being the maximum of all input sections alignments, which is the
- case unless someone is overriding alignment via scripts.) */
eh_alignment = 4;
- if (sec->map_head.s != NULL
- && (sec->map_head.s->size != 4
- || sec->map_head.s->map_head.s != NULL))
- eh_alignment = 1 << sec->output_section->alignment_power;
offset = (offset + eh_alignment - 1) & -eh_alignment;
sec->rawsize = sec->size;
sec->size = offset;
- return offset != sec->rawsize;
+ if (sec->size != sec->rawsize)
+ changed = 1;
+
+ if (changed && adjust_eh_frame_local_symbols (sec, cookie))
+ {
+ Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ symtab_hdr->contents = (unsigned char *) cookie->locsyms;
+ }
+ return changed;
}
/* This function is called for .eh_frame_hdr section after
if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
{
/* For compact frames we only add the header. The actual table comes
- from the .eh_frame_entry sections. */
+ from the .eh_frame_entry sections. */
sec->size = 8;
}
else
{
for (o = abfd->sections; o; o = o->next)
{
- const char *name = bfd_get_section_name (abfd, o);
+ const char *name = bfd_section_name (o);
if (strcmp (name, ".eh_frame_entry")
&& !bfd_is_abs_section (o->output_section))
if (addr <= last_addr)
{
/* xgettext:c-format */
- _bfd_error_handler (_("%B: %A not in order"), sec->owner, sec);
+ _bfd_error_handler (_("%pB: %pA not in order"), sec->owner, sec);
return FALSE;
}
if (addr & 1)
{
/* xgettext:c-format */
- _bfd_error_handler (_("%B: %A invalid input section size"),
+ _bfd_error_handler (_("%pB: %pA invalid input section size"),
sec->owner, sec);
bfd_set_error (bfd_error_bad_value);
return FALSE;
if (last_addr >= addr + sec->rawsize)
{
/* xgettext:c-format */
- _bfd_error_handler (_("%B: %A points past end of text section"),
+ _bfd_error_handler (_("%pB: %pA points past end of text section"),
sec->owner, sec);
bfd_set_error (bfd_error_bad_value);
return FALSE;
{
hdr_info->frame_hdr_is_compact = FALSE;
hdr_info->u.dwarf.array = (struct eh_frame_array_ent *)
- bfd_malloc (hdr_info->u.dwarf.fde_count
+ bfd_malloc (hdr_info->u.dwarf.fde_count
* sizeof (*hdr_info->u.dwarf.array));
}
if (hdr_info->u.dwarf.array == NULL)
|| ent->u.cie.per_encoding_relative)
{
char *aug;
- unsigned int action, extra_string, extra_data;
+ unsigned int version, action, extra_string, extra_data;
unsigned int per_width, per_encoding;
/* Need to find 'R' or 'L' augmentation's argument and modify
extra_string = extra_augmentation_string_bytes (ent);
extra_data = extra_augmentation_data_bytes (ent);
- /* Skip length, id and version. */
- buf += 9;
+ /* Skip length, id. */
+ buf += 8;
+ version = *buf++;
aug = (char *) buf;
buf += strlen (aug) + 1;
skip_leb128 (&buf, end);
skip_leb128 (&buf, end);
- skip_leb128 (&buf, end);
+ if (version == 1)
+ skip_bytes (&buf, end, 1);
+ else
+ skip_leb128 (&buf, end);
if (*aug == 'z')
{
/* The uleb128 will always be a single byte for the kind
address += elf_gp (abfd);
break;
default:
- (*info->callbacks->einfo)
- (_("%P: DW_EH_PE_datarel unspecified"
- " for this architecture.\n"));
+ _bfd_error_handler
+ (_("DW_EH_PE_datarel unspecified"
+ " for this architecture"));
/* Fall thru */
case bfd_arch_frv:
case bfd_arch_i386:
+ case bfd_arch_nios2:
BFD_ASSERT (htab->hgot != NULL
&& ((htab->hgot->root.type
== bfd_link_hash_defined)
if (sec->output_section != osec)
{
_bfd_error_handler
- (_("Invalid output section for .eh_frame_entry: %A"),
+ (_("invalid output section for .eh_frame_entry: %pA"),
sec->output_section);
return FALSE;
}
p->offset = p->u.indirect.section->output_offset;
if (p->next != NULL)
- i--;
+ i--;
}
if (i != 0)
{
_bfd_error_handler
- (_("Invalid contents in %A section"), osec);
+ (_("invalid contents in %pA section"), osec);
return FALSE;
}
/* The .eh_frame_hdr format for DWARF frames:
ubyte version (currently 1)
- ubyte eh_frame_ptr_enc (DW_EH_PE_* encoding of pointer to start of
+ ubyte eh_frame_ptr_enc (DW_EH_PE_* encoding of pointer to start of
.eh_frame section)
ubyte fde_count_enc (DW_EH_PE_* encoding of total FDE count
number (or DW_EH_PE_omit if there is no
overlap = TRUE;
}
if (overflow)
- (*info->callbacks->einfo) (_("%P: .eh_frame_hdr entry overflow.\n"));
+ _bfd_error_handler (_(".eh_frame_hdr entry overflow"));
if (overlap)
- (*info->callbacks->einfo)
- (_("%P: .eh_frame_hdr refers to overlapping FDEs.\n"));
+ _bfd_error_handler (_(".eh_frame_hdr refers to overlapping FDEs"));
if (overflow || overlap)
{
bfd_set_error (bfd_error_bad_value);
retval = FALSE;
free (contents);
- if (hdr_info->u.dwarf.array != NULL)
- free (hdr_info->u.dwarf.array);
+ free (hdr_info->u.dwarf.array);
return retval;
}
/* Return the width of FDE addresses. This is the default implementation. */
unsigned int
-_bfd_elf_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
+_bfd_elf_eh_frame_address_size (bfd *abfd, const asection *sec ATTRIBUTE_UNUSED)
{
return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4;
}