diff options
author | Alan Modra <amodra@gmail.com> | 2002-01-16 05:50:03 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2002-01-16 05:50:03 +0000 |
commit | 5d1634d792f2d90ada5c17faf98672c58717fc79 (patch) | |
tree | 378657bea3bcae56accb2bda0ac6979693926c98 /bfd | |
parent | b84da006ed75bf3318d1562f08cf63f804ebff46 (diff) | |
download | gdb-5d1634d792f2d90ada5c17faf98672c58717fc79.zip gdb-5d1634d792f2d90ada5c17faf98672c58717fc79.tar.gz gdb-5d1634d792f2d90ada5c17faf98672c58717fc79.tar.bz2 |
* 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.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 40 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 513 | ||||
-rw-r--r-- | bfd/elf64-ppc.h | 22 | ||||
-rw-r--r-- | bfd/elflink.c | 2 |
4 files changed, 454 insertions, 123 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 0da0df2..87de4f2 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,45 @@ 2002-01-16 Alan Modra <amodra@bigpond.net.au> + * 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. + +2002-01-16 Alan Modra <amodra@bigpond.net.au> + * elf32-arm.h (elf32_arm_size_dynamic_sections): When removing sections, use bfd_section_list_remove. * elf32-i370.c (i370_elf_size_dynamic_sections): Likewise. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 3726f94..7f2f0c4 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -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 - Suite 330, Boston, MA 02111-1307, USA. */ #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_elf_reloc_type_class 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_sections #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 */ -}; +/* .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 */ -#define PPC64_ELF_GLINK_SIZE \ - ((sizeof ppc64_elf_glink_code / sizeof ppc64_elf_glink_code[0]) * 4) +/* 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 (dynobj, info) || (!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, relocs) 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, relocs) 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, relocs) (*_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, relocs) } 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_bfd, info) 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_bfd, info) 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_bfd, info) 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_bfd, info) 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; } @@ -2969,11 +3032,224 @@ ppc64_elf_final_link (abfd, info) if (s != NULL) TOCstart = s->output_section->vma + s->output_offset; - elf_gp (abfd) = TOCstart; + 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); + + 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; +} - /* Invoke the regular ELF backend linker to do all the work. */ - return bfd_elf64_bfd_final_link (abfd, info); +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; + + 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, info, input_bfd, input_section, 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, info, input_bfd, input_section, 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, info, input_bfd, input_section, 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, info, input_bfd, input_section, 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_bfd, info, h, sym) /* 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_bfd, info, h, sym) 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_bfd, info, h, sym) 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 (output_bfd, info) 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 (output_bfd, info) } } + 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 (output_bfd, info) #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 diff --git a/bfd/elf64-ppc.h b/bfd/elf64-ppc.h new file mode 100644 index 0000000..b261a58 --- /dev/null +++ b/bfd/elf64-ppc.h @@ -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 *)); diff --git a/bfd/elflink.c b/bfd/elflink.c index fdc82b7..2032efa 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -137,7 +137,7 @@ _bfd_elf_create_dynamic_sections (abfd, info) 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; |