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

View File

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

View File

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

View File

@ -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))
{
eh_changed = 1;
if (i->size != i->rawsize)
changed = 1; 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)