summaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2004-08-19 07:47:25 +0000
committerJakub Jelinek <jakub@redhat.com>2004-08-19 07:47:25 +0000
commitc667ad7bdc5598c43cdce9fea8e72a70c49b75db (patch)
tree662ccb24d100316de60fd10e4241f8f73677fc74 /bfd/elf64-ppc.c
parent58ddb73058fd5a5f961bc0131400cf95e1ade405 (diff)
downloadgdb-c667ad7bdc5598c43cdce9fea8e72a70c49b75db.tar.gz
* elf64-ppc.c (ppc64_elf_edit_opd): Support 16 byte long .opd
entries (where fd_aux overlaps next entry's fd_func). Add non_overlapping argument, use it. (ppc64_elf_check_relocs, ppc64_elf_gc_mark_hook, adjust_opd_syms, ppc64_elf_size_stubs, ppc64_elf_relocate_section, ppc64_elf_output_symbol_hook): Use address / 8 instead of address / 24 as indexes into opd_sym_map/opd_adjust array. * elf64-ppc.h (ppc64_elf_edit_opd): Adjust prototype. * emultempl/ppc64elf.em (non_overlapping_opd): New variable. (ppc_before_allocation): Pass it to ppc64_elf_edit_opd). (OPTION_NON_OVERLAPPING_OPD): Define. (PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add --non-overlapping-opd option.
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c163
1 files changed, 137 insertions, 26 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 33ffd3906c2..d44d8d3d452 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -4206,7 +4206,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
};
bfd_size_type amt;
- amt = sec->size * sizeof (union opd_info) / 24;
+ amt = sec->size * sizeof (union opd_info) / 8;
opd_sym_map = bfd_zalloc (abfd, amt);
if (opd_sym_map == NULL)
return FALSE;
@@ -4513,7 +4513,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (s == NULL)
return FALSE;
else if (s != sec)
- opd_sym_map[rel->r_offset / 24] = s;
+ opd_sym_map[rel->r_offset / 8] = s;
}
}
/* Fall through. */
@@ -4892,7 +4892,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
if (!rsec->gc_mark)
_bfd_elf_gc_mark (info, rsec, ppc64_elf_gc_mark_hook);
- rsec = opd_sym_section[sym->st_value / 24];
+ rsec = opd_sym_section[sym->st_value / 8];
}
}
@@ -5674,7 +5674,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
opd_adjust = get_opd_info (sym_sec);
if (opd_adjust != NULL)
{
- long adjust = opd_adjust[eh->elf.root.u.def.value / 24];
+ long adjust = opd_adjust[eh->elf.root.u.def.value / 8];
if (adjust == -1)
{
/* This entry has been deleted. */
@@ -5705,10 +5705,12 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
applications. */
bfd_boolean
-ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
+ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
+ bfd_boolean non_overlapping)
{
bfd *ibfd;
bfd_boolean some_edited = FALSE;
+ asection *need_pad = NULL;
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
{
@@ -5720,13 +5722,14 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
bfd_vma offset;
bfd_size_type amt;
long *opd_adjust;
- bfd_boolean need_edit;
+ bfd_boolean need_edit, add_aux_fields;
+ bfd_size_type cnt_16b = 0;
sec = bfd_get_section_by_name (ibfd, ".opd");
if (sec == NULL)
continue;
- amt = sec->size * sizeof (long) / 24;
+ amt = sec->size * sizeof (long) / 8;
opd_adjust = get_opd_info (sec);
if (opd_adjust == NULL)
{
@@ -5757,6 +5760,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
/* First run through the relocs to check they are sane, and to
determine whether we need to edit this opd section. */
need_edit = FALSE;
+ need_pad = sec;
offset = 0;
relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; )
@@ -5767,7 +5771,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
- /* .opd contains a regular array of 24 byte entries. We're
+ /* .opd contains a regular array of 16 or 24 byte entries. We're
only interested in the reloc pointing to a function entry
point. */
if (rel->r_offset != offset
@@ -5779,6 +5783,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
Also, there's nothing to prevent someone putting
something silly in .opd with the assembler. No .opd
optimization for them! */
+ broken_opd:
(*_bfd_error_handler)
(_("%B: .opd is not a regular array of opd entries"), ibfd);
need_edit = FALSE;
@@ -5826,19 +5831,54 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
|| sym_sec->output_section == bfd_abs_section_ptr)
need_edit = TRUE;
- offset += 24;
rel += 2;
- /* Allow for the possibility of a reloc on the third word. */
- if (rel < relend
- && rel->r_offset == offset - 8)
- rel += 1;
+ if (rel == relend
+ || (rel + 1 == relend && rel->r_offset == offset + 16))
+ {
+ if (sec->size == offset + 24)
+ {
+ need_pad = NULL;
+ break;
+ }
+ if (rel == relend && sec->size == offset + 16)
+ {
+ cnt_16b++;
+ break;
+ }
+ goto broken_opd;
+ }
+
+ if (rel->r_offset == offset + 24)
+ offset += 24;
+ else if (rel->r_offset != offset + 16)
+ goto broken_opd;
+ else if (rel + 1 < relend
+ && ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
+ && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
+ {
+ offset += 16;
+ cnt_16b++;
+ }
+ else if (rel + 2 < relend
+ && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
+ && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
+ {
+ offset += 24;
+ rel += 1;
+ }
+ else
+ goto broken_opd;
}
- if (need_edit)
+ add_aux_fields = non_overlapping && cnt_16b > 0;
+
+ if (need_edit || add_aux_fields)
{
Elf_Internal_Rela *write_rel;
bfd_byte *rptr, *wptr;
+ bfd_byte *new_contents = NULL;
bfd_boolean skip;
+ long opd_ent_size;
/* This seems a waste of time as input .opd sections are all
zeros as generated by gcc, but I suppose there's no reason
@@ -5867,9 +5907,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
wptr = sec->contents;
rptr = sec->contents;
+ new_contents = sec->contents;
+
+ if (add_aux_fields)
+ {
+ new_contents = bfd_malloc (sec->size + cnt_16b * 8);
+ if (new_contents == NULL)
+ return FALSE;
+ need_pad = FALSE;
+ wptr = new_contents;
+ }
+
write_rel = relstart;
skip = FALSE;
offset = 0;
+ opd_ent_size = 0;
for (rel = relstart; rel < relend; rel++)
{
unsigned long r_symndx;
@@ -5885,6 +5937,19 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
if (rel->r_offset == offset)
{
struct ppc_link_hash_entry *fdh = NULL;
+
+ /* See if the .opd entry is full 24 byte or
+ 16 byte (with fd_aux entry overlapped with next
+ fd_func). */
+ opd_ent_size = 24;
+ if ((rel + 2 == relend && sec->size == offset + 16)
+ || (rel + 3 < relend
+ && rel[2].r_offset == offset + 16
+ && rel[3].r_offset == offset + 24
+ && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
+ && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+ opd_ent_size = 16;
+
if (h != NULL
&& h->root.root.string[0] == '.')
fdh = get_fdh ((struct ppc_link_hash_entry *) h,
@@ -5901,7 +5966,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
fdh->elf.root.u.def.value = 0;
fdh->elf.root.u.def.section = sym_sec;
}
- opd_adjust[rel->r_offset / 24] = -1;
+ opd_adjust[rel->r_offset / 8] = -1;
}
else
{
@@ -5916,7 +5981,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
for local symbols, because various places
in the generic ELF code use the value
stored in u.def.value. */
- fdh->elf.root.u.def.value = wptr - sec->contents;
+ fdh->elf.root.u.def.value = wptr - new_contents;
fdh->adjust_done = 1;
}
@@ -5926,14 +5991,20 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
for the function descriptor sym which we
don't have at the moment. So keep an
array of adjustments. */
- opd_adjust[rel->r_offset / 24] = wptr - rptr;
+ opd_adjust[rel->r_offset / 8]
+ = (wptr - new_contents) - (rptr - sec->contents);
if (wptr != rptr)
- memcpy (wptr, rptr, 24);
- wptr += 24;
+ memcpy (wptr, rptr, opd_ent_size);
+ wptr += opd_ent_size;
+ if (add_aux_fields && opd_ent_size == 16)
+ {
+ memset (wptr, '\0', 8);
+ wptr += 8;
+ }
}
- rptr += 24;
- offset += 24;
+ rptr += opd_ent_size;
+ offset += opd_ent_size;
}
if (skip)
@@ -5971,15 +6042,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
/* We need to adjust any reloc offsets to point to the
new opd entries. While we're at it, we may as well
remove redundant relocs. */
- rel->r_offset += wptr - rptr;
+ rel->r_offset += opd_adjust[(offset - opd_ent_size) / 8];
if (write_rel != rel)
memcpy (write_rel, rel, sizeof (*rel));
++write_rel;
}
}
- sec->size = wptr - sec->contents;
+ sec->size = wptr - new_contents;
sec->reloc_count = write_rel - relstart;
+ if (add_aux_fields)
+ {
+ free (sec->contents);
+ sec->contents = new_contents;
+ }
+
/* Fudge the size too, as this is used later in
elf_bfd_final_link if we are emitting relocs. */
elf_section_data (sec)->rel_hdr.sh_size
@@ -6003,6 +6080,40 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
if (some_edited)
elf_link_hash_traverse (elf_hash_table (info), adjust_opd_syms, NULL);
+ /* If we are doing a final link and the last .opd entry is just 16 byte
+ long, add a 8 byte padding after it. */
+ if (need_pad != NULL && !info->relocatable)
+ {
+ bfd_byte *p;
+
+ if ((need_pad->flags & SEC_IN_MEMORY) == 0)
+ {
+ BFD_ASSERT (need_pad->size > 0);
+
+ p = bfd_malloc (need_pad->size + 8);
+ if (p == NULL)
+ return FALSE;
+
+ if (! bfd_get_section_contents (need_pad->owner, need_pad,
+ p, 0, need_pad->size))
+ return FALSE;
+
+ need_pad->contents = p;
+ need_pad->flags |= (SEC_IN_MEMORY | SEC_HAS_CONTENTS);
+ }
+ else
+ {
+ p = bfd_realloc (need_pad->contents, need_pad->size + 8);
+ if (p == NULL)
+ return FALSE;
+
+ need_pad->contents = p;
+ }
+
+ memset (need_pad->contents + need_pad->size, 0, 8);
+ need_pad->size += 8;
+ }
+
return TRUE;
}
@@ -7770,7 +7881,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
if (hash == NULL)
{
- long adjust = opd_adjust[sym_value / 24];
+ long adjust = opd_adjust[sym_value / 8];
if (adjust == -1)
continue;
sym_value += adjust;
@@ -8308,7 +8419,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
opd_adjust = get_opd_info (sec);
if (opd_adjust != NULL)
{
- long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 24];
+ long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 8];
if (adjust == -1)
relocation = 0;
else
@@ -9604,7 +9715,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
if (!info->relocatable)
value -= input_sec->output_section->vma;
- adjust = opd_adjust[value / 24];
+ adjust = opd_adjust[value / 8];
if (adjust == -1)
elfsym->st_value = 0;
else