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:
Alan Modra
2017-04-27 11:08:14 +09:30
parent 641338d8e9
commit d7153c4ac3
4 changed files with 185 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)