mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-23 03:29:47 +08:00
Edit .eh_frame symbols
Experimental support for moving symbols defined in .eh_frame as their CIEs/FDEs are edited or merged. * elf-bfd.h (struct eh_cie_fde): Add aug_str_len and aug_data_len. (_bfd_elf_adjust_eh_frame_global_symbol): Declare. * elf-eh-frame.c (_bfd_elf_parse_eh_frame): Set aug_str_len and aug_data_len. (offset_adjust): New function. (_bfd_elf_adjust_eh_frame_global_symbol): Likewise. (adjust_eh_frame_local_symbols): Likewise. (_bfd_elf_discard_section_eh_frame): Call adjust_eh_frame_local_symbols after changing anything. Return true if anything changed. * elflink.c (bfd_elf_discard_info): If .eh_frame changed, call _bfd_elf_adjust_eh_frame_global_symbol for globals.
This commit is contained in:
@ -1,3 +1,17 @@
|
||||
2017-04-27 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* elf-bfd.h (struct eh_cie_fde): Add aug_str_len and aug_data_len.
|
||||
(_bfd_elf_adjust_eh_frame_global_symbol): Declare.
|
||||
* elf-eh-frame.c (_bfd_elf_parse_eh_frame): Set aug_str_len and
|
||||
aug_data_len.
|
||||
(offset_adjust): New function.
|
||||
(_bfd_elf_adjust_eh_frame_global_symbol): Likewise.
|
||||
(adjust_eh_frame_local_symbols): Likewise.
|
||||
(_bfd_elf_discard_section_eh_frame): Call adjust_eh_frame_local_symbols
|
||||
after changing anything. Return true if anything changed.
|
||||
* elflink.c (bfd_elf_discard_info): If .eh_frame changed, call
|
||||
_bfd_elf_adjust_eh_frame_global_symbol for globals.
|
||||
|
||||
2017-04-27 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* elflink.c (_bfd_elf_link_hash_hide_symbol): Clear dynstr_index
|
||||
|
@ -326,6 +326,12 @@ struct eh_cie_fde
|
||||
or 0 if the CIE doesn't have any. */
|
||||
unsigned int personality_offset : 8;
|
||||
|
||||
/* Length of augmentation. aug_str_len is the length of the
|
||||
string including null terminator. aug_data_len is the length
|
||||
of the rest up to the initial insns. */
|
||||
unsigned int aug_str_len : 3;
|
||||
unsigned int aug_data_len : 5;
|
||||
|
||||
/* True if we have marked relocations associated with this CIE. */
|
||||
unsigned int gc_mark : 1;
|
||||
|
||||
@ -354,7 +360,7 @@ struct eh_cie_fde
|
||||
unsigned int merged : 1;
|
||||
|
||||
/* Unused bits. */
|
||||
unsigned int pad1 : 17;
|
||||
unsigned int pad1 : 9;
|
||||
} cie;
|
||||
} u;
|
||||
unsigned int reloc_index;
|
||||
@ -2180,6 +2186,8 @@ extern bfd_boolean _bfd_elf_end_eh_frame_parsing
|
||||
extern bfd_boolean _bfd_elf_discard_section_eh_frame
|
||||
(bfd *, struct bfd_link_info *, asection *,
|
||||
bfd_boolean (*) (bfd_vma, void *), struct elf_reloc_cookie *);
|
||||
extern bfd_boolean _bfd_elf_adjust_eh_frame_global_symbol
|
||||
(struct elf_link_hash_entry *, void *);
|
||||
extern bfd_boolean _bfd_elf_discard_section_eh_frame_hdr
|
||||
(bfd *, struct bfd_link_info *);
|
||||
extern bfd_vma _bfd_elf_eh_frame_section_offset
|
||||
|
@ -757,6 +757,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
|
||||
|
||||
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')
|
||||
{
|
||||
@ -845,6 +846,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
|
||||
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. */
|
||||
@ -1327,6 +1330,143 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
|
||||
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, 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;
|
||||
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 (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
|
||||
@ -1342,6 +1482,7 @@ _bfd_elf_discard_section_eh_frame
|
||||
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;
|
||||
@ -1428,6 +1569,7 @@ _bfd_elf_discard_section_eh_frame
|
||||
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)
|
||||
{
|
||||
@ -1447,6 +1589,8 @@ _bfd_elf_discard_section_eh_frame
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1463,7 +1607,15 @@ _bfd_elf_discard_section_eh_frame
|
||||
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
|
||||
|
@ -13807,6 +13807,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
|
||||
if (o != NULL)
|
||||
{
|
||||
asection *i;
|
||||
int eh_changed = 0;
|
||||
|
||||
for (i = o->map_head.s; i != NULL; i = i->map_head.s)
|
||||
{
|
||||
@ -13824,10 +13825,17 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
|
||||
if (_bfd_elf_discard_section_eh_frame (abfd, info, i,
|
||||
bfd_elf_reloc_symbol_deleted_p,
|
||||
&cookie))
|
||||
changed = 1;
|
||||
{
|
||||
eh_changed = 1;
|
||||
if (i->size != i->rawsize)
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
fini_reloc_cookie_for_section (&cookie, i);
|
||||
}
|
||||
if (eh_changed)
|
||||
elf_link_hash_traverse (elf_hash_table (info),
|
||||
_bfd_elf_adjust_eh_frame_global_symbol, NULL);
|
||||
}
|
||||
|
||||
for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
|
||||
|
Reference in New Issue
Block a user