/* Alpha specific support for 64-bit ELF
- Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
- Free Software Foundation, Inc.
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@tamu.edu>.
This file is part of BFD, the Binary File Descriptor library.
#define ECOFF_64
#include "ecoffswap.h"
+\f
+/* Instruction data for plt generation and relaxation. */
+
+#define OP_LDA 0x08
+#define OP_LDAH 0x09
+#define OP_LDQ 0x29
+#define OP_BR 0x30
+#define OP_BSR 0x34
+
+#define INSN_LDA (OP_LDA << 26)
+#define INSN_LDAH (OP_LDAH << 26)
+#define INSN_LDQ (OP_LDQ << 26)
+#define INSN_BR (OP_BR << 26)
+
+#define INSN_ADDQ 0x40000400
+#define INSN_RDUNIQ 0x0000009e
+#define INSN_SUBQ 0x40000520
+#define INSN_S4SUBQ 0x40000560
+#define INSN_UNOP 0x2ffe0000
+
+#define INSN_JSR 0x68004000
+#define INSN_JMP 0x68000000
+#define INSN_JSR_MASK 0xfc00c000
+
+#define INSN_A(I,A) (I | (A << 21))
+#define INSN_AB(I,A,B) (I | (A << 21) | (B << 16))
+#define INSN_ABC(I,A,B,C) (I | (A << 21) | (B << 16) | C)
+#define INSN_ABO(I,A,B,O) (I | (A << 21) | (B << 16) | ((O) & 0xffff))
+#define INSN_AD(I,A,D) (I | (A << 21) | (((D) >> 2) & 0x1fffff))
+
+/* PLT/GOT Stuff */
+
+/* Set by ld emulation. Putting this into the link_info or hash structure
+ is simply working too hard. */
+#ifdef USE_SECUREPLT
+bfd_boolean elf64_alpha_use_secureplt = TRUE;
+#else
+bfd_boolean elf64_alpha_use_secureplt = FALSE;
+#endif
+
+#define OLD_PLT_HEADER_SIZE 32
+#define OLD_PLT_ENTRY_SIZE 12
+#define NEW_PLT_HEADER_SIZE 36
+#define NEW_PLT_ENTRY_SIZE 4
+
+#define PLT_HEADER_SIZE \
+ (elf64_alpha_use_secureplt ? NEW_PLT_HEADER_SIZE : OLD_PLT_HEADER_SIZE)
+#define PLT_ENTRY_SIZE \
+ (elf64_alpha_use_secureplt ? NEW_PLT_ENTRY_SIZE : OLD_PLT_ENTRY_SIZE)
+
+#define MAX_GOT_SIZE (64*1024)
+
+#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so"
\f
struct alpha_elf_link_hash_entry
{
int flags;
/* Contexts in which a literal was referenced. */
-#define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01
-#define ALPHA_ELF_LINK_HASH_LU_MEM 0x02
-#define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04
-#define ALPHA_ELF_LINK_HASH_LU_JSR 0x08
-#define ALPHA_ELF_LINK_HASH_LU_TLSGD 0x10
-#define ALPHA_ELF_LINK_HASH_LU_TLSLDM 0x20
-#define ALPHA_ELF_LINK_HASH_LU_FUNC 0x38
-#define ALPHA_ELF_LINK_HASH_TLS_IE 0x40
-#define ALPHA_ELF_LINK_HASH_PLT_LOC 0x80
+#define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01
+#define ALPHA_ELF_LINK_HASH_LU_MEM 0x02
+#define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04
+#define ALPHA_ELF_LINK_HASH_LU_JSR 0x08
+#define ALPHA_ELF_LINK_HASH_LU_TLSGD 0x10
+#define ALPHA_ELF_LINK_HASH_LU_TLSLDM 0x20
+#define ALPHA_ELF_LINK_HASH_LU_JSRDIRECT 0x40
+#define ALPHA_ELF_LINK_HASH_LU_PLT 0x38
+#define ALPHA_ELF_LINK_HASH_TLS_IE 0x80
/* Used to implement multiple .got subsections. */
struct alpha_elf_got_entry
/* The .got offset for this entry. */
int got_offset;
+ /* The .plt offset for this entry. */
+ int plt_offset;
+
/* How many references to this entry? */
int use_count;
if (ret == (struct alpha_elf_link_hash_table *) NULL)
return NULL;
- if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
- elf64_alpha_link_hash_newfunc))
+ if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
+ elf64_alpha_link_hash_newfunc,
+ sizeof (struct alpha_elf_link_hash_entry)))
{
free (ret);
return NULL;
- align_power ((bfd_vma) 16, \
elf_hash_table (info)->tls_sec->alignment_power))
\f
-/* PLT/GOT Stuff */
-#define PLT_HEADER_SIZE 32
-#define PLT_HEADER_WORD1 (bfd_vma) 0xc3600000 /* br $27,.+4 */
-#define PLT_HEADER_WORD2 (bfd_vma) 0xa77b000c /* ldq $27,12($27) */
-#define PLT_HEADER_WORD3 (bfd_vma) 0x47ff041f /* nop */
-#define PLT_HEADER_WORD4 (bfd_vma) 0x6b7b0000 /* jmp $27,($27) */
-
-#define PLT_ENTRY_SIZE 12
-#define PLT_ENTRY_WORD1 0xc3800000 /* br $28, plt0 */
-#define PLT_ENTRY_WORD2 0
-#define PLT_ENTRY_WORD3 0
-
-#define MAX_GOT_SIZE (64*1024)
-
-#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so"
-\f
/* Handle an Alpha specific section when reading an object file. This
is called when bfd_section_from_shdr finds a section with an unknown
type.
elf64_alpha_create_got_section (bfd *abfd,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
+ flagword flags;
asection *s;
- if ((s = bfd_get_section_by_name (abfd, ".got")))
- {
- /* Check for a non-linker created .got? */
- if (alpha_elf_tdata (abfd)->got == NULL)
- alpha_elf_tdata (abfd)->got = s;
- return TRUE;
- }
-
- s = bfd_make_section_with_flags (abfd, ".got", (SEC_ALLOC | SEC_LOAD
- | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY
- | SEC_LINKER_CREATED));
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
if (s == NULL
|| !bfd_set_section_alignment (abfd, s, 3))
return FALSE;
alpha_elf_tdata (abfd)->got = s;
+ /* Make sure the object's gotobj is set to itself so that we default
+ to every object with its own .got. We'll merge .gots later once
+ we've collected each object's info. */
+ alpha_elf_tdata (abfd)->gotobj = abfd;
+
return TRUE;
}
elf64_alpha_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
{
asection *s;
+ flagword flags;
struct elf_link_hash_entry *h;
- struct bfd_link_hash_entry *bh;
/* We need to create .plt, .rela.plt, .got, and .rela.got sections. */
- s = bfd_make_section_with_flags (abfd, ".plt",
- (SEC_ALLOC | SEC_LOAD
- | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY
- | SEC_LINKER_CREATED
- | SEC_CODE));
- if (s == NULL
- || ! bfd_set_section_alignment (abfd, s, 3))
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | (elf64_alpha_use_secureplt ? SEC_READONLY : 0));
+ s = bfd_make_section_anyway_with_flags (abfd, ".plt", flags);
+ if (s == NULL || ! bfd_set_section_alignment (abfd, s, 4))
return FALSE;
/* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the
.plt section. */
- bh = NULL;
- if (! (_bfd_generic_link_add_one_symbol
- (info, abfd, "_PROCEDURE_LINKAGE_TABLE_", BSF_GLOBAL, s,
- (bfd_vma) 0, (const char *) NULL, FALSE,
- get_elf_backend_data (abfd)->collect, &bh)))
+ h = _bfd_elf_define_linkage_sym (abfd, info, s,
+ "_PROCEDURE_LINKAGE_TABLE_");
+ elf_hash_table (info)->hplt = h;
+ if (h == NULL)
return FALSE;
- h = (struct elf_link_hash_entry *) bh;
- h->def_regular = 1;
- h->type = STT_OBJECT;
- if (info->shared
- && ! bfd_elf_link_record_dynamic_symbol (info, h))
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED | SEC_READONLY);
+ s = bfd_make_section_anyway_with_flags (abfd, ".rela.plt", flags);
+ if (s == NULL || ! bfd_set_section_alignment (abfd, s, 3))
return FALSE;
- s = bfd_make_section_with_flags (abfd, ".rela.plt",
- (SEC_ALLOC | SEC_LOAD
- | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY
- | SEC_LINKER_CREATED
- | SEC_READONLY));
- if (s == NULL
- || ! bfd_set_section_alignment (abfd, s, 3))
- return FALSE;
+ if (elf64_alpha_use_secureplt)
+ {
+ flags = SEC_ALLOC | SEC_LINKER_CREATED;
+ s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags);
+ if (s == NULL || ! bfd_set_section_alignment (abfd, s, 3))
+ return FALSE;
+ }
/* We may or may not have created a .got section for this object, but
we definitely havn't done the rest of the work. */
- if (!elf64_alpha_create_got_section (abfd, info))
- return FALSE;
+ if (alpha_elf_tdata(abfd)->gotobj == NULL)
+ {
+ if (!elf64_alpha_create_got_section (abfd, info))
+ return FALSE;
+ }
- s = bfd_make_section_with_flags (abfd, ".rela.got",
- (SEC_ALLOC | SEC_LOAD
- | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY
- | SEC_LINKER_CREATED
- | SEC_READONLY));
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED | SEC_READONLY);
+ s = bfd_make_section_anyway_with_flags (abfd, ".rela.got", flags);
if (s == NULL
|| !bfd_set_section_alignment (abfd, s, 3))
return FALSE;
dynobj's .got section. We don't do this in the linker script
because we don't want to define the symbol if we are not creating
a global offset table. */
- bh = NULL;
- if (!(_bfd_generic_link_add_one_symbol
- (info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL,
- alpha_elf_tdata(abfd)->got, (bfd_vma) 0, (const char *) NULL,
- FALSE, get_elf_backend_data (abfd)->collect, &bh)))
- return FALSE;
- h = (struct elf_link_hash_entry *) bh;
- h->def_regular = 1;
- h->type = STT_OBJECT;
-
- if (info->shared
- && ! bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
-
+ h = _bfd_elf_define_linkage_sym (abfd, info, alpha_elf_tdata(abfd)->got,
+ "_GLOBAL_OFFSET_TABLE_");
elf_hash_table (info)->hgot = h;
+ if (h == NULL)
+ return FALSE;
return TRUE;
}
else
h->esym.asym.value = 0;
}
- else if (h->root.needs_plt)
- {
- /* Set type and value for a symbol with a function stub. */
- h->esym.asym.st = stProc;
- sec = bfd_get_section_by_name (einfo->abfd, ".plt");
- if (sec == NULL)
- h->esym.asym.value = 0;
- else
- {
- output_section = sec->output_section;
- if (output_section != NULL)
- h->esym.asym.value = (h->root.plt.offset
- + sec->output_offset
- + output_section->vma);
- else
- h->esym.asym.value = 0;
- }
- }
if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
h->root.root.root.string,
gotent->gotobj = abfd;
gotent->addend = r_addend;
gotent->got_offset = -1;
+ gotent->plt_offset = -1;
gotent->use_count = 1;
gotent->reloc_type = r_type;
gotent->reloc_done = 0;
return gotent;
}
+static bfd_boolean
+elf64_alpha_want_plt (struct alpha_elf_link_hash_entry *ah)
+{
+ return ((ah->root.type == STT_FUNC
+ || ah->root.root.type == bfd_link_hash_undefweak
+ || ah->root.root.type == bfd_link_hash_undefined)
+ && (ah->flags & ALPHA_ELF_LINK_HASH_LU_PLT) != 0
+ && (ah->flags & ~ALPHA_ELF_LINK_HASH_LU_PLT) == 0);
+}
+
/* Handle dynamic relocations when doing an Alpha ELF link. */
static bfd_boolean
Elf_Internal_Shdr *symtab_hdr;
struct alpha_elf_link_hash_entry **sym_hashes;
const Elf_Internal_Rela *rel, *relend;
- bfd_boolean got_created;
bfd_size_type amt;
if (info->relocatable)
rel_sec_name = NULL;
symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
sym_hashes = alpha_elf_sym_hashes(abfd);
- got_created = FALSE;
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; ++rel)
This will be important when it comes to decide if we can
create a .plt entry for a function symbol. */
while (++rel < relend && ELF64_R_TYPE (rel->r_info) == R_ALPHA_LITUSE)
- if (rel->r_addend >= 1 && rel->r_addend <= 5)
+ if (rel->r_addend >= 1 && rel->r_addend <= 6)
gotent_flags |= 1 << rel->r_addend;
--rel;
if (need & NEED_GOT)
{
- if (!got_created)
+ if (alpha_elf_tdata(abfd)->gotobj == NULL)
{
if (!elf64_alpha_create_got_section (abfd, info))
return FALSE;
-
- /* Make sure the object's gotobj is set to itself so
- that we default to every object with its own .got.
- We'll merge .gots later once we've collected each
- object's info. */
- alpha_elf_tdata(abfd)->gotobj = abfd;
-
- got_created = 1;
}
}
h->flags = gotent_flags;
/* Make a guess as to whether a .plt entry is needed. */
- if ((gotent_flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
- && !(gotent_flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC))
- h->root.needs_plt = 1;
- else
- h->root.needs_plt = 0;
- }
+ /* ??? It appears that we won't make it into
+ adjust_dynamic_symbol for symbols that remain
+ totally undefined. Copying this check here means
+ we can create a plt entry for them too. */
+ h->root.needs_plt
+ = (maybe_dynamic && elf64_alpha_want_plt (h));
+ }
}
}
ah = (struct alpha_elf_link_hash_entry *)h;
/* Now that we've seen all of the input symbols, finalize our decision
- about whether this symbol should get a .plt entry. */
-
- if (alpha_elf_dynamic_symbol_p (h, info)
- && ((h->type == STT_FUNC
- && !(ah->flags & ALPHA_ELF_LINK_HASH_LU_ADDR))
- || (h->type == STT_NOTYPE
- && (ah->flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
- && !(ah->flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC)))
- /* Don't prevent otherwise valid programs from linking by attempting
- to create a new .got entry somewhere. A Correct Solution would be
- to add a new .got section to a new object file and let it be merged
- somewhere later. But for now don't bother. */
- && ah->got_entries)
+ about whether this symbol should get a .plt entry. Irritatingly, it
+ is common for folk to leave undefined symbols in shared libraries,
+ and they still expect lazy binding; accept undefined symbols in lieu
+ of STT_FUNC. */
+ if (alpha_elf_dynamic_symbol_p (h, info) && elf64_alpha_want_plt (ah))
{
- h->needs_plt = 1;
+ h->needs_plt = TRUE;
s = bfd_get_section_by_name(dynobj, ".plt");
if (!s && !elf64_alpha_create_dynamic_sections (dynobj, info))
return FALSE;
- /* The first bit of the .plt is reserved. */
- if (s->size == 0)
- s->size = PLT_HEADER_SIZE;
-
- h->plt.offset = s->size;
- s->size += PLT_ENTRY_SIZE;
-
- /* We also need a JMP_SLOT entry in the .rela.plt section. */
- s = bfd_get_section_by_name (dynobj, ".rela.plt");
- BFD_ASSERT (s != NULL);
- s->size += sizeof (Elf64_External_Rela);
+ /* We need one plt entry per got subsection. Delay allocation of
+ the actual plt entries until size_plt_section, called from
+ size_dynamic_sections or during relaxation. */
return TRUE;
}
else
- h->needs_plt = 0;
+ h->needs_plt = FALSE;
/* If this is a weak symbol, and there is a real definition, the
processor independent code will have arranged for us to see the
elf64_alpha_calc_got_offsets_for_symbol (struct alpha_elf_link_hash_entry *h,
PTR arg ATTRIBUTE_UNUSED)
{
- bfd_boolean result = TRUE;
struct alpha_elf_got_entry *gotent;
if (h->root.root.type == bfd_link_hash_warning)
bfd_size_type *plge;
td = alpha_elf_tdata (gotent->gotobj);
- if (td == NULL)
- {
- _bfd_error_handler (_("Symbol %s has no GOT subsection for offset 0x%x"),
- h->root.root.root.string, gotent->got_offset);
- result = FALSE;
- continue;
- }
plge = &td->got->size;
gotent->got_offset = *plge;
*plge += alpha_got_entry_size (gotent->reloc_type);
}
- return result;
+ return TRUE;
}
static void
{
asection *splt = (asection *) data;
struct alpha_elf_got_entry *gotent;
+ bfd_boolean saw_one = FALSE;
/* If we didn't need an entry before, we still don't. */
if (!h->root.needs_plt)
return TRUE;
- /* There must still be a LITERAL got entry for the function. */
+ /* For each LITERAL got entry still in use, allocate a plt entry. */
for (gotent = h->got_entries; gotent ; gotent = gotent->next)
if (gotent->reloc_type == R_ALPHA_LITERAL
&& gotent->use_count > 0)
- break;
+ {
+ if (splt->size == 0)
+ splt->size = PLT_HEADER_SIZE;
+ gotent->plt_offset = splt->size;
+ splt->size += PLT_ENTRY_SIZE;
+ saw_one = TRUE;
+ }
- /* If there is, reset the PLT offset. If not, there's no longer
- a need for the PLT entry. */
- if (gotent)
- {
- if (splt->size == 0)
- splt->size = PLT_HEADER_SIZE;
- h->root.plt.offset = splt->size;
- splt->size += PLT_ENTRY_SIZE;
- }
- else
- {
- h->root.needs_plt = 0;
- h->root.plt.offset = -1;
- }
+ /* If there weren't any, there's no longer a need for the PLT entry. */
+ if (!saw_one)
+ h->root.needs_plt = FALSE;
return TRUE;
}
static bfd_boolean
elf64_alpha_size_plt_section (struct bfd_link_info *info)
{
- asection *splt, *spltrel;
+ asection *splt, *spltrel, *sgotplt;
unsigned long entries;
bfd *dynobj;
dynobj = elf_hash_table(info)->dynobj;
- splt = bfd_get_section_by_name(dynobj, ".plt");
+ splt = bfd_get_section_by_name (dynobj, ".plt");
if (splt == NULL)
return TRUE;
/* Every plt entry requires a JMP_SLOT relocation. */
spltrel = bfd_get_section_by_name (dynobj, ".rela.plt");
if (splt->size)
- entries = (splt->size - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
+ {
+ if (elf64_alpha_use_secureplt)
+ entries = (splt->size - NEW_PLT_HEADER_SIZE) / NEW_PLT_ENTRY_SIZE;
+ else
+ entries = (splt->size - OLD_PLT_HEADER_SIZE) / OLD_PLT_ENTRY_SIZE;
+ }
else
entries = 0;
spltrel->size = entries * sizeof (Elf64_External_Rela);
+ /* When using the secureplt, we need two words somewhere in the data
+ segment for the dynamic linker to tell us where to go. This is the
+ entire contents of the .got.plt section. */
+ if (elf64_alpha_use_secureplt)
+ {
+ sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
+ sgotplt->size = entries ? 16 : 0;
+ }
+
return TRUE;
}
if (h->root.root.type == bfd_link_hash_warning)
h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
+ /* If we're using a plt for this symbol, then all of its relocations
+ for its got entries go into .rela.plt. */
+ if (h->root.needs_plt)
+ return TRUE;
+
/* If the symbol is dynamic, we'll need all the relocations in their
natural form. If this is a shared object, and it has been forced
local, we'll need the same number of RELATIVE relocations. */
entries += alpha_dynamic_entries_for_reloc (gotent->reloc_type,
dynamic, info->shared);
- /* If we are using a .plt entry, subtract one, as the first
- reference uses a .rela.plt entry instead. */
- if (h->root.plt.offset != MINUS_ONE)
- entries--;
-
if (entries > 0)
{
bfd *dynobj = elf_hash_table(info)->dynobj;
elf64_alpha_calc_dynrel_sizes, info);
elf64_alpha_size_rela_got_section (info);
+ elf64_alpha_size_plt_section (info);
}
/* else we're not dynamic and by definition we don't need such things. */
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
- bfd_boolean strip;
if (!(s->flags & SEC_LINKER_CREATED))
continue;
of the dynobj section names depend upon the input files. */
name = bfd_get_section_name (dynobj, s);
- /* If we don't need this section, strip it from the output file.
- This is to handle .rela.bss and .rela.plt. We must create it
- in create_dynamic_sections, because it must be created before
- the linker maps input sections to output sections. The
- linker does that before adjust_dynamic_symbol is called, and
- it is that function which decides whether anything needs to
- go into these sections. */
-
- strip = FALSE;
-
if (strncmp (name, ".rela", 5) == 0)
{
- strip = (s->size == 0);
-
- if (!strip)
+ if (s->size != 0)
{
- if (strcmp(name, ".rela.plt") == 0)
+ if (strcmp (name, ".rela.plt") == 0)
relplt = TRUE;
/* We use the reloc_count field as a counter if we need
s->reloc_count = 0;
}
}
- else if (strcmp (name, ".plt") != 0)
+ else if (strncmp (name, ".got", 4) != 0
+ && strcmp (name, ".plt") != 0
+ && strcmp (name, ".dynbss") != 0)
{
/* It's not one of our dynamic sections, so don't allocate space. */
continue;
}
- if (strip)
- s->flags |= SEC_EXCLUDE;
- else
+ if (s->size == 0)
+ {
+ /* If we don't need this section, strip it from the output file.
+ This is to handle .rela.bss and .rela.plt. We must create it
+ in create_dynamic_sections, because it must be created before
+ the linker maps input sections to output sections. The
+ linker does that before adjust_dynamic_symbol is called, and
+ it is that function which decides whether anything needs to
+ go into these sections. */
+ s->flags |= SEC_EXCLUDE;
+ }
+ else if ((s->flags & SEC_HAS_CONTENTS) != 0)
{
/* Allocate memory for the section contents. */
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
- if (s->contents == NULL && s->size != 0)
+ if (s->contents == NULL)
return FALSE;
}
}
|| !add_dynamic_entry (DT_PLTREL, DT_RELA)
|| !add_dynamic_entry (DT_JMPREL, 0))
return FALSE;
+
+ if (elf64_alpha_use_secureplt
+ && !add_dynamic_entry (DT_ALPHA_PLTRO, 1))
+ return FALSE;
}
if (!add_dynamic_entry (DT_RELA, 0)
related to Alpha in particular. They are by David Wall, then of
DEC WRL. */
-#define OP_LDA 0x08
-#define OP_LDAH 0x09
-#define INSN_JSR 0x68004000
-#define INSN_JSR_MASK 0xfc00c000
-#define OP_LDQ 0x29
-#define OP_BR 0x30
-#define OP_BSR 0x34
-#define INSN_UNOP 0x2ffe0000
-#define INSN_ADDQ 0x40000400
-#define INSN_RDUNIQ 0x0000009e
-
struct alpha_relax_info
{
bfd *abfd;
{
if (ELF64_R_TYPE (urel->r_info) != R_ALPHA_LITUSE)
break;
- if (urel->r_addend <= 3)
+ if (urel->r_addend <= 6)
flags |= 1 << urel->r_addend;
}
case LITUSE_ALPHA_JSR:
case LITUSE_ALPHA_TLSGD:
case LITUSE_ALPHA_TLSLDM:
+ case LITUSE_ALPHA_JSRDIRECT:
{
bfd_vma optdest, org;
bfd_signed_vma odisp;
/* If this is a tp-relative relocation against sym 0,
this is hackery from relax_section. Force the value to
- be the tls base. */
+ be the tls module base. */
if (r_symndx == 0
&& (r_type == R_ALPHA_TLSLDM
|| r_type == R_ALPHA_GOTTPREL
|| r_type == R_ALPHA_TPRELHI
|| r_type == R_ALPHA_TPRELLO
|| r_type == R_ALPHA_TPREL16))
- value = tp_base;
+ value = dtp_base;
if (local_got_entries)
gotent = local_got_entries[r_symndx];
struct elf_link_hash_entry *h,
Elf_Internal_Sym *sym)
{
+ struct alpha_elf_link_hash_entry *ah = (struct alpha_elf_link_hash_entry *)h;
bfd *dynobj = elf_hash_table(info)->dynobj;
- if (h->plt.offset != MINUS_ONE)
+ if (h->needs_plt)
{
/* Fill in the .plt entry for this symbol. */
asection *splt, *sgot, *srel;
BFD_ASSERT (h->dynindx != -1);
- /* The first .got entry will be updated by the .plt with the
- address of the target function. */
- gotent = ((struct alpha_elf_link_hash_entry *) h)->got_entries;
- BFD_ASSERT (gotent && gotent->addend == 0);
-
splt = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (splt != NULL);
srel = bfd_get_section_by_name (dynobj, ".rela.plt");
BFD_ASSERT (srel != NULL);
- sgot = alpha_elf_tdata (gotent->gotobj)->got;
- BFD_ASSERT (sgot != NULL);
-
- got_addr = (sgot->output_section->vma
- + sgot->output_offset
- + gotent->got_offset);
- plt_addr = (splt->output_section->vma
- + splt->output_offset
- + h->plt.offset);
-
- plt_index = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
- /* Fill in the entry in the procedure linkage table. */
- {
- bfd_vma insn1, insn2, insn3;
-
- insn1 = PLT_ENTRY_WORD1 | ((-(h->plt.offset + 4) >> 2) & 0x1fffff);
- insn2 = PLT_ENTRY_WORD2;
- insn3 = PLT_ENTRY_WORD3;
-
- bfd_put_32 (output_bfd, insn1, splt->contents + h->plt.offset);
- bfd_put_32 (output_bfd, insn2, splt->contents + h->plt.offset + 4);
- bfd_put_32 (output_bfd, insn3, splt->contents + h->plt.offset + 8);
- }
+ for (gotent = ah->got_entries; gotent ; gotent = gotent->next)
+ if (gotent->reloc_type == R_ALPHA_LITERAL
+ && gotent->use_count > 0)
+ {
+ unsigned int insn;
+ int disp;
- /* Fill in the entry in the .rela.plt section. */
- outrel.r_offset = got_addr;
- outrel.r_info = ELF64_R_INFO(h->dynindx, R_ALPHA_JMP_SLOT);
- outrel.r_addend = 0;
+ sgot = alpha_elf_tdata (gotent->gotobj)->got;
+ BFD_ASSERT (sgot != NULL);
- loc = srel->contents + plt_index * sizeof (Elf64_External_Rela);
- bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+ BFD_ASSERT (gotent->got_offset != -1);
+ BFD_ASSERT (gotent->plt_offset != -1);
- if (!h->def_regular)
- {
- /* Mark the symbol as undefined, rather than as defined in the
- .plt section. Leave the value alone. */
- sym->st_shndx = SHN_UNDEF;
- }
+ got_addr = (sgot->output_section->vma
+ + sgot->output_offset
+ + gotent->got_offset);
+ plt_addr = (splt->output_section->vma
+ + splt->output_offset
+ + gotent->plt_offset);
- /* Fill in the entries in the .got. */
- bfd_put_64 (output_bfd, plt_addr, sgot->contents + gotent->got_offset);
+ plt_index = (gotent->plt_offset-PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
- /* Subsequent .got entries will continue to bounce through the .plt. */
- if (gotent->next)
- {
- srel = bfd_get_section_by_name (dynobj, ".rela.got");
- BFD_ASSERT (! info->shared || srel != NULL);
+ /* Fill in the entry in the procedure linkage table. */
+ if (elf64_alpha_use_secureplt)
+ {
+ disp = (PLT_HEADER_SIZE - 4) - (gotent->plt_offset + 4);
+ insn = INSN_AD (INSN_BR, 31, disp);
+ bfd_put_32 (output_bfd, insn,
+ splt->contents + gotent->plt_offset);
- gotent = gotent->next;
- do
- {
- sgot = alpha_elf_tdata(gotent->gotobj)->got;
- BFD_ASSERT(sgot != NULL);
- BFD_ASSERT(gotent->addend == 0);
+ plt_index = ((gotent->plt_offset - NEW_PLT_HEADER_SIZE)
+ / NEW_PLT_ENTRY_SIZE);
+ }
+ else
+ {
+ disp = -(gotent->plt_offset + 4);
+ insn = INSN_AD (INSN_BR, 28, disp);
+ bfd_put_32 (output_bfd, insn,
+ splt->contents + gotent->plt_offset);
+ bfd_put_32 (output_bfd, INSN_UNOP,
+ splt->contents + gotent->plt_offset + 4);
+ bfd_put_32 (output_bfd, INSN_UNOP,
+ splt->contents + gotent->plt_offset + 8);
+
+ plt_index = ((gotent->plt_offset - OLD_PLT_HEADER_SIZE)
+ / OLD_PLT_ENTRY_SIZE);
+ }
- bfd_put_64 (output_bfd, plt_addr,
- sgot->contents + gotent->got_offset);
+ /* Fill in the entry in the .rela.plt section. */
+ outrel.r_offset = got_addr;
+ outrel.r_info = ELF64_R_INFO(h->dynindx, R_ALPHA_JMP_SLOT);
+ outrel.r_addend = 0;
- if (info->shared)
- elf64_alpha_emit_dynrel (output_bfd, info, sgot, srel,
- gotent->got_offset, 0,
- R_ALPHA_RELATIVE, plt_addr);
+ loc = srel->contents + plt_index * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
- gotent = gotent->next;
- }
- while (gotent != NULL);
- }
+ /* Fill in the entry in the .got. */
+ bfd_put_64 (output_bfd, plt_addr,
+ sgot->contents + gotent->got_offset);
+ }
}
else if (alpha_elf_dynamic_symbol_p (h, info))
{
/* Mark some specially defined symbols as absolute. */
if (strcmp (h->root.root.string, "_DYNAMIC") == 0
- || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0
- || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0)
+ || h == elf_hash_table (info)->hgot
+ || h == elf_hash_table (info)->hplt)
sym->st_shndx = SHN_ABS;
return TRUE;
if (elf_hash_table (info)->dynamic_sections_created)
{
- asection *splt;
+ asection *splt, *sgotplt, *srelaplt;
Elf64_External_Dyn *dyncon, *dynconend;
+ bfd_vma plt_vma, gotplt_vma;
splt = bfd_get_section_by_name (dynobj, ".plt");
+ srelaplt = bfd_get_section_by_name (output_bfd, ".rela.plt");
BFD_ASSERT (splt != NULL && sdyn != NULL);
+ plt_vma = splt->output_section->vma + splt->output_offset;
+
+ gotplt_vma = 0;
+ if (elf64_alpha_use_secureplt)
+ {
+ sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
+ BFD_ASSERT (sgotplt != NULL);
+ if (sgotplt->size > 0)
+ gotplt_vma = sgotplt->output_section->vma + sgotplt->output_offset;
+ }
+
dyncon = (Elf64_External_Dyn *) sdyn->contents;
dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
- const char *name;
- asection *s;
bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
case DT_PLTGOT:
- name = ".plt";
- goto get_vma;
+ dyn.d_un.d_ptr
+ = elf64_alpha_use_secureplt ? gotplt_vma : plt_vma;
+ break;
case DT_PLTRELSZ:
- name = ".rela.plt";
- goto get_size;
+ dyn.d_un.d_val = srelaplt ? srelaplt->size : 0;
+ break;
case DT_JMPREL:
- name = ".rela.plt";
- goto get_vma;
+ dyn.d_un.d_ptr = srelaplt ? srelaplt->vma : 0;
+ break;
case DT_RELASZ:
/* My interpretation of the TIS v1.1 ELF document indicates
the rest of the BFD does. It is, however, what the
glibc ld.so wants. Do this fixup here until we found
out who is right. */
- s = bfd_get_section_by_name (output_bfd, ".rela.plt");
- if (s)
- dyn.d_un.d_val -= s->size;
- break;
-
- get_vma:
- s = bfd_get_section_by_name (output_bfd, name);
- dyn.d_un.d_ptr = (s ? s->vma : 0);
- break;
-
- get_size:
- s = bfd_get_section_by_name (output_bfd, name);
- dyn.d_un.d_val = s->size;
+ if (srelaplt)
+ dyn.d_un.d_val -= srelaplt->size;
break;
}
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
}
- /* Initialize the PLT0 entry. */
+ /* Initialize the plt header. */
if (splt->size > 0)
{
- bfd_put_32 (output_bfd, PLT_HEADER_WORD1, splt->contents);
- bfd_put_32 (output_bfd, PLT_HEADER_WORD2, splt->contents + 4);
- bfd_put_32 (output_bfd, PLT_HEADER_WORD3, splt->contents + 8);
- bfd_put_32 (output_bfd, PLT_HEADER_WORD4, splt->contents + 12);
+ unsigned int insn;
+ int ofs;
+
+ if (elf64_alpha_use_secureplt)
+ {
+ ofs = gotplt_vma - (plt_vma + PLT_HEADER_SIZE);
- /* The next two words will be filled in by ld.so */
- bfd_put_64 (output_bfd, (bfd_vma) 0, splt->contents + 16);
- bfd_put_64 (output_bfd, (bfd_vma) 0, splt->contents + 24);
+ insn = INSN_ABC (INSN_SUBQ, 27, 28, 25);
+ bfd_put_32 (output_bfd, insn, splt->contents);
+
+ insn = INSN_ABO (INSN_LDAH, 28, 28, (ofs + 0x8000) >> 16);
+ bfd_put_32 (output_bfd, insn, splt->contents + 4);
+
+ insn = INSN_ABC (INSN_S4SUBQ, 25, 25, 25);
+ bfd_put_32 (output_bfd, insn, splt->contents + 8);
+
+ insn = INSN_ABO (INSN_LDA, 28, 28, ofs);
+ bfd_put_32 (output_bfd, insn, splt->contents + 12);
+
+ insn = INSN_ABO (INSN_LDQ, 27, 28, 0);
+ bfd_put_32 (output_bfd, insn, splt->contents + 16);
+
+ insn = INSN_ABC (INSN_ADDQ, 25, 25, 25);
+ bfd_put_32 (output_bfd, insn, splt->contents + 20);
+
+ insn = INSN_ABO (INSN_LDQ, 28, 28, 8);
+ bfd_put_32 (output_bfd, insn, splt->contents + 24);
+
+ insn = INSN_AB (INSN_JMP, 31, 27);
+ bfd_put_32 (output_bfd, insn, splt->contents + 28);
+
+ insn = INSN_AD (INSN_BR, 28, -PLT_HEADER_SIZE);
+ bfd_put_32 (output_bfd, insn, splt->contents + 32);
+ }
+ else
+ {
+ insn = INSN_AD (INSN_BR, 27, 0); /* br $27, .+4 */
+ bfd_put_32 (output_bfd, insn, splt->contents);
+
+ insn = INSN_ABO (INSN_LDQ, 27, 27, 12);
+ bfd_put_32 (output_bfd, insn, splt->contents + 4);
+
+ insn = INSN_UNOP;
+ bfd_put_32 (output_bfd, insn, splt->contents + 8);
+
+ insn = INSN_AB (INSN_JMP, 27, 27);
+ bfd_put_32 (output_bfd, insn, splt->contents + 12);
+
+ /* The next two words will be filled in by ld.so. */
+ bfd_put_64 (output_bfd, 0, splt->contents + 16);
+ bfd_put_64 (output_bfd, 0, splt->contents + 24);
+ }
elf_section_data (splt->output_section)->this_hdr.sh_entsize = 0;
}
}
}
\f
-static struct bfd_elf_special_section const
- alpha_special_sections_s[]=
+static const struct bfd_elf_special_section elf64_alpha_special_sections[] =
{
- { ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_ALPHA_GPREL },
{ ".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_ALPHA_GPREL },
+ { ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_ALPHA_GPREL },
{ NULL, 0, 0, 0, 0 }
};
-static struct bfd_elf_special_section const *
- elf64_alpha_special_sections[27] =
-{
- NULL, /* 'a' */
- NULL, /* 'b' */
- NULL, /* 'c' */
- NULL, /* 'd' */
- NULL, /* 'e' */
- NULL, /* 'f' */
- NULL, /* 'g' */
- NULL, /* 'h' */
- NULL, /* 'i' */
- NULL, /* 'j' */
- NULL, /* 'k' */
- NULL, /* 'l' */
- NULL, /* 'm' */
- NULL, /* 'n' */
- NULL, /* 'o' */
- NULL, /* 'p' */
- NULL, /* 'q' */
- NULL, /* 'r' */
- alpha_special_sections_s, /* 's' */
- NULL, /* 't' */
- NULL, /* 'u' */
- NULL, /* 'v' */
- NULL, /* 'w' */
- NULL, /* 'x' */
- NULL, /* 'y' */
- NULL, /* 'z' */
- NULL /* other */
-};
-
/* ECOFF swapping routines. These are used when dealing with the
.mdebug section, which is in the ECOFF debugging format. Copied
from elf32-mips.c. */