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>
|
2017-04-27 Alan Modra <amodra@gmail.com>
|
||||||
|
|
||||||
* elflink.c (_bfd_elf_link_hash_hide_symbol): Clear dynstr_index
|
* 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. */
|
or 0 if the CIE doesn't have any. */
|
||||||
unsigned int personality_offset : 8;
|
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. */
|
/* True if we have marked relocations associated with this CIE. */
|
||||||
unsigned int gc_mark : 1;
|
unsigned int gc_mark : 1;
|
||||||
|
|
||||||
@ -354,7 +360,7 @@ struct eh_cie_fde
|
|||||||
unsigned int merged : 1;
|
unsigned int merged : 1;
|
||||||
|
|
||||||
/* Unused bits. */
|
/* Unused bits. */
|
||||||
unsigned int pad1 : 17;
|
unsigned int pad1 : 9;
|
||||||
} cie;
|
} cie;
|
||||||
} u;
|
} u;
|
||||||
unsigned int reloc_index;
|
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
|
extern bfd_boolean _bfd_elf_discard_section_eh_frame
|
||||||
(bfd *, struct bfd_link_info *, asection *,
|
(bfd *, struct bfd_link_info *, asection *,
|
||||||
bfd_boolean (*) (bfd_vma, void *), struct elf_reloc_cookie *);
|
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
|
extern bfd_boolean _bfd_elf_discard_section_eh_frame_hdr
|
||||||
(bfd *, struct bfd_link_info *);
|
(bfd *, struct bfd_link_info *);
|
||||||
extern bfd_vma _bfd_elf_eh_frame_section_offset
|
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);
|
strcpy (cie->augmentation, (char *) buf);
|
||||||
buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
|
buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
|
||||||
|
this_inf->u.cie.aug_str_len = buf - start - 1;
|
||||||
ENSURE_NO_RELOCS (buf);
|
ENSURE_NO_RELOCS (buf);
|
||||||
if (buf[0] == 'e' && buf[1] == 'h')
|
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;
|
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
|
/* For shared libraries, try to get rid of as many RELATIVE relocs
|
||||||
as possible. */
|
as possible. */
|
||||||
@ -1327,6 +1330,143 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
|
|||||||
return new_cie->cie_inf;
|
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
|
/* This function is called for each input file before the .eh_frame
|
||||||
section is relocated. It discards duplicate CIEs and FDEs for discarded
|
section is relocated. It discards duplicate CIEs and FDEs for discarded
|
||||||
functions. The function returns TRUE iff any entries have been
|
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_sec_info *sec_info;
|
||||||
struct eh_frame_hdr_info *hdr_info;
|
struct eh_frame_hdr_info *hdr_info;
|
||||||
unsigned int ptr_size, offset, eh_alignment;
|
unsigned int ptr_size, offset, eh_alignment;
|
||||||
|
int changed;
|
||||||
|
|
||||||
if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
|
if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -1428,6 +1569,7 @@ _bfd_elf_discard_section_eh_frame
|
|||||||
last FDE instead. For other FDEs we align according to their
|
last FDE instead. For other FDEs we align according to their
|
||||||
encoding, in order to align FDE address range entries naturally. */
|
encoding, in order to align FDE address range entries naturally. */
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
changed = 0;
|
||||||
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
|
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
|
||||||
if (!ent->removed)
|
if (!ent->removed)
|
||||||
{
|
{
|
||||||
@ -1447,6 +1589,8 @@ _bfd_elf_discard_section_eh_frame
|
|||||||
}
|
}
|
||||||
offset = (offset + eh_alignment - 1) & -eh_alignment;
|
offset = (offset + eh_alignment - 1) & -eh_alignment;
|
||||||
ent->new_offset = offset;
|
ent->new_offset = offset;
|
||||||
|
if (ent->new_offset != ent->offset)
|
||||||
|
changed = 1;
|
||||||
offset += size_of_output_cie_fde (ent);
|
offset += size_of_output_cie_fde (ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1463,7 +1607,15 @@ _bfd_elf_discard_section_eh_frame
|
|||||||
offset = (offset + eh_alignment - 1) & -eh_alignment;
|
offset = (offset + eh_alignment - 1) & -eh_alignment;
|
||||||
sec->rawsize = sec->size;
|
sec->rawsize = sec->size;
|
||||||
sec->size = offset;
|
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
|
/* 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)
|
if (o != NULL)
|
||||||
{
|
{
|
||||||
asection *i;
|
asection *i;
|
||||||
|
int eh_changed = 0;
|
||||||
|
|
||||||
for (i = o->map_head.s; i != NULL; i = i->map_head.s)
|
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,
|
if (_bfd_elf_discard_section_eh_frame (abfd, info, i,
|
||||||
bfd_elf_reloc_symbol_deleted_p,
|
bfd_elf_reloc_symbol_deleted_p,
|
||||||
&cookie))
|
&cookie))
|
||||||
changed = 1;
|
{
|
||||||
|
eh_changed = 1;
|
||||||
|
if (i->size != i->rawsize)
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
fini_reloc_cookie_for_section (&cookie, i);
|
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)
|
for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
|
||||||
|
Reference in New Issue
Block a user