This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
elf64-ppc fixes for shared libs
- From: Alan Modra <amodra at bigpond dot net dot au>
- To: binutils at sources dot redhat dot com
- Date: Wed, 16 Jan 2002 16:17:44 +1030
- Subject: elf64-ppc fixes for shared libs
Lots of them. We were not in good shape.
bfd/ChangeLog
* elflink.c (_bfd_elf_create_dynamic_sections): Don't set SEC_CODE
when plt_not_loaded.
* elf64-ppc.c (ppc64_elf_create_dynamic_sections): No need to
clear .plt SEC_CODE here. Create .stub and correct .glink flags.
(PLT_INITIAL_ENTRY_SIZE): Set to 24.
(ppc64_elf_glink_code): Delete.
(PPC64_ELF_GLINK_SIZE): Delete.
(ADDIS_R12_R2, STD_R2_40R1, LD_R11_0R12, LD_R2_0R12, MTCTR_R11,
BCTR, ADDIS_R12_R12_1, LD_R2_40R1, NOP, LI_R0_0, B_DOT, LIS_R0_0,
ORI_R0_R0_0): Define.
(PLT_CALL_STUB_SIZE, GLINK_CALL_STUB_SIZE): Define.
(struct ppc_link_hash_table): Add sstub and plt_overflow.
(ppc64_elf_link_hash_table_create): Init them.
(ppc64_elf_check_relocs <R_PPC64_REL24>): Refcount .plt entry.
Don't copy to shared lib.
(ppc64_elf_check_relocs): Call bfd_set_error on errors.
(ppc64_elf_gc_sweep_hook <R_PPC64_REL24>): Sweep plt refcount.
(allocate_dynrelocs <plt>): Don't change function sym here. Make
room for .stub and .glink code.
(ppc64_elf_size_dynamic_sections): Handle .stub. Make entry for
DT_PPC64_GLINK.
(ppc64_elf_final_link): Rename to ppc64_elf_set_toc. Don't call
bfd_elf64_bfd_final_link.
(bfd_elf64_bfd_final_link): Don't define.
(ppc64_elf_size_stubs): New.
(build_plt_stub): New.
(build_one_stub): New.
(ppc64_elf_build_stubs): New.
(ppc64_elf_relocate_section <toc relocs>): Remove assert.
(ppc64_elf_relocate_section): Don't copy R_PPC64_REL24 relocs.
(ppc64_elf_finish_dynamic_symbol): Don't build stubs here. Set
DT_PPC64_GLINK entry. Tweak DT_PLTGOT, DT_JMPREL, DT_PLTRELSZ in
case output sections not separate. Adjust DT_RELASZ to not
include plt relocs. Set reserved got entry. Set got and plt
entry size.
(elf_backend_got_header_size): Set to 8.
* elf64-ppc.h: New file.
binutils/ChangeLog
* readelf.c (get_ppc64_dynamic_type): New.
(get_dynamic_type): Call it.
include/elf/ChangeLog
* ppc.h (DT_PPC64_GLINK): Define.
ld/ChangeLog
* Makefile.am (eelf64ppc.c, eelf64lppc.c): Depend on ppc64elf.em.
* Makefile.in: Regenerate.
* emulparams/elf64ppc.sh (EXTRA_EM_FILE): Define.
* emultempl/ppc64elf.em: New file.
--
Alan Modra
IBM OzLabs - Linux Technology Centre
Index: bfd/elf64-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-ppc.c,v
retrieving revision 1.15
diff -u -p -r1.15 elf64-ppc.c
--- elf64-ppc.c 2001/12/18 17:59:59 1.15
+++ elf64-ppc.c 2002/01/16 04:57:07
@@ -1,5 +1,5 @@
/* PowerPC64-specific support for 64-bit ELF.
- Copyright 1999, 2000, 2001 Free Software Foundation, Inc.
+ Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Written by Linus Nordberg, Swox AB <info@swox.com>,
based on elf32-ppc.c by Ian Lance Taylor.
@@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suit
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/ppc.h"
+#include "elf64-ppc.h"
#define USE_RELA /* we want RELA relocations, not REL. */
@@ -75,8 +76,10 @@ static enum elf_reloc_type_class ppc64_e
PARAMS ((const Elf_Internal_Rela *));
static boolean ppc64_elf_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
-static boolean ppc64_elf_final_link
- PARAMS ((bfd *, struct bfd_link_info *));
+static bfd_byte *build_plt_stub
+ PARAMS ((bfd *, bfd_byte *, int, int));
+static boolean build_one_stub
+ PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean ppc64_elf_fake_sections
PARAMS ((bfd *, Elf64_Internal_Shdr *, asection *));
static boolean ppc64_elf_relocate_section
@@ -104,24 +107,45 @@ static boolean ppc64_elf_finish_dynamic_
#define PLT_ENTRY_SIZE 24
/* The initial size of the plt reserved for the dynamic linker. */
-#define PLT_INITIAL_ENTRY_SIZE 96
+#define PLT_INITIAL_ENTRY_SIZE PLT_ENTRY_SIZE
/* TOC base pointers offset from start of TOC. */
#define TOC_BASE_OFF (0x8000)
-/* Global linkage stub. The first instruction has its offset patched. */
-/* FIXME: Need traceback table? */
-static const unsigned int ppc64_elf_glink_code[] = {
- 0xe9820000, /* ld %r12,0(%r2) */
- 0xf8410028, /* std %r2,40(%r1) */
- 0xe80c0000, /* ld %r0,0(%r12) */
- 0xe84c0008, /* ld %r2,8(%r12) */
- 0x7c0903a6, /* mtctr %r0 */
- 0x4e800420 /* bctr */
-};
-
-#define PPC64_ELF_GLINK_SIZE \
- ((sizeof ppc64_elf_glink_code / sizeof ppc64_elf_glink_code[0]) * 4)
+/* .plt call stub instructions. */
+#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
+#define STD_R2_40R1 0xf8410028 /* std %r2,40(%r1) */
+#define LD_R11_0R12 0xe96c0000 /* ld %r11,xxx+0@l(%r12) */
+#define LD_R2_0R12 0xe84c0000 /* ld %r2,xxx+8@l(%r12) */
+#define MTCTR_R11 0x7d6903a6 /* mtctr %r11 */
+ /* ld %r11,xxx+16@l(%r12) */
+#define BCTR 0x4e800420 /* bctr */
+
+/* The normal stub is this size. */
+#define PLT_CALL_STUB_SIZE (7*4)
+
+/* But sometimes the .plt entry crosses a 64k boundary, and we need
+ to adjust the high word with this insn. */
+#define ADDIS_R12_R12_1 0x3d8c0001 /* addis %r12,%r12,1 */
+
+/* The .glink fixup call stub is the same as the .plt call stub, but
+ the first instruction restores r2, and the std is omitted. */
+#define LD_R2_40R1 0xe8410028 /* ld %r2,40(%r1) */
+
+/* Always allow this much space. */
+#define GLINK_CALL_STUB_SIZE (8*4)
+
+/* Pad with this. */
+#define NOP 0x60000000
+
+/* .glink entries for the first 32k functions are two instructions. */
+#define LI_R0_0 0x38000000 /* li %r0,0 */
+#define B_DOT 0x48000000 /* b . */
+
+/* After that, we need two instructions to load the index, followed by
+ a branch. */
+#define LIS_R0_0 0x3c000000 /* lis %r0,0 */
+#define ORI_R0_R0_0 0x60008000 /* ori %r0,%r0,0 */
/* Relocation HOWTO's. */
static reloc_howto_type *ppc64_elf_howto_table[(int) R_PPC_max];
@@ -1618,8 +1642,12 @@ struct ppc_link_hash_table
asection *srelplt;
asection *sdynbss;
asection *srelbss;
+ asection *sstub;
asection *sglink;
+ /* Set on error. */
+ int plt_overflow;
+
/* Small local sym to section mapping cache. */
struct sym_sec_cache sym_sec;
};
@@ -1683,7 +1711,9 @@ ppc64_elf_link_hash_table_create (abfd)
htab->srelplt = NULL;
htab->sdynbss = NULL;
htab->srelbss = NULL;
+ htab->sstub = NULL;
htab->sglink = NULL;
+ htab->plt_overflow = 0;
htab->sym_sec.abfd = NULL;
return &htab->elf.root;
@@ -1718,8 +1748,8 @@ create_got_section (dynobj, info)
return true;
}
-/* Create the .glink section as well as the ordinary dynamic
- sections. */
+/* Create the .stub and .glink sections as well as the ordinary
+ dynamic sections. */
static boolean
ppc64_elf_create_dynamic_sections (dynobj, info)
@@ -1746,15 +1776,14 @@ ppc64_elf_create_dynamic_sections (dynob
|| (!info->shared && !htab->srelbss))
abort ();
- /* Our .plt just contains pointers, no code. */
- flags = bfd_get_section_flags (dynobj, htab->splt);
- flags &= ~SEC_CODE;
- if (! bfd_set_section_flags (dynobj, htab->splt, flags))
+ /* Create .stub and .glink for global linkage functions. */
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->sstub = bfd_make_section (dynobj, ".stub");
+ if (htab->sstub == NULL
+ || ! bfd_set_section_flags (dynobj, htab->sstub, flags)
+ || ! bfd_set_section_alignment (dynobj, htab->sstub, 2))
return false;
-
- /* Create .glink for global linkage functions. */
- flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
- | SEC_LINKER_CREATED);
htab->sglink = bfd_make_section (dynobj, ".glink");
if (htab->sglink == NULL
|| ! bfd_set_section_flags (dynobj, htab->sglink, flags)
@@ -1814,7 +1843,7 @@ ppc64_elf_copy_indirect_symbol (dir, ind
/* Look through the relocs for a section during the first phase, and
calculate needed space in the global offset table, procedure
- linkage table, and dynamic reloc sections. */
+ linkage table, and dynamic reloc sections. */
static boolean
ppc64_elf_check_relocs (abfd, info, sec, relocs)
@@ -1956,9 +1985,18 @@ ppc64_elf_check_relocs (abfd, info, sec,
return false;
break;
+ case R_PPC64_REL24:
+ if (h != NULL)
+ {
+ /* We may need a .plt entry if the function this reloc
+ refers to is in a shared lib. */
+ h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+ h->plt.refcount += 1;
+ }
+ break;
+
case R_PPC64_REL64:
case R_PPC64_REL32:
- case R_PPC64_REL24:
case R_PPC64_REL14:
case R_PPC64_REL14_BRTAKEN:
case R_PPC64_REL14_BRNTAKEN:
@@ -2022,6 +2060,12 @@ ppc64_elf_check_relocs (abfd, info, sec,
struct ppc_dyn_relocs *p;
struct ppc_dyn_relocs **head;
+#if 0
+ /* Don't propagate .opd relocs. */
+ if (strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0)
+ break;
+#endif
+
/* We must copy these reloc types into the output file.
Create a reloc section in dynobj and make room for
this reloc. */
@@ -2044,6 +2088,7 @@ ppc64_elf_check_relocs (abfd, info, sec,
(*_bfd_error_handler)
(_("%s: bad relocation section name `%s\'"),
bfd_archive_filename (abfd), name);
+ bfd_set_error (bfd_error_bad_value);
}
if (htab->elf.dynobj == NULL)
@@ -2227,10 +2272,18 @@ ppc64_elf_gc_sweep_hook (abfd, info, sec
}
break;
+ case R_PPC64_REL24:
+ if (r_symndx >= symtab_hdr->sh_info)
+ {
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ if (h->plt.refcount > 0)
+ h->plt.refcount--;
+ }
+ /* Fall thru. */
+
case R_PPC64_REL14:
case R_PPC64_REL14_BRNTAKEN:
case R_PPC64_REL14_BRTAKEN:
- case R_PPC64_REL24:
case R_PPC64_REL32:
case R_PPC64_REL64:
if (r_symndx >= symtab_hdr->sh_info)
@@ -2418,7 +2471,7 @@ ppc64_elf_adjust_dynamic_symbol (info, h
}
/* If we didn't find any dynamic relocs in read-only sections, then
- we'll be keeping the dynamic relocs and avoiding the copy reloc. */
+ we'll be keeping the dynamic relocs and avoiding the copy reloc. */
if (p == NULL)
{
h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF;
@@ -2548,21 +2601,22 @@ allocate_dynrelocs (h, inf)
s->_raw_size += PLT_INITIAL_ENTRY_SIZE;
h->plt.offset = s->_raw_size;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
/* Make room for this entry. */
s->_raw_size += PLT_ENTRY_SIZE;
- /* Point the function at the linkage stub. This works because
- the only references to the function code sym are calls.
- Function pointer comparisons use the function descriptor. */
- s = htab->sglink;
- h->root.type = bfd_link_hash_defined;
- h->root.u.def.section = s;
- h->root.u.def.value = s->_raw_size;
- h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+ /* Make room for the .stub and .glink code. */
+ s = htab->sstub;
+ s->_raw_size += PLT_CALL_STUB_SIZE;
- /* Make room for global linkage code in .glink. */
- s->_raw_size += PPC64_ELF_GLINK_SIZE;
+ s = htab->sglink;
+ if (s->_raw_size == 0)
+ s->_raw_size += GLINK_CALL_STUB_SIZE;
+ /* We need bigger stubs past index 32767. */
+ if (s->_raw_size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+ s->_raw_size += 4;
+ s->_raw_size += 2*4;
/* We also need to make an entry in the .rela.plt section. */
s = htab->srelplt;
@@ -2804,11 +2858,14 @@ ppc64_elf_size_dynamic_sections (output_
relocs = false;
for (s = dynobj->sections; s != NULL; s = s->next)
{
+ bfd_vma size;
+
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
if (s == htab->splt
|| s == htab->sgot
+ || s == htab->sstub
|| s == htab->sglink)
{
/* Strip this section if we don't need it; see the
@@ -2855,7 +2912,13 @@ ppc64_elf_size_dynamic_sections (output_
section's contents are written out. This should not happen,
but this way if it does, we get a R_PPC64_NONE reloc instead
of garbage. */
- s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
+ size = s->_raw_size;
+ if (s == htab->sstub)
+ {
+ /* .stub may grow. Allocate enough for the maximum growth. */
+ size += (size + 65536 + 65535) / 65536 * 4;
+ }
+ s->contents = (bfd_byte *) bfd_zalloc (dynobj, size);
if (s->contents == NULL)
return false;
}
@@ -2881,7 +2944,8 @@ ppc64_elf_size_dynamic_sections (output_
if (!add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
|| !add_dynamic_entry (DT_PLTREL, DT_RELA)
- || !add_dynamic_entry (DT_JMPREL, 0))
+ || !add_dynamic_entry (DT_JMPREL, 0)
+ || !add_dynamic_entry (DT_PPC64_GLINK, 0))
return false;
}
@@ -2912,28 +2976,27 @@ ppc64_elf_size_dynamic_sections (output_
final symbol resolution and section placement has been determined.
We use this hook to provide a value for TOCstart, which we store in
- the output bfd elf_gp, then we call the generic ELF final link
- routine. */
+ the output bfd elf_gp. */
-static boolean
-ppc64_elf_final_link (abfd, info)
- bfd *abfd;
+boolean
+ppc64_elf_set_toc (obfd, info)
+ bfd *obfd;
struct bfd_link_info *info;
{
- if (! info->relocateable)
+ if (!info->relocateable)
{
asection *s;
bfd_vma TOCstart;
- /* The TOC consists of sections .got, .toc, .tocbss, .plt in this
+ /* The TOC consists of sections .got, .toc, .tocbss, .plt in that
order. The TOC starts where the first of these sections starts. */
- s = bfd_get_section_by_name (abfd, ".got");
+ s = bfd_get_section_by_name (obfd, ".got");
if (s == NULL)
- s = bfd_get_section_by_name (abfd, ".toc");
+ s = bfd_get_section_by_name (obfd, ".toc");
if (s == NULL)
- s = bfd_get_section_by_name (abfd, ".tocbss");
+ s = bfd_get_section_by_name (obfd, ".tocbss");
if (s == NULL)
- s = bfd_get_section_by_name (abfd, ".plt");
+ s = bfd_get_section_by_name (obfd, ".plt");
if (s == NULL)
{
/* This may happen for
@@ -2946,21 +3009,21 @@ ppc64_elf_final_link (abfd, info)
/* Look for a likely section. We probably won't even be
using TOCstart. */
- for (s = abfd->sections; s != NULL; s = s->next)
+ for (s = obfd->sections; s != NULL; s = s->next)
if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY))
== (SEC_ALLOC | SEC_SMALL_DATA))
break;
if (s == NULL)
- for (s = abfd->sections; s != NULL; s = s->next)
+ for (s = obfd->sections; s != NULL; s = s->next)
if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA))
== (SEC_ALLOC | SEC_SMALL_DATA))
break;
if (s == NULL)
- for (s = abfd->sections; s != NULL; s = s->next)
+ for (s = obfd->sections; s != NULL; s = s->next)
if ((s->flags & (SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC)
break;
if (s == NULL)
- for (s = abfd->sections; s != NULL; s = s->next)
+ for (s = obfd->sections; s != NULL; s = s->next)
if ((s->flags & SEC_ALLOC) == SEC_ALLOC)
break;
}
@@ -2968,12 +3031,225 @@ ppc64_elf_final_link (abfd, info)
TOCstart = 0;
if (s != NULL)
TOCstart = s->output_section->vma + s->output_offset;
+
+ elf_gp (obfd) = TOCstart;
+ }
+ return true;
+}
+
+/* PowerPC64 .plt entries are 24 bytes long, which doesn't divide
+ evenly into 64k. Sometimes with a large enough .plt, we'll need to
+ use offsets differing in the high 16 bits when accessing a .plt
+ entry from a .plt call stub. This function adjusts the size of
+ .stub to accommodate the extra stub instruction needed in such
+ cases. */
+
+boolean
+ppc64_elf_size_stubs (obfd, info, changed)
+ bfd *obfd;
+ struct bfd_link_info *info;
+ int *changed;
+{
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ bfd_vma plt_offset, next_64k;
+ long base, num, extra;
+
+ /* .plt and .stub should be both present, or both absent. */
+ if ((htab->splt == NULL || htab->splt->_raw_size == 0)
+ != (htab->sstub == NULL || htab->sstub->_raw_size == 0))
+ abort ();
+
+ /* If no .plt, then nothing to do. */
+ if (htab->splt == NULL || htab->splt->_raw_size == 0)
+ return true;
+
+ plt_offset = (htab->splt->output_section->vma
+ + htab->splt->output_offset
+ - elf_gp (obfd));
+ next_64k = (plt_offset + 65535) & -65536;
+
+ /* If the .plt doesn't have any entries crossing a 64k boundary,
+ then there is no need for bigger stubs. */
+ if (next_64k <= plt_offset + htab->splt->_raw_size)
+ return true;
+
+ /* OK, so we have at least one transition. Since .plt entries are
+ 24 bytes long, we'll strike it lucky every 3*64k, with the 64k
+ boundary between .plt entries. */
+ base = next_64k / 65536;
+ num = (plt_offset + htab->splt->_raw_size - next_64k) / 65536;
+ extra = (base % 3 + num + 1) * 2 / 3;
+
+ /* Allow one extra instruction for each EXTRA. The change in .stub
+ may change the location of .toc and .plt. .toc and .plt ought to
+ move as a group, but someone might be playing with eg. .plt
+ alignment, so don't allow .stub size to decrease. */
+ if (htab->sstub->_cooked_size < htab->sstub->_raw_size + extra * 4)
+ {
+ htab->sstub->_cooked_size = htab->sstub->_raw_size + extra * 4;
+ *changed = true;
+ }
+ return true;
+}
+
+/* Build a .plt call stub. */
+
+static bfd_byte *
+build_plt_stub (obfd, p, offset, glink)
+ bfd *obfd;
+ bfd_byte *p;
+ int offset;
+ int glink;
+{
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
+ if (glink)
+ bfd_put_32 (obfd, LD_R2_40R1, p), p += 4;
+ bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
+ if (!glink)
+ bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
+ if (PPC_HA (offset + 8) != PPC_HA (offset))
+ bfd_put_32 (obfd, ADDIS_R12_R12_1, p), p += 4;
+ offset += 8;
+ bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset), p), p += 4;
+ if (PPC_HA (offset + 8) != PPC_HA (offset))
+ bfd_put_32 (obfd, ADDIS_R12_R12_1, p), p += 4;
+ offset += 8;
+ bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
+ bfd_put_32 (obfd, BCTR, p), p += 4;
+ return p;
+}
+
+/* Build the stubs for one function call. */
+
+static boolean
+build_one_stub (h, inf)
+ struct elf_link_hash_entry *h;
+ PTR inf;
+{
+ struct bfd_link_info *info;
+ struct ppc_link_hash_table *htab;
+
+ if (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ return true;
+
+ info = (struct bfd_link_info *) inf;
+ htab = ppc_hash_table (info);
- elf_gp (abfd) = TOCstart;
+ if (htab->elf.dynamic_sections_created
+ && h->plt.offset != (bfd_vma) -1)
+ {
+ asection *s;
+ bfd_vma plt_r2;
+ bfd_byte *p;
+ unsigned int indx;
+
+ /* Point the function at the linkage stub. This works because
+ the only references to the function code sym are calls.
+ Function pointer comparisons use the function descriptor. */
+ s = htab->sstub;
+ h->root.type = bfd_link_hash_defined;
+ h->root.u.def.section = s;
+ h->root.u.def.value = s->_cooked_size;
+
+ /* Build the .plt call stub. */
+ plt_r2 = (htab->splt->output_section->vma
+ + htab->splt->output_offset
+ + h->plt.offset
+ - elf_gp (htab->splt->output_section->owner)
+ - TOC_BASE_OFF);
+
+ if (plt_r2 + 0x80000000 > 0xffffffff)
+ {
+ (*_bfd_error_handler)
+ (_("linkage table overflow against `%s'"),
+ h->root.root.string);
+ bfd_set_error (bfd_error_bad_value);
+ htab->plt_overflow = true;
+ return false;
+ }
+ p = s->contents + s->_cooked_size;
+ p = build_plt_stub (s->owner, p, (int) plt_r2, 0);
+ s->_cooked_size = p - s->contents;
+
+ /* Build the .glink lazy link call stub. */
+ s = htab->sglink;
+ p = s->contents + s->_cooked_size;
+ indx = s->reloc_count;
+ if (indx < 0x8000)
+ {
+ bfd_put_32 (s->owner, LI_R0_0 | indx, p);
+ p += 4;
+ }
+ else
+ {
+ bfd_put_32 (s->owner, LIS_R0_0 | PPC_HI (indx), p);
+ p += 4;
+ bfd_put_32 (s->owner, ORI_R0_R0_0 | PPC_LO (indx), p);
+ p += 4;
+ }
+ bfd_put_32 (s->owner, B_DOT | ((s->contents - p) & 0x3fffffc), p);
+ p += 4;
+ s->_cooked_size = p - s->contents;
+ s->reloc_count += 1;
+ }
+ return true;
+}
+
+boolean
+ppc64_elf_build_stubs (obfd, info)
+ bfd *obfd;
+ struct bfd_link_info *info;
+{
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ bfd_vma old_stub_size;
+ bfd_vma plt_r2;
+ bfd_byte *p;
+
+ /* If no .plt stubs, then nothing to do. */
+ if (htab->sstub == NULL || htab->sstub->_raw_size == 0)
+ return true;
+
+ old_stub_size = htab->sstub->_cooked_size;
+ htab->sstub->_cooked_size = 0;
+
+ /* Build the .glink plt call stub. */
+ plt_r2 = (htab->splt->output_section->vma
+ + htab->splt->output_offset
+ - elf_gp (obfd)
+ - TOC_BASE_OFF);
+ p = htab->sglink->contents;
+ p = build_plt_stub (htab->sglink->owner, p, (int) plt_r2, 1);
+ while (p - htab->sglink->contents < GLINK_CALL_STUB_SIZE)
+ {
+ bfd_put_32 (htab->sglink->owner, NOP, p);
+ p += 4;
}
+ htab->sglink->_cooked_size = p - htab->sglink->contents;
+
+ /* Use reloc_count to count entries. */
+ htab->sglink->reloc_count = 0;
- /* Invoke the regular ELF backend linker to do all the work. */
- return bfd_elf64_bfd_final_link (abfd, info);
+ elf_link_hash_traverse (&htab->elf, build_one_stub, (PTR) info);
+ htab->sglink->reloc_count = 0;
+
+ if (htab->plt_overflow)
+ return false;
+
+ if (old_stub_size != htab->sstub->_cooked_size
+ || htab->sglink->_raw_size != htab->sglink->_cooked_size)
+ {
+ (*_bfd_error_handler)
+ (_("stub section size doesn't match calculated size"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ return true;
}
/* Set up any other section flags and such that may be necessary. */
@@ -3374,21 +3650,12 @@ ppc64_elf_relocate_section (output_bfd,
which is the address of the start of the TOC plus 0x8000.
The TOC consists of sections .got, .toc, .tocbss, and .plt,
in this order. */
-
case R_PPC64_TOC16:
case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_HI:
case R_PPC64_TOC16_DS:
case R_PPC64_TOC16_LO_DS:
case R_PPC64_TOC16_HA:
- /* Only .got, .toc and *UND* symbols are allowed. */
- BFD_ASSERT (sec != (asection *) 0
- && (bfd_is_und_section (sec)
- || strcmp (bfd_get_section_name (abfd, sec),
- ".toc") == 0
- || strcmp (bfd_get_section_name (abfd, sec),
- ".got") == 0));
-
addend -= TOCstart + TOC_BASE_OFF;
break;
@@ -3403,12 +3670,14 @@ ppc64_elf_relocate_section (output_bfd,
addend -= sec->output_section->vma;
break;
+ case R_PPC64_REL24:
+ break;
+
/* Relocations that may need to be propagated if this is a
dynamic object. */
case R_PPC64_REL14:
case R_PPC64_REL14_BRNTAKEN:
case R_PPC64_REL14_BRTAKEN:
- case R_PPC64_REL24:
case R_PPC64_REL32:
case R_PPC64_REL64:
case R_PPC64_ADDR14:
@@ -3431,12 +3700,15 @@ ppc64_elf_relocate_section (output_bfd,
case R_PPC64_UADDR16:
case R_PPC64_UADDR32:
case R_PPC64_UADDR64:
- case R_PPC64_TOC:
/* r_symndx will be zero only for relocs against symbols
from removed linkonce sections, or sections discarded by
a linker script. */
- if (r_symndx == 0
- || (input_section->flags & SEC_ALLOC) == 0)
+ if (r_symndx == 0)
+ break;
+ /* Fall thru. */
+
+ case R_PPC64_TOC:
+ if ((input_section->flags & SEC_ALLOC) == 0)
break;
if ((info->shared
@@ -3462,6 +3734,12 @@ ppc64_elf_relocate_section (output_bfd,
asection *sreloc;
Elf64_External_Rela *loc;
+#if 0
+ if (strcmp (bfd_get_section_name (output_bfd, input_section),
+ ".opd") == 0)
+ break;
+#endif
+
/* When generating a dynamic object, these relocations
are copied into the output file to be resolved at run
time. */
@@ -3715,8 +3993,7 @@ ppc64_elf_finish_dynamic_symbol (output_
/* This symbol has an entry in the procedure linkage table. Set
it up. */
- if (h->dynindx == -1
- || htab->splt == NULL
+ if (htab->splt == NULL
|| htab->srelplt == NULL
|| htab->sglink == NULL
|| h->root.root.string[0] != '.'
@@ -3724,7 +4001,7 @@ ppc64_elf_finish_dynamic_symbol (output_
abort ();
/* Find its corresponding function descriptor.
- ppc64_elf_adjust_dynamic_symbol has already set it up for us. */
+ ppc64_elf_adjust_dynamic_symbol has already set it up for us. */
funcdesc_h = elf_link_hash_lookup (elf_hash_table (info),
h->root.root.string + 1,
@@ -3745,42 +4022,6 @@ ppc64_elf_finish_dynamic_symbol (output_
loc = (Elf64_External_Rela *) htab->srelplt->contents;
loc += (h->plt.offset - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE;
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
-
- /* We need to create a linkage function. */
- if (h->root.u.def.section == htab->sglink)
- {
- bfd_byte *p;
- bfd_vma pltoff;
- const unsigned int *stub;
-
- /* Where to write it. */
- p = h->root.u.def.section->contents + h->root.u.def.value;
-
- /* The function descriptor is in the PLT. */
- pltoff = htab->splt->output_section->vma
- + htab->splt->output_section->output_offset
- + h->plt.offset
- - elf_gp (output_bfd) - TOC_BASE_OFF;
-
- if (pltoff + 0x8000 > 0xffff)
- {
- (*_bfd_error_handler)
- (_("linkage table overflow against `%s'"),
- h->root.root.string);
- }
-
- /* Write it out. */
- stub = ppc64_elf_glink_code;
- bfd_put_32 (output_bfd, *stub | (pltoff & 0xfffc), p);
- while (p += 4, ++stub < (ppc64_elf_glink_code
- + (sizeof (ppc64_elf_glink_code)
- / sizeof (*ppc64_elf_glink_code))));
- {
- bfd_put_32 (output_bfd, (bfd_vma) *stub, p);
- }
- }
- else
- abort ();
}
if (h->got.offset != (bfd_vma) -1)
@@ -3917,19 +4158,30 @@ ppc64_elf_finish_dynamic_sections (outpu
default:
continue;
+ case DT_PPC64_GLINK:
+ dyn.d_un.d_ptr = (htab->sglink->output_section->vma
+ + htab->sglink->output_offset);
+ break;
+
case DT_PLTGOT:
- dyn.d_un.d_ptr = htab->splt->output_section->vma;
+ dyn.d_un.d_ptr = (htab->splt->output_section->vma
+ + htab->splt->output_offset);
break;
case DT_JMPREL:
- dyn.d_un.d_ptr = htab->srelplt->output_section->vma;
+ dyn.d_un.d_ptr = (htab->srelplt->output_section->vma
+ + htab->srelplt->output_offset);
break;
case DT_PLTRELSZ:
- if (htab->srelplt->output_section->_cooked_size != 0)
- dyn.d_un.d_val = htab->srelplt->output_section->_cooked_size;
- else
- dyn.d_un.d_val = htab->srelplt->output_section->_raw_size;
+ dyn.d_un.d_val = htab->srelplt->_raw_size;
+ break;
+
+ case DT_RELASZ:
+ /* Don't count procedure linkage table relocs in the
+ overall reloc count. */
+ if (htab->srelplt != NULL)
+ dyn.d_un.d_val -= htab->srelplt->_raw_size;
break;
}
@@ -3937,6 +4189,25 @@ ppc64_elf_finish_dynamic_sections (outpu
}
}
+ if (htab->sgot != NULL && htab->sgot->_raw_size != 0)
+ {
+ /* Fill in the first entry in the global offset table.
+ We use it to hold the link-time TOCbase. */
+ bfd_put_64 (output_bfd,
+ elf_gp (output_bfd) - TOC_BASE_OFF,
+ htab->sgot->contents);
+
+ /* Set .got entry size. */
+ elf_section_data (htab->sgot->output_section)->this_hdr.sh_entsize = 8;
+ }
+
+ if (htab->splt != NULL && htab->splt->_raw_size != 0)
+ {
+ /* Set .plt entry size. */
+ elf_section_data (htab->splt->output_section)->this_hdr.sh_entsize
+ = PLT_ENTRY_SIZE;
+ }
+
return true;
}
@@ -3962,17 +4233,15 @@ ppc64_elf_finish_dynamic_sections (outpu
#define elf_backend_plt_alignment 3
#define elf_backend_plt_not_loaded 1
#define elf_backend_got_symbol_offset 0
-#define elf_backend_got_header_size 0
+#define elf_backend_got_header_size 8
+#define elf_backend_plt_header_size PLT_INITIAL_ENTRY_SIZE
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
-#define elf_backend_plt_header_size PLT_INITIAL_ENTRY_SIZE
-
#define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup
#define bfd_elf64_bfd_set_private_flags ppc64_elf_set_private_flags
#define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data
#define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create
-#define bfd_elf64_bfd_final_link ppc64_elf_final_link
#define elf_backend_section_from_shdr ppc64_elf_section_from_shdr
#define elf_backend_create_dynamic_sections ppc64_elf_create_dynamic_sections
Index: bfd/elf64-ppc.h
===================================================================
RCS file: elf64-ppc.h
diff -N elf64-ppc.h
--- /dev/null Tue May 5 13:32:27 1998
+++ elf64-ppc.h Tue Jan 15 20:57:07 2002
@@ -0,0 +1,22 @@
+/* PowerPC64-specific support for 64-bit ELF.
+ Copyright 2002 Free Software Foundation, Inc.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+boolean ppc64_elf_set_toc PARAMS ((bfd *, struct bfd_link_info *));
+boolean ppc64_elf_size_stubs PARAMS ((bfd *, struct bfd_link_info *, int *));
+boolean ppc64_elf_build_stubs PARAMS ((bfd *, struct bfd_link_info *));
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.22
diff -u -p -r1.22 elflink.c
--- elflink.c 2001/11/07 16:50:36 1.22
+++ elflink.c 2002/01/16 04:57:07
@@ -137,7 +137,7 @@ _bfd_elf_create_dynamic_sections (abfd,
pltflags = flags;
pltflags |= SEC_CODE;
if (bed->plt_not_loaded)
- pltflags &= ~ (SEC_LOAD | SEC_HAS_CONTENTS);
+ pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
if (bed->plt_readonly)
pltflags |= SEC_READONLY;
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.145
diff -u -p -r1.145 readelf.c
--- readelf.c 2002/01/09 18:59:11 1.145
+++ readelf.c 2002/01/16 04:57:31
@@ -159,6 +159,7 @@ static bfd_vma byte_get_littl
static bfd_vma byte_get_big_endian PARAMS ((unsigned char *, int));
static const char * get_mips_dynamic_type PARAMS ((unsigned long));
static const char * get_sparc64_dynamic_type PARAMS ((unsigned long));
+static const char * get_ppc64_dynamic_type PARAMS ((unsigned long));
static const char * get_parisc_dynamic_type PARAMS ((unsigned long));
static const char * get_dynamic_type PARAMS ((unsigned long));
static int slurp_rela_relocs PARAMS ((FILE *, unsigned long, unsigned long, Elf_Internal_Rela **, unsigned long *));
@@ -1177,6 +1178,18 @@ get_sparc64_dynamic_type (type)
}
static const char *
+get_ppc64_dynamic_type (type)
+ unsigned long type;
+{
+ switch (type)
+ {
+ case DT_PPC64_GLINK: return "PPC64_GLINK";
+ default:
+ return NULL;
+ }
+}
+
+static const char *
get_parisc_dynamic_type (type)
unsigned long type;
{
@@ -1287,6 +1300,9 @@ get_dynamic_type (type)
break;
case EM_SPARCV9:
result = get_sparc64_dynamic_type (type);
+ break;
+ case EM_PPC64:
+ result = get_ppc64_dynamic_type (type);
break;
default:
result = NULL;
Index: include/elf/ppc.h
===================================================================
RCS file: /cvs/src/src/include/elf/ppc.h,v
retrieving revision 1.6
diff -u -p -r1.6 ppc.h
--- ppc.h 2001/08/27 10:26:34 1.6
+++ ppc.h 2002/01/16 04:57:54
@@ -171,6 +171,9 @@ END_RELOC_NUMBERS (R_PPC_max)
#define R_PPC64_GNU_VTINHERIT R_PPC_GNU_VTINHERIT
#define R_PPC64_GNU_VTENTRY R_PPC_GNU_VTENTRY
+/* Specify the start of the .glink section. */
+#define DT_PPC64_GLINK DT_LOPROC
+
/* Processor specific flags for the ELF header e_flags field. */
#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag. */
Index: ld/Makefile.am
===================================================================
RCS file: /cvs/src/src/ld/Makefile.am,v
retrieving revision 1.82
diff -u -p -r1.82 Makefile.am
--- Makefile.am 2002/01/08 04:23:01 1.82
+++ Makefile.am 2002/01/16 04:57:58
@@ -533,11 +533,11 @@ eelf32ppclinux.c: $(srcdir)/emulparams/e
$(srcdir)/emulparams/elf32ppc.sh \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32ppclinux "$(tdir_elf32ppclinux)"
-eelf64ppc.c: $(srcdir)/emulparams/elf64ppc.sh \
+eelf64ppc.c: $(srcdir)/emulparams/elf64ppc.sh $(srcdir)/emultempl/ppc64elf.em \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf64ppc "$(tdir_elf64ppc)"
eelf64lppc.c: $(srcdir)/emulparams/elf64lppc.sh \
- $(srcdir)/emulparams/elf64ppc.sh \
+ $(srcdir)/emulparams/elf64ppc.sh $(srcdir)/emultempl/ppc64elf.em \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf64lppc "$(tdir_elf64lppc)"
eelf32i370.c: $(srcdir)/emulparams/elf32i370.sh \
Index: ld/emulparams/elf64ppc.sh
===================================================================
RCS file: /cvs/src/src/ld/emulparams/elf64ppc.sh,v
retrieving revision 1.1
diff -u -p -r1.1 elf64ppc.sh
--- elf64ppc.sh 2001/08/27 10:45:55 1.1
+++ elf64ppc.sh 2002/01/16 04:58:03
@@ -1,4 +1,5 @@
TEMPLATE_NAME=elf32
+EXTRA_EM_FILE=ppc64elf
ELFSIZE=64
GENERATE_SHLIB_SCRIPT=yes
SCRIPT_NAME=elf
Index: ld/emultempl/ppc64elf.em
===================================================================
RCS file: ppc64elf.em
diff -N ppc64elf.em
--- /dev/null Tue May 5 13:32:27 1998
+++ ppc64elf.em Tue Jan 15 20:58:03 2002
@@ -0,0 +1,99 @@
+# This shell script emits a C file. -*- C -*-
+# Copyright 2002 Free Software Foundation, Inc.
+#
+# This file is part of GLD, the Gnu Linker.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# This file is sourced from elf32.em, and defines extra powerpc64-elf
+# specific routines.
+#
+cat >>e${EMULATION_NAME}.c <<EOF
+
+#include "elf64-ppc.h"
+
+static int need_laying_out = 0;
+
+static void gld${EMULATION_NAME}_after_allocation PARAMS ((void));
+static void gld${EMULATION_NAME}_finish PARAMS ((void));
+
+/* Call the back-end function to set TOC base after we have placed all
+ the sections. */
+static void
+gld${EMULATION_NAME}_after_allocation ()
+{
+ if (!ppc64_elf_set_toc (output_bfd, &link_info))
+ einfo ("%X%P: can not set TOC base: %E\n");
+}
+
+/* Final emulation specific call. PowerPC64 has 24 byte .plt entries,
+ and needs different call stubs for any entries that cross a 64k
+ boundary relative to the TOC. That means we need to wait until all
+ sections have been laid out to initialise the stubs. */
+
+static void
+gld${EMULATION_NAME}_finish ()
+{
+ /* If generating a relocatable output file, then we don't have any
+ stubs. */
+ if (link_info.relocateable)
+ return;
+
+ /* bfd_elf64_discard_info just plays with debugging sections,
+ ie. doesn't affect any code, so we can delay resizing the
+ sections. It's likely we'll resize everything in the process of
+ adjusting stub sizes. */
+ if (bfd_elf${ELFSIZE}_discard_info (output_bfd, &link_info))
+ need_laying_out = 1;
+
+ while (1)
+ {
+ /* Call into the BFD backend to do the real work. */
+ if (! ppc64_elf_size_stubs (output_bfd, &link_info, &need_laying_out))
+ {
+ einfo ("%X%P: can not size stub section: %E\n");
+ return;
+ }
+
+ if (!need_laying_out)
+ break;
+
+ /* If we have changed the size of the stub section, then we need
+ to recalculate all the section offsets. After this, we may
+ need to adjust the stub size again. */
+ need_laying_out = 0;
+
+ /* Resize the sections. */
+ lang_size_sections (stat_ptr->head, abs_output_section,
+ &stat_ptr->head, 0, (bfd_vma) 0, NULL);
+
+ /* Recalculate TOC base. */
+ ldemul_after_allocation ();
+
+ /* Do the assignments again. */
+ lang_do_assignments (stat_ptr->head, abs_output_section,
+ (fill_type) 0, (bfd_vma) 0);
+ }
+
+ if (! ppc64_elf_build_stubs (output_bfd, &link_info))
+ einfo ("%X%P: can not build stubs: %E\n");
+}
+EOF
+
+# Put these extra ppc64elf routines in ld_${EMULATION_NAME}_emulation
+#
+LDEMUL_AFTER_ALLOCATION=gld${EMULATION_NAME}_after_allocation
+LDEMUL_FINISH=gld${EMULATION_NAME}_finish