Line 1... |
Line 1... |
/* Xtensa-specific support for 32-bit ELF.
|
/* Xtensa-specific support for 32-bit ELF.
|
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009
|
|
Free Software Foundation, Inc.
|
|
|
This file is part of BFD, the Binary File Descriptor library.
|
This file is part of BFD, the Binary File Descriptor library.
|
|
|
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
modify it under the terms of the GNU General Public License as
|
modify it under the terms of the GNU General Public License as
|
Line 106... |
Line 107... |
static bfd_boolean xtensa_is_insntable_section (asection *);
|
static bfd_boolean xtensa_is_insntable_section (asection *);
|
static bfd_boolean xtensa_is_littable_section (asection *);
|
static bfd_boolean xtensa_is_littable_section (asection *);
|
static bfd_boolean xtensa_is_proptable_section (asection *);
|
static bfd_boolean xtensa_is_proptable_section (asection *);
|
static int internal_reloc_compare (const void *, const void *);
|
static int internal_reloc_compare (const void *, const void *);
|
static int internal_reloc_matches (const void *, const void *);
|
static int internal_reloc_matches (const void *, const void *);
|
extern asection *xtensa_get_property_section (asection *, const char *);
|
static asection *xtensa_get_property_section (asection *, const char *);
|
|
extern asection *xtensa_make_property_section (asection *, const char *);
|
static flagword xtensa_get_property_predef_flags (asection *);
|
static flagword xtensa_get_property_predef_flags (asection *);
|
|
|
/* Other functions called directly by the linker. */
|
/* Other functions called directly by the linker. */
|
|
|
typedef void (*deps_callback_t)
|
typedef void (*deps_callback_t)
|
Line 285... |
Line 287... |
bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_ALT", FALSE, 0, 0, TRUE),
|
bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_ALT", FALSE, 0, 0, TRUE),
|
HOWTO (R_XTENSA_SLOT13_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
|
HOWTO (R_XTENSA_SLOT13_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
|
bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT", FALSE, 0, 0, TRUE),
|
bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT", FALSE, 0, 0, TRUE),
|
HOWTO (R_XTENSA_SLOT14_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
|
HOWTO (R_XTENSA_SLOT14_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
|
bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT", FALSE, 0, 0, TRUE),
|
bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT", FALSE, 0, 0, TRUE),
|
|
|
|
/* TLS relocations. */
|
|
HOWTO (R_XTENSA_TLSDESC_FN, 0, 2, 32, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_FN",
|
|
FALSE, 0, 0xffffffff, FALSE),
|
|
HOWTO (R_XTENSA_TLSDESC_ARG, 0, 2, 32, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_ARG",
|
|
FALSE, 0, 0xffffffff, FALSE),
|
|
HOWTO (R_XTENSA_TLS_DTPOFF, 0, 2, 32, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLS_DTPOFF",
|
|
FALSE, 0, 0xffffffff, FALSE),
|
|
HOWTO (R_XTENSA_TLS_TPOFF, 0, 2, 32, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLS_TPOFF",
|
|
FALSE, 0, 0xffffffff, FALSE),
|
|
HOWTO (R_XTENSA_TLS_FUNC, 0, 0, 0, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLS_FUNC",
|
|
FALSE, 0, 0, FALSE),
|
|
HOWTO (R_XTENSA_TLS_ARG, 0, 0, 0, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLS_ARG",
|
|
FALSE, 0, 0, FALSE),
|
|
HOWTO (R_XTENSA_TLS_CALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
|
|
bfd_elf_xtensa_reloc, "R_XTENSA_TLS_CALL",
|
|
FALSE, 0, 0, FALSE),
|
};
|
};
|
|
|
#if DEBUG_GEN_RELOC
|
#if DEBUG_GEN_RELOC
|
#define TRACE(str) \
|
#define TRACE(str) \
|
fprintf (stderr, "Xtensa bfd reloc lookup %d (%s)\n", code, str)
|
fprintf (stderr, "Xtensa bfd reloc lookup %d (%s)\n", code, str)
|
Line 372... |
Line 397... |
|
|
case BFD_RELOC_VTABLE_ENTRY:
|
case BFD_RELOC_VTABLE_ENTRY:
|
TRACE ("BFD_RELOC_VTABLE_ENTRY");
|
TRACE ("BFD_RELOC_VTABLE_ENTRY");
|
return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ];
|
return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ];
|
|
|
|
case BFD_RELOC_XTENSA_TLSDESC_FN:
|
|
TRACE ("BFD_RELOC_XTENSA_TLSDESC_FN");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_FN ];
|
|
|
|
case BFD_RELOC_XTENSA_TLSDESC_ARG:
|
|
TRACE ("BFD_RELOC_XTENSA_TLSDESC_ARG");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_ARG ];
|
|
|
|
case BFD_RELOC_XTENSA_TLS_DTPOFF:
|
|
TRACE ("BFD_RELOC_XTENSA_TLS_DTPOFF");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLS_DTPOFF ];
|
|
|
|
case BFD_RELOC_XTENSA_TLS_TPOFF:
|
|
TRACE ("BFD_RELOC_XTENSA_TLS_TPOFF");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLS_TPOFF ];
|
|
|
|
case BFD_RELOC_XTENSA_TLS_FUNC:
|
|
TRACE ("BFD_RELOC_XTENSA_TLS_FUNC");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLS_FUNC ];
|
|
|
|
case BFD_RELOC_XTENSA_TLS_ARG:
|
|
TRACE ("BFD_RELOC_XTENSA_TLS_ARG");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLS_ARG ];
|
|
|
|
case BFD_RELOC_XTENSA_TLS_CALL:
|
|
TRACE ("BFD_RELOC_XTENSA_TLS_CALL");
|
|
return &elf_howto_table[(unsigned) R_XTENSA_TLS_CALL ];
|
|
|
default:
|
default:
|
if (code >= BFD_RELOC_XTENSA_SLOT0_OP
|
if (code >= BFD_RELOC_XTENSA_SLOT0_OP
|
&& code <= BFD_RELOC_XTENSA_SLOT14_OP)
|
&& code <= BFD_RELOC_XTENSA_SLOT14_OP)
|
{
|
{
|
unsigned n = (R_XTENSA_SLOT0_OP +
|
unsigned n = (R_XTENSA_SLOT0_OP +
|
Line 476... |
Line 529... |
0xb1, 0x00, 0x00, /* l32r a11, [literal for reloc index] */
|
0xb1, 0x00, 0x00, /* l32r a11, [literal for reloc index] */
|
0xa0, 0x08, 0x00, /* jx a8 */
|
0xa0, 0x08, 0x00, /* jx a8 */
|
0 /* unused */
|
0 /* unused */
|
};
|
};
|
|
|
|
/* The size of the thread control block. */
|
|
#define TCB_SIZE 8
|
|
|
|
struct elf_xtensa_link_hash_entry
|
|
{
|
|
struct elf_link_hash_entry elf;
|
|
|
|
bfd_signed_vma tlsfunc_refcount;
|
|
|
|
#define GOT_UNKNOWN 0
|
|
#define GOT_NORMAL 1
|
|
#define GOT_TLS_GD 2 /* global or local dynamic */
|
|
#define GOT_TLS_IE 4 /* initial or local exec */
|
|
#define GOT_TLS_ANY (GOT_TLS_GD | GOT_TLS_IE)
|
|
unsigned char tls_type;
|
|
};
|
|
|
|
#define elf_xtensa_hash_entry(ent) ((struct elf_xtensa_link_hash_entry *)(ent))
|
|
|
|
struct elf_xtensa_obj_tdata
|
|
{
|
|
struct elf_obj_tdata root;
|
|
|
|
/* tls_type for each local got entry. */
|
|
char *local_got_tls_type;
|
|
|
|
bfd_signed_vma *local_tlsfunc_refcounts;
|
|
};
|
|
|
|
#define elf_xtensa_tdata(abfd) \
|
|
((struct elf_xtensa_obj_tdata *) (abfd)->tdata.any)
|
|
|
|
#define elf_xtensa_local_got_tls_type(abfd) \
|
|
(elf_xtensa_tdata (abfd)->local_got_tls_type)
|
|
|
|
#define elf_xtensa_local_tlsfunc_refcounts(abfd) \
|
|
(elf_xtensa_tdata (abfd)->local_tlsfunc_refcounts)
|
|
|
|
#define is_xtensa_elf(bfd) \
|
|
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
|
|
&& elf_tdata (bfd) != NULL \
|
|
&& elf_object_id (bfd) == XTENSA_ELF_TDATA)
|
|
|
|
static bfd_boolean
|
|
elf_xtensa_mkobject (bfd *abfd)
|
|
{
|
|
return bfd_elf_allocate_object (abfd, sizeof (struct elf_xtensa_obj_tdata),
|
|
XTENSA_ELF_TDATA);
|
|
}
|
|
|
/* Xtensa ELF linker hash table. */
|
/* Xtensa ELF linker hash table. */
|
|
|
struct elf_xtensa_link_hash_table
|
struct elf_xtensa_link_hash_table
|
{
|
{
|
struct elf_link_hash_table elf;
|
struct elf_link_hash_table elf;
|
Line 498... |
Line 601... |
the sections have to be created before size_dynamic_sections,
|
the sections have to be created before size_dynamic_sections,
|
where we figure out the exact number of PLT entries that will be
|
where we figure out the exact number of PLT entries that will be
|
needed. It is OK if this count is an overestimate, e.g., some
|
needed. It is OK if this count is an overestimate, e.g., some
|
relocations may be removed by GC. */
|
relocations may be removed by GC. */
|
int plt_reloc_count;
|
int plt_reloc_count;
|
|
|
|
struct elf_xtensa_link_hash_entry *tlsbase;
|
};
|
};
|
|
|
/* Get the Xtensa ELF linker hash table from a link_info structure. */
|
/* Get the Xtensa ELF linker hash table from a link_info structure. */
|
|
|
#define elf_xtensa_hash_table(p) \
|
#define elf_xtensa_hash_table(p) \
|
((struct elf_xtensa_link_hash_table *) ((p)->hash))
|
((struct elf_xtensa_link_hash_table *) ((p)->hash))
|
|
|
|
/* Create an entry in an Xtensa ELF linker hash table. */
|
|
|
|
static struct bfd_hash_entry *
|
|
elf_xtensa_link_hash_newfunc (struct bfd_hash_entry *entry,
|
|
struct bfd_hash_table *table,
|
|
const char *string)
|
|
{
|
|
/* Allocate the structure if it has not already been allocated by a
|
|
subclass. */
|
|
if (entry == NULL)
|
|
{
|
|
entry = bfd_hash_allocate (table,
|
|
sizeof (struct elf_xtensa_link_hash_entry));
|
|
if (entry == NULL)
|
|
return entry;
|
|
}
|
|
|
|
/* Call the allocation method of the superclass. */
|
|
entry = _bfd_elf_link_hash_newfunc (entry, table, string);
|
|
if (entry != NULL)
|
|
{
|
|
struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (entry);
|
|
eh->tlsfunc_refcount = 0;
|
|
eh->tls_type = GOT_UNKNOWN;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
/* Create an Xtensa ELF linker hash table. */
|
/* Create an Xtensa ELF linker hash table. */
|
|
|
static struct bfd_link_hash_table *
|
static struct bfd_link_hash_table *
|
elf_xtensa_link_hash_table_create (bfd *abfd)
|
elf_xtensa_link_hash_table_create (bfd *abfd)
|
{
|
{
|
|
struct elf_link_hash_entry *tlsbase;
|
struct elf_xtensa_link_hash_table *ret;
|
struct elf_xtensa_link_hash_table *ret;
|
bfd_size_type amt = sizeof (struct elf_xtensa_link_hash_table);
|
bfd_size_type amt = sizeof (struct elf_xtensa_link_hash_table);
|
|
|
ret = bfd_malloc (amt);
|
ret = bfd_malloc (amt);
|
if (ret == NULL)
|
if (ret == NULL)
|
return NULL;
|
return NULL;
|
|
|
if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
|
if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
|
_bfd_elf_link_hash_newfunc,
|
elf_xtensa_link_hash_newfunc,
|
sizeof (struct elf_link_hash_entry)))
|
sizeof (struct elf_xtensa_link_hash_entry)))
|
{
|
{
|
free (ret);
|
free (ret);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
Line 535... |
Line 670... |
ret->sgotloc = NULL;
|
ret->sgotloc = NULL;
|
ret->spltlittbl = NULL;
|
ret->spltlittbl = NULL;
|
|
|
ret->plt_reloc_count = 0;
|
ret->plt_reloc_count = 0;
|
|
|
|
/* Create a hash entry for "_TLS_MODULE_BASE_" to speed up checking
|
|
for it later. */
|
|
tlsbase = elf_link_hash_lookup (&ret->elf, "_TLS_MODULE_BASE_",
|
|
TRUE, FALSE, FALSE);
|
|
tlsbase->root.type = bfd_link_hash_new;
|
|
tlsbase->root.u.undef.abfd = NULL;
|
|
tlsbase->non_elf = 0;
|
|
ret->tlsbase = elf_xtensa_hash_entry (tlsbase);
|
|
ret->tlsbase->tls_type = GOT_UNKNOWN;
|
|
|
return &ret->elf.root;
|
return &ret->elf.root;
|
}
|
}
|
|
|
|
/* Copy the extra info we tack onto an elf_link_hash_entry. */
|
|
|
|
static void
|
|
elf_xtensa_copy_indirect_symbol (struct bfd_link_info *info,
|
|
struct elf_link_hash_entry *dir,
|
|
struct elf_link_hash_entry *ind)
|
|
{
|
|
struct elf_xtensa_link_hash_entry *edir, *eind;
|
|
|
|
edir = elf_xtensa_hash_entry (dir);
|
|
eind = elf_xtensa_hash_entry (ind);
|
|
|
|
if (ind->root.type == bfd_link_hash_indirect)
|
|
{
|
|
edir->tlsfunc_refcount += eind->tlsfunc_refcount;
|
|
eind->tlsfunc_refcount = 0;
|
|
|
|
if (dir->got.refcount <= 0)
|
|
{
|
|
edir->tls_type = eind->tls_type;
|
|
eind->tls_type = GOT_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
_bfd_elf_link_hash_copy_indirect (info, dir, ind);
|
|
}
|
|
|
static inline bfd_boolean
|
static inline bfd_boolean
|
elf_xtensa_dynamic_symbol_p (struct elf_link_hash_entry *h,
|
elf_xtensa_dynamic_symbol_p (struct elf_link_hash_entry *h,
|
struct bfd_link_info *info)
|
struct bfd_link_info *info)
|
{
|
{
|
/* Check if we should do dynamic things to this symbol. The
|
/* Check if we should do dynamic things to this symbol. The
|
Line 797... |
Line 969... |
Elf_Internal_Shdr *symtab_hdr;
|
Elf_Internal_Shdr *symtab_hdr;
|
struct elf_link_hash_entry **sym_hashes;
|
struct elf_link_hash_entry **sym_hashes;
|
const Elf_Internal_Rela *rel;
|
const Elf_Internal_Rela *rel;
|
const Elf_Internal_Rela *rel_end;
|
const Elf_Internal_Rela *rel_end;
|
|
|
if (info->relocatable)
|
if (info->relocatable || (sec->flags & SEC_ALLOC) == 0)
|
return TRUE;
|
return TRUE;
|
|
|
|
BFD_ASSERT (is_xtensa_elf (abfd));
|
|
|
htab = elf_xtensa_hash_table (info);
|
htab = elf_xtensa_hash_table (info);
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
sym_hashes = elf_sym_hashes (abfd);
|
sym_hashes = elf_sym_hashes (abfd);
|
|
|
rel_end = relocs + sec->reloc_count;
|
rel_end = relocs + sec->reloc_count;
|
for (rel = relocs; rel < rel_end; rel++)
|
for (rel = relocs; rel < rel_end; rel++)
|
{
|
{
|
unsigned int r_type;
|
unsigned int r_type;
|
unsigned long r_symndx;
|
unsigned long r_symndx;
|
struct elf_link_hash_entry *h;
|
struct elf_link_hash_entry *h = NULL;
|
|
struct elf_xtensa_link_hash_entry *eh;
|
|
int tls_type, old_tls_type;
|
|
bfd_boolean is_got = FALSE;
|
|
bfd_boolean is_plt = FALSE;
|
|
bfd_boolean is_tlsfunc = FALSE;
|
|
|
r_symndx = ELF32_R_SYM (rel->r_info);
|
r_symndx = ELF32_R_SYM (rel->r_info);
|
r_type = ELF32_R_TYPE (rel->r_info);
|
r_type = ELF32_R_TYPE (rel->r_info);
|
|
|
if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
|
if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
|
Line 821... |
Line 1000... |
(*_bfd_error_handler) (_("%B: bad symbol index: %d"),
|
(*_bfd_error_handler) (_("%B: bad symbol index: %d"),
|
abfd, r_symndx);
|
abfd, r_symndx);
|
return FALSE;
|
return FALSE;
|
}
|
}
|
|
|
if (r_symndx < symtab_hdr->sh_info)
|
if (r_symndx >= symtab_hdr->sh_info)
|
h = NULL;
|
|
else
|
|
{
|
{
|
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
|
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
|
while (h->root.type == bfd_link_hash_indirect
|
while (h->root.type == bfd_link_hash_indirect
|
|| h->root.type == bfd_link_hash_warning)
|
|| h->root.type == bfd_link_hash_warning)
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
}
|
}
|
|
eh = elf_xtensa_hash_entry (h);
|
|
|
switch (r_type)
|
switch (r_type)
|
{
|
{
|
case R_XTENSA_32:
|
case R_XTENSA_TLSDESC_FN:
|
if (h == NULL)
|
if (info->shared)
|
goto local_literal;
|
{
|
|
tls_type = GOT_TLS_GD;
|
|
is_got = TRUE;
|
|
is_tlsfunc = TRUE;
|
|
}
|
|
else
|
|
tls_type = GOT_TLS_IE;
|
|
break;
|
|
|
if ((sec->flags & SEC_ALLOC) != 0)
|
case R_XTENSA_TLSDESC_ARG:
|
|
if (info->shared)
|
{
|
{
|
if (h->got.refcount <= 0)
|
tls_type = GOT_TLS_GD;
|
h->got.refcount = 1;
|
is_got = TRUE;
|
|
}
|
else
|
else
|
h->got.refcount += 1;
|
{
|
|
tls_type = GOT_TLS_IE;
|
|
if (h && elf_xtensa_hash_entry (h) != htab->tlsbase)
|
|
is_got = TRUE;
|
}
|
}
|
break;
|
break;
|
|
|
|
case R_XTENSA_TLS_DTPOFF:
|
|
if (info->shared)
|
|
tls_type = GOT_TLS_GD;
|
|
else
|
|
tls_type = GOT_TLS_IE;
|
|
break;
|
|
|
|
case R_XTENSA_TLS_TPOFF:
|
|
tls_type = GOT_TLS_IE;
|
|
if (info->shared)
|
|
info->flags |= DF_STATIC_TLS;
|
|
if (info->shared || h)
|
|
is_got = TRUE;
|
|
break;
|
|
|
|
case R_XTENSA_32:
|
|
tls_type = GOT_NORMAL;
|
|
is_got = TRUE;
|
|
break;
|
|
|
case R_XTENSA_PLT:
|
case R_XTENSA_PLT:
|
/* If this relocation is against a local symbol, then it's
|
tls_type = GOT_NORMAL;
|
exactly the same as a normal local GOT entry. */
|
is_plt = TRUE;
|
if (h == NULL)
|
break;
|
goto local_literal;
|
|
|
|
if ((sec->flags & SEC_ALLOC) != 0)
|
case R_XTENSA_GNU_VTINHERIT:
|
|
/* This relocation describes the C++ object vtable hierarchy.
|
|
Reconstruct it for later use during GC. */
|
|
if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
|
|
return FALSE;
|
|
continue;
|
|
|
|
case R_XTENSA_GNU_VTENTRY:
|
|
/* This relocation describes which C++ vtable entries are actually
|
|
used. Record for later use during GC. */
|
|
BFD_ASSERT (h != NULL);
|
|
if (h != NULL
|
|
&& !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
|
|
return FALSE;
|
|
continue;
|
|
|
|
default:
|
|
/* Nothing to do for any other relocations. */
|
|
continue;
|
|
}
|
|
|
|
if (h)
|
|
{
|
|
if (is_plt)
|
{
|
{
|
if (h->plt.refcount <= 0)
|
if (h->plt.refcount <= 0)
|
{
|
{
|
h->needs_plt = 1;
|
h->needs_plt = 1;
|
h->plt.refcount = 1;
|
h->plt.refcount = 1;
|
Line 873... |
Line 1105... |
{
|
{
|
if (! add_extra_plt_sections (info, htab->plt_reloc_count))
|
if (! add_extra_plt_sections (info, htab->plt_reloc_count))
|
return FALSE;
|
return FALSE;
|
}
|
}
|
}
|
}
|
break;
|
else if (is_got)
|
|
|
local_literal:
|
|
if ((sec->flags & SEC_ALLOC) != 0)
|
|
{
|
{
|
bfd_signed_vma *local_got_refcounts;
|
if (h->got.refcount <= 0)
|
|
h->got.refcount = 1;
|
|
else
|
|
h->got.refcount += 1;
|
|
}
|
|
|
/* This is a global offset table entry for a local symbol. */
|
if (is_tlsfunc)
|
local_got_refcounts = elf_local_got_refcounts (abfd);
|
eh->tlsfunc_refcount += 1;
|
if (local_got_refcounts == NULL)
|
|
|
old_tls_type = eh->tls_type;
|
|
}
|
|
else
|
{
|
{
|
bfd_size_type size;
|
/* Allocate storage the first time. */
|
|
if (elf_local_got_refcounts (abfd) == NULL)
|
|
{
|
|
bfd_size_type size = symtab_hdr->sh_info;
|
|
void *mem;
|
|
|
size = symtab_hdr->sh_info;
|
mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma));
|
size *= sizeof (bfd_signed_vma);
|
if (mem == NULL)
|
local_got_refcounts =
|
|
(bfd_signed_vma *) bfd_zalloc (abfd, size);
|
|
if (local_got_refcounts == NULL)
|
|
return FALSE;
|
return FALSE;
|
elf_local_got_refcounts (abfd) = local_got_refcounts;
|
elf_local_got_refcounts (abfd) = (bfd_signed_vma *) mem;
|
}
|
|
local_got_refcounts[r_symndx] += 1;
|
|
}
|
|
break;
|
|
|
|
case R_XTENSA_GNU_VTINHERIT:
|
mem = bfd_zalloc (abfd, size);
|
/* This relocation describes the C++ object vtable hierarchy.
|
if (mem == NULL)
|
Reconstruct it for later use during GC. */
|
|
if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
|
|
return FALSE;
|
return FALSE;
|
break;
|
elf_xtensa_local_got_tls_type (abfd) = (char *) mem;
|
|
|
case R_XTENSA_GNU_VTENTRY:
|
mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma));
|
/* This relocation describes which C++ vtable entries are actually
|
if (mem == NULL)
|
used. Record for later use during GC. */
|
|
BFD_ASSERT (h != NULL);
|
|
if (h != NULL
|
|
&& !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
|
|
return FALSE;
|
return FALSE;
|
break;
|
elf_xtensa_local_tlsfunc_refcounts (abfd)
|
|
= (bfd_signed_vma *) mem;
|
|
}
|
|
|
default:
|
/* This is a global offset table entry for a local symbol. */
|
break;
|
if (is_got || is_plt)
|
|
elf_local_got_refcounts (abfd) [r_symndx] += 1;
|
|
|
|
if (is_tlsfunc)
|
|
elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx] += 1;
|
|
|
|
old_tls_type = elf_xtensa_local_got_tls_type (abfd) [r_symndx];
|
|
}
|
|
|
|
if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_IE))
|
|
tls_type |= old_tls_type;
|
|
/* If a TLS symbol is accessed using IE at least once,
|
|
there is no point to use a dynamic model for it. */
|
|
else if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
|
|
&& ((old_tls_type & GOT_TLS_GD) == 0
|
|
|| (tls_type & GOT_TLS_IE) == 0))
|
|
{
|
|
if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_GD))
|
|
tls_type = old_tls_type;
|
|
else if ((old_tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_GD))
|
|
tls_type |= old_tls_type;
|
|
else
|
|
{
|
|
(*_bfd_error_handler)
|
|
(_("%B: `%s' accessed both as normal and thread local symbol"),
|
|
abfd,
|
|
h ? h->root.root.string : "<local>");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (old_tls_type != tls_type)
|
|
{
|
|
if (eh)
|
|
eh->tls_type = tls_type;
|
|
else
|
|
elf_xtensa_local_got_tls_type (abfd) [r_symndx] = tls_type;
|
}
|
}
|
}
|
}
|
|
|
return TRUE;
|
return TRUE;
|
}
|
}
|
Line 1001... |
Line 1266... |
/* Update the GOT & PLT entry reference counts
|
/* Update the GOT & PLT entry reference counts
|
for the section being removed. */
|
for the section being removed. */
|
|
|
static bfd_boolean
|
static bfd_boolean
|
elf_xtensa_gc_sweep_hook (bfd *abfd,
|
elf_xtensa_gc_sweep_hook (bfd *abfd,
|
struct bfd_link_info *info ATTRIBUTE_UNUSED,
|
struct bfd_link_info *info,
|
asection *sec,
|
asection *sec,
|
const Elf_Internal_Rela *relocs)
|
const Elf_Internal_Rela *relocs)
|
{
|
{
|
Elf_Internal_Shdr *symtab_hdr;
|
Elf_Internal_Shdr *symtab_hdr;
|
struct elf_link_hash_entry **sym_hashes;
|
struct elf_link_hash_entry **sym_hashes;
|
bfd_signed_vma *local_got_refcounts;
|
|
const Elf_Internal_Rela *rel, *relend;
|
const Elf_Internal_Rela *rel, *relend;
|
|
struct elf_xtensa_link_hash_table *htab;
|
|
|
|
htab = elf_xtensa_hash_table (info);
|
|
|
if (info->relocatable)
|
if (info->relocatable)
|
return TRUE;
|
return TRUE;
|
|
|
if ((sec->flags & SEC_ALLOC) == 0)
|
if ((sec->flags & SEC_ALLOC) == 0)
|
return TRUE;
|
return TRUE;
|
|
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
|
sym_hashes = elf_sym_hashes (abfd);
|
sym_hashes = elf_sym_hashes (abfd);
|
local_got_refcounts = elf_local_got_refcounts (abfd);
|
|
|
|
relend = relocs + sec->reloc_count;
|
relend = relocs + sec->reloc_count;
|
for (rel = relocs; rel < relend; rel++)
|
for (rel = relocs; rel < relend; rel++)
|
{
|
{
|
unsigned long r_symndx;
|
unsigned long r_symndx;
|
unsigned int r_type;
|
unsigned int r_type;
|
struct elf_link_hash_entry *h = NULL;
|
struct elf_link_hash_entry *h = NULL;
|
|
struct elf_xtensa_link_hash_entry *eh;
|
|
bfd_boolean is_got = FALSE;
|
|
bfd_boolean is_plt = FALSE;
|
|
bfd_boolean is_tlsfunc = FALSE;
|
|
|
r_symndx = ELF32_R_SYM (rel->r_info);
|
r_symndx = ELF32_R_SYM (rel->r_info);
|
if (r_symndx >= symtab_hdr->sh_info)
|
if (r_symndx >= symtab_hdr->sh_info)
|
{
|
{
|
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
|
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
|
while (h->root.type == bfd_link_hash_indirect
|
while (h->root.type == bfd_link_hash_indirect
|
|| h->root.type == bfd_link_hash_warning)
|
|| h->root.type == bfd_link_hash_warning)
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
}
|
}
|
|
eh = elf_xtensa_hash_entry (h);
|
|
|
r_type = ELF32_R_TYPE (rel->r_info);
|
r_type = ELF32_R_TYPE (rel->r_info);
|
switch (r_type)
|
switch (r_type)
|
{
|
{
|
case R_XTENSA_32:
|
case R_XTENSA_TLSDESC_FN:
|
if (h == NULL)
|
if (info->shared)
|
goto local_literal;
|
{
|
if (h->got.refcount > 0)
|
is_got = TRUE;
|
h->got.refcount--;
|
is_tlsfunc = TRUE;
|
|
}
|
break;
|
break;
|
|
|
case R_XTENSA_PLT:
|
case R_XTENSA_TLSDESC_ARG:
|
if (h == NULL)
|
if (info->shared)
|
goto local_literal;
|
is_got = TRUE;
|
if (h->plt.refcount > 0)
|
else
|
h->plt.refcount--;
|
{
|
|
if (h && elf_xtensa_hash_entry (h) != htab->tlsbase)
|
|
is_got = TRUE;
|
|
}
|
break;
|
break;
|
|
|
local_literal:
|
case R_XTENSA_TLS_TPOFF:
|
if (local_got_refcounts[r_symndx] > 0)
|
if (info->shared || h)
|
local_got_refcounts[r_symndx] -= 1;
|
is_got = TRUE;
|
break;
|
break;
|
|
|
default:
|
case R_XTENSA_32:
|
|
is_got = TRUE;
|
break;
|
break;
|
|
|
|
case R_XTENSA_PLT:
|
|
is_plt = TRUE;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (h)
|
|
{
|
|
if (is_plt)
|
|
{
|
|
if (h->plt.refcount > 0)
|
|
h->plt.refcount--;
|
|
}
|
|
else if (is_got)
|
|
{
|
|
if (h->got.refcount > 0)
|
|
h->got.refcount--;
|
|
}
|
|
if (is_tlsfunc)
|
|
{
|
|
if (eh->tlsfunc_refcount > 0)
|
|
eh->tlsfunc_refcount--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (is_got || is_plt)
|
|
{
|
|
bfd_signed_vma *got_refcount
|
|
= &elf_local_got_refcounts (abfd) [r_symndx];
|
|
if (*got_refcount > 0)
|
|
*got_refcount -= 1;
|
|
}
|
|
if (is_tlsfunc)
|
|
{
|
|
bfd_signed_vma *tlsfunc_refcount
|
|
= &elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx];
|
|
if (*tlsfunc_refcount > 0)
|
|
*tlsfunc_refcount -= 1;
|
|
}
|
}
|
}
|
}
|
}
|
|
|
return TRUE;
|
return TRUE;
|
}
|
}
|
Line 1084... |
Line 1403... |
return FALSE;
|
return FALSE;
|
htab->splt = bfd_get_section_by_name (dynobj, ".plt");
|
htab->splt = bfd_get_section_by_name (dynobj, ".plt");
|
htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
|
htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
|
htab->sgot = bfd_get_section_by_name (dynobj, ".got");
|
htab->sgot = bfd_get_section_by_name (dynobj, ".got");
|
htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
|
htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
|
|
htab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
|
|
|
/* Create any extra PLT sections in case check_relocs has already
|
/* Create any extra PLT sections in case check_relocs has already
|
been called on all the non-dynamic input files. */
|
been called on all the non-dynamic input files. */
|
if (! add_extra_plt_sections (info, htab->plt_reloc_count))
|
if (! add_extra_plt_sections (info, htab->plt_reloc_count))
|
return FALSE;
|
return FALSE;
|
Line 1099... |
Line 1419... |
/* Mark the ".got.plt" section READONLY. */
|
/* Mark the ".got.plt" section READONLY. */
|
if (htab->sgotplt == NULL
|
if (htab->sgotplt == NULL
|
|| ! bfd_set_section_flags (dynobj, htab->sgotplt, flags))
|
|| ! bfd_set_section_flags (dynobj, htab->sgotplt, flags))
|
return FALSE;
|
return FALSE;
|
|
|
/* Create ".rela.got". */
|
|
htab->srelgot = bfd_make_section_with_flags (dynobj, ".rela.got", flags);
|
|
if (htab->srelgot == NULL
|
|
|| ! bfd_set_section_alignment (dynobj, htab->srelgot, 2))
|
|
return FALSE;
|
|
|
|
/* Create ".got.loc" (literal tables for use by dynamic linker). */
|
/* Create ".got.loc" (literal tables for use by dynamic linker). */
|
htab->sgotloc = bfd_make_section_with_flags (dynobj, ".got.loc", flags);
|
htab->sgotloc = bfd_make_section_with_flags (dynobj, ".got.loc", flags);
|
if (htab->sgotloc == NULL
|
if (htab->sgotloc == NULL
|
|| ! bfd_set_section_alignment (dynobj, htab->sgotloc, 2))
|
|| ! bfd_set_section_alignment (dynobj, htab->sgotloc, 2))
|
return FALSE;
|
return FALSE;
|
Line 1197... |
Line 1511... |
static bfd_boolean
|
static bfd_boolean
|
elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg)
|
elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg)
|
{
|
{
|
struct bfd_link_info *info;
|
struct bfd_link_info *info;
|
struct elf_xtensa_link_hash_table *htab;
|
struct elf_xtensa_link_hash_table *htab;
|
bfd_boolean is_dynamic;
|
struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (h);
|
|
|
if (h->root.type == bfd_link_hash_indirect)
|
if (h->root.type == bfd_link_hash_indirect)
|
return TRUE;
|
return TRUE;
|
|
|
if (h->root.type == bfd_link_hash_warning)
|
if (h->root.type == bfd_link_hash_warning)
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
h = (struct elf_link_hash_entry *) h->root.u.i.link;
|
|
|
info = (struct bfd_link_info *) arg;
|
info = (struct bfd_link_info *) arg;
|
htab = elf_xtensa_hash_table (info);
|
htab = elf_xtensa_hash_table (info);
|
|
|
is_dynamic = elf_xtensa_dynamic_symbol_p (h, info);
|
/* If we saw any use of an IE model for this symbol, we can then optimize
|
|
away GOT entries for any TLSDESC_FN relocs. */
|
|
if ((eh->tls_type & GOT_TLS_IE) != 0)
|
|
{
|
|
BFD_ASSERT (h->got.refcount >= eh->tlsfunc_refcount);
|
|
h->got.refcount -= eh->tlsfunc_refcount;
|
|
}
|
|
|
if (! is_dynamic)
|
if (! elf_xtensa_dynamic_symbol_p (h, info))
|
elf_xtensa_make_sym_local (info, h);
|
elf_xtensa_make_sym_local (info, h);
|
|
|
if (h->plt.refcount > 0)
|
if (h->plt.refcount > 0)
|
htab->srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
|
htab->srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
|
|
|
Line 1246... |
Line 1566... |
symtab_hdr = &elf_tdata (i)->symtab_hdr;
|
symtab_hdr = &elf_tdata (i)->symtab_hdr;
|
cnt = symtab_hdr->sh_info;
|
cnt = symtab_hdr->sh_info;
|
|
|
for (j = 0; j < cnt; ++j)
|
for (j = 0; j < cnt; ++j)
|
{
|
{
|
|
/* If we saw any use of an IE model for this symbol, we can
|
|
then optimize away GOT entries for any TLSDESC_FN relocs. */
|
|
if ((elf_xtensa_local_got_tls_type (i) [j] & GOT_TLS_IE) != 0)
|
|
{
|
|
bfd_signed_vma *tlsfunc_refcount
|
|
= &elf_xtensa_local_tlsfunc_refcounts (i) [j];
|
|
BFD_ASSERT (local_got_refcounts[j] >= *tlsfunc_refcount);
|
|
local_got_refcounts[j] -= *tlsfunc_refcount;
|
|
}
|
|
|
if (local_got_refcounts[j] > 0)
|
if (local_got_refcounts[j] > 0)
|
htab->srelgot->size += (local_got_refcounts[j]
|
htab->srelgot->size += (local_got_refcounts[j]
|
* sizeof (Elf32_External_Rela));
|
* sizeof (Elf32_External_Rela));
|
}
|
}
|
}
|
}
|
Line 1495... |
Line 1825... |
#undef add_dynamic_entry
|
#undef add_dynamic_entry
|
|
|
return TRUE;
|
return TRUE;
|
}
|
}
|
|
|
|
static bfd_boolean
|
|
elf_xtensa_always_size_sections (bfd *output_bfd,
|
|
struct bfd_link_info *info)
|
|
{
|
|
struct elf_xtensa_link_hash_table *htab;
|
|
asection *tls_sec;
|
|
|
|
htab = elf_xtensa_hash_table (info);
|
|
tls_sec = htab->elf.tls_sec;
|
|
|
|
if (tls_sec && (htab->tlsbase->tls_type & GOT_TLS_ANY) != 0)
|
|
{
|
|
struct elf_link_hash_entry *tlsbase = &htab->tlsbase->elf;
|
|
struct bfd_link_hash_entry *bh = &tlsbase->root;
|
|
const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
|
|
|
|
tlsbase->type = STT_TLS;
|
|
if (!(_bfd_generic_link_add_one_symbol
|
|
(info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
|
|
tls_sec, 0, NULL, FALSE,
|
|
bed->collect, &bh)))
|
|
return FALSE;
|
|
tlsbase->def_regular = 1;
|
|
tlsbase->other = STV_HIDDEN;
|
|
(*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Return the base VMA address which should be subtracted from real addresses
|
|
when resolving @dtpoff relocation.
|
|
This is PT_TLS segment p_vaddr. */
|
|
|
|
static bfd_vma
|
|
dtpoff_base (struct bfd_link_info *info)
|
|
{
|
|
/* If tls_sec is NULL, we should have signalled an error already. */
|
|
if (elf_hash_table (info)->tls_sec == NULL)
|
|
return 0;
|
|
return elf_hash_table (info)->tls_sec->vma;
|
|
}
|
|
|
|
/* Return the relocation value for @tpoff relocation
|
|
if STT_TLS virtual address is ADDRESS. */
|
|
|
|
static bfd_vma
|
|
tpoff (struct bfd_link_info *info, bfd_vma address)
|
|
{
|
|
struct elf_link_hash_table *htab = elf_hash_table (info);
|
|
bfd_vma base;
|
|
|
|
/* If tls_sec is NULL, we should have signalled an error already. */
|
|
if (htab->tls_sec == NULL)
|
|
return 0;
|
|
base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power);
|
|
return address - htab->tls_sec->vma + base;
|
|
}
|
|
|
/* Perform the specified relocation. The instruction at (contents + address)
|
/* Perform the specified relocation. The instruction at (contents + address)
|
is modified to set one operand to represent the value in "relocation". The
|
is modified to set one operand to represent the value in "relocation". The
|
operand position is determined by the relocation type recorded in the
|
operand position is determined by the relocation type recorded in the
|
howto. */
|
howto. */
|
|
|
Line 1543... |
Line 1932... |
{
|
{
|
case R_XTENSA_NONE:
|
case R_XTENSA_NONE:
|
case R_XTENSA_DIFF8:
|
case R_XTENSA_DIFF8:
|
case R_XTENSA_DIFF16:
|
case R_XTENSA_DIFF16:
|
case R_XTENSA_DIFF32:
|
case R_XTENSA_DIFF32:
|
|
case R_XTENSA_TLS_FUNC:
|
|
case R_XTENSA_TLS_ARG:
|
|
case R_XTENSA_TLS_CALL:
|
return bfd_reloc_ok;
|
return bfd_reloc_ok;
|
|
|
case R_XTENSA_ASM_EXPAND:
|
case R_XTENSA_ASM_EXPAND:
|
if (!is_weak_undef)
|
if (!is_weak_undef)
|
{
|
{
|
Line 1582... |
Line 1974... |
howto = &elf_howto_table[(unsigned) R_XTENSA_SLOT0_OP ];
|
howto = &elf_howto_table[(unsigned) R_XTENSA_SLOT0_OP ];
|
}
|
}
|
break;
|
break;
|
|
|
case R_XTENSA_32:
|
case R_XTENSA_32:
|
case R_XTENSA_PLT:
|
|
{
|
{
|
bfd_vma x;
|
bfd_vma x;
|
x = bfd_get_32 (abfd, contents + address);
|
x = bfd_get_32 (abfd, contents + address);
|
x = x + relocation;
|
x = x + relocation;
|
bfd_put_32 (abfd, x, contents + address);
|
bfd_put_32 (abfd, x, contents + address);
|
Line 1594... |
Line 1985... |
return bfd_reloc_ok;
|
return bfd_reloc_ok;
|
|
|
case R_XTENSA_32_PCREL:
|
case R_XTENSA_32_PCREL:
|
bfd_put_32 (abfd, relocation - self_address, contents + address);
|
bfd_put_32 (abfd, relocation - self_address, contents + address);
|
return bfd_reloc_ok;
|
return bfd_reloc_ok;
|
|
|
|
case R_XTENSA_PLT:
|
|
case R_XTENSA_TLSDESC_FN:
|
|
case R_XTENSA_TLSDESC_ARG:
|
|
case R_XTENSA_TLS_DTPOFF:
|
|
case R_XTENSA_TLS_TPOFF:
|
|
bfd_put_32 (abfd, relocation, contents + address);
|
|
return bfd_reloc_ok;
|
}
|
}
|
|
|
/* Only instruction slot-specific relocations handled below.... */
|
/* Only instruction slot-specific relocations handled below.... */
|
slot = get_relocation_slot (howto->type);
|
slot = get_relocation_slot (howto->type);
|
if (slot == XTENSA_UNDEFINED)
|
if (slot == XTENSA_UNDEFINED)
|
Line 1880... |
Line 2279... |
*error_message = vsprint_msg (*error_message, ": (%s + 0x%lx)",
|
*error_message = vsprint_msg (*error_message, ": (%s + 0x%lx)",
|
strlen (symbol->name) + 17,
|
strlen (symbol->name) + 17,
|
symbol->name,
|
symbol->name,
|
(unsigned long) reloc_entry->addend);
|
(unsigned long) reloc_entry->addend);
|
}
|
}
|
|
|
return flag;
|
return flag;
|
|
}
|
|
|
|
|
|
/* Set up an entry in the procedure linkage table. */
|
|
|
|
static bfd_vma
|
|
elf_xtensa_create_plt_entry (struct bfd_link_info *info,
|
|
bfd *output_bfd,
|
|
unsigned reloc_index)
|
|
{
|
|
asection *splt, *sgotplt;
|
|
bfd_vma plt_base, got_base;
|
|
bfd_vma code_offset, lit_offset;
|
|
int chunk;
|
|
|
|
chunk = reloc_index / PLT_ENTRIES_PER_CHUNK;
|
|
splt = elf_xtensa_get_plt_section (info, chunk);
|
|
sgotplt = elf_xtensa_get_gotplt_section (info, chunk);
|
|
BFD_ASSERT (splt != NULL && sgotplt != NULL);
|
|
|
|
plt_base = splt->output_section->vma + splt->output_offset;
|
|
got_base = sgotplt->output_section->vma + sgotplt->output_offset;
|
|
|
|
lit_offset = 8 + (reloc_index % PLT_ENTRIES_PER_CHUNK) * 4;
|
|
code_offset = (reloc_index % PLT_ENTRIES_PER_CHUNK) * PLT_ENTRY_SIZE;
|
|
|
|
/* Fill in the literal entry. This is the offset of the dynamic
|
|
relocation entry. */
|
|
bfd_put_32 (output_bfd, reloc_index * sizeof (Elf32_External_Rela),
|
|
sgotplt->contents + lit_offset);
|
|
|
|
/* Fill in the entry in the procedure linkage table. */
|
|
memcpy (splt->contents + code_offset,
|
|
(bfd_big_endian (output_bfd)
|
|
? elf_xtensa_be_plt_entry
|
|
: elf_xtensa_le_plt_entry),
|
|
PLT_ENTRY_SIZE);
|
|
bfd_put_16 (output_bfd, l32r_offset (got_base + 0,
|
|
plt_base + code_offset + 3),
|
|
splt->contents + code_offset + 4);
|
|
bfd_put_16 (output_bfd, l32r_offset (got_base + 4,
|
|
plt_base + code_offset + 6),
|
|
splt->contents + code_offset + 7);
|
|
bfd_put_16 (output_bfd, l32r_offset (got_base + lit_offset,
|
|
plt_base + code_offset + 9),
|
|
splt->contents + code_offset + 10);
|
|
|
|
return plt_base + code_offset;
|
|
}
|
|
|
|
|
|
static bfd_boolean get_indirect_call_dest_reg (xtensa_opcode, unsigned *);
|
|
|
|
static bfd_boolean
|
|
replace_tls_insn (Elf_Internal_Rela *rel,
|
|
bfd *abfd,
|
|
asection *input_section,
|
|
bfd_byte *contents,
|
|
bfd_boolean is_ld_model,
|
|
char **error_message)
|
|
{
|
|
static xtensa_insnbuf ibuff = NULL;
|
|
static xtensa_insnbuf sbuff = NULL;
|
|
xtensa_isa isa = xtensa_default_isa;
|
|
xtensa_format fmt;
|
|
xtensa_opcode old_op, new_op;
|
|
bfd_size_type input_size;
|
|
int r_type;
|
|
unsigned dest_reg, src_reg;
|
|
|
|
if (ibuff == NULL)
|
|
{
|
|
ibuff = xtensa_insnbuf_alloc (isa);
|
|
sbuff = xtensa_insnbuf_alloc (isa);
|
|
}
|
|
|
|
input_size = bfd_get_section_limit (abfd, input_section);
|
|
|
|
/* Read the instruction into a buffer and decode the opcode. */
|
|
xtensa_insnbuf_from_chars (isa, ibuff, contents + rel->r_offset,
|
|
input_size - rel->r_offset);
|
|
fmt = xtensa_format_decode (isa, ibuff);
|
|
if (fmt == XTENSA_UNDEFINED)
|
|
{
|
|
*error_message = "cannot decode instruction format";
|
|
return FALSE;
|
|
}
|
|
|
|
BFD_ASSERT (xtensa_format_num_slots (isa, fmt) == 1);
|
|
xtensa_format_get_slot (isa, fmt, 0, ibuff, sbuff);
|
|
|
|
old_op = xtensa_opcode_decode (isa, fmt, 0, sbuff);
|
|
if (old_op == XTENSA_UNDEFINED)
|
|
{
|
|
*error_message = "cannot decode instruction opcode";
|
|
return FALSE;
|
|
}
|
|
|
|
r_type = ELF32_R_TYPE (rel->r_info);
|
|
switch (r_type)
|
|
{
|
|
case R_XTENSA_TLS_FUNC:
|
|
case R_XTENSA_TLS_ARG:
|
|
if (old_op != get_l32r_opcode ()
|
|
|| xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
|
|
sbuff, &dest_reg) != 0)
|
|
{
|
|
*error_message = "cannot extract L32R destination for TLS access";
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case R_XTENSA_TLS_CALL:
|
|
if (! get_indirect_call_dest_reg (old_op, &dest_reg)
|
|
|| xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
|
|
sbuff, &src_reg) != 0)
|
|
{
|
|
*error_message = "cannot extract CALLXn operands for TLS access";
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
abort ();
|
|
}
|
|
|
|
if (is_ld_model)
|
|
{
|
|
switch (r_type)
|
|
{
|
|
case R_XTENSA_TLS_FUNC:
|
|
case R_XTENSA_TLS_ARG:
|
|
/* Change the instruction to a NOP (or "OR a1, a1, a1" for older
|
|
versions of Xtensa). */
|
|
new_op = xtensa_opcode_lookup (isa, "nop");
|
|
if (new_op == XTENSA_UNDEFINED)
|
|
{
|
|
new_op = xtensa_opcode_lookup (isa, "or");
|
|
if (new_op == XTENSA_UNDEFINED
|
|
|| xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
|
|
sbuff, 1) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
|
|
sbuff, 1) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
|
|
sbuff, 1) != 0)
|
|
{
|
|
*error_message = "cannot encode OR for TLS access";
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0)
|
|
{
|
|
*error_message = "cannot encode NOP for TLS access";
|
|
return FALSE;
|
|
}
|
}
|
}
|
|
break;
|
|
|
|
case R_XTENSA_TLS_CALL:
|
/* Set up an entry in the procedure linkage table. */
|
/* Read THREADPTR into the CALLX's return value register. */
|
|
new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
|
static bfd_vma
|
if (new_op == XTENSA_UNDEFINED
|
elf_xtensa_create_plt_entry (struct bfd_link_info *info,
|
|| xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
|
bfd *output_bfd,
|
|| xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
|
unsigned reloc_index)
|
sbuff, dest_reg + 2) != 0)
|
{
|
{
|
asection *splt, *sgotplt;
|
*error_message = "cannot encode RUR.THREADPTR for TLS access";
|
bfd_vma plt_base, got_base;
|
return FALSE;
|
bfd_vma code_offset, lit_offset;
|
}
|
int chunk;
|
break;
|
|
}
|
chunk = reloc_index / PLT_ENTRIES_PER_CHUNK;
|
}
|
splt = elf_xtensa_get_plt_section (info, chunk);
|
else
|
sgotplt = elf_xtensa_get_gotplt_section (info, chunk);
|
{
|
BFD_ASSERT (splt != NULL && sgotplt != NULL);
|
switch (r_type)
|
|
{
|
plt_base = splt->output_section->vma + splt->output_offset;
|
case R_XTENSA_TLS_FUNC:
|
got_base = sgotplt->output_section->vma + sgotplt->output_offset;
|
new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
|
|
if (new_op == XTENSA_UNDEFINED
|
|
|| xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
|
|
sbuff, dest_reg) != 0)
|
|
{
|
|
*error_message = "cannot encode RUR.THREADPTR for TLS access";
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
lit_offset = 8 + (reloc_index % PLT_ENTRIES_PER_CHUNK) * 4;
|
case R_XTENSA_TLS_ARG:
|
code_offset = (reloc_index % PLT_ENTRIES_PER_CHUNK) * PLT_ENTRY_SIZE;
|
/* Nothing to do. Keep the original L32R instruction. */
|
|
return TRUE;
|
|
|
/* Fill in the literal entry. This is the offset of the dynamic
|
case R_XTENSA_TLS_CALL:
|
relocation entry. */
|
/* Add the CALLX's src register (holding the THREADPTR value)
|
bfd_put_32 (output_bfd, reloc_index * sizeof (Elf32_External_Rela),
|
to the first argument register (holding the offset) and put
|
sgotplt->contents + lit_offset);
|
the result in the CALLX's return value register. */
|
|
new_op = xtensa_opcode_lookup (isa, "add");
|
|
if (new_op == XTENSA_UNDEFINED
|
|
|| xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
|
|
sbuff, dest_reg + 2) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
|
|
sbuff, dest_reg + 2) != 0
|
|
|| xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
|
|
sbuff, src_reg) != 0)
|
|
{
|
|
*error_message = "cannot encode ADD for TLS access";
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
/* Fill in the entry in the procedure linkage table. */
|
xtensa_format_set_slot (isa, fmt, 0, ibuff, sbuff);
|
memcpy (splt->contents + code_offset,
|
xtensa_insnbuf_to_chars (isa, ibuff, contents + rel->r_offset,
|
(bfd_big_endian (output_bfd)
|
input_size - rel->r_offset);
|
? elf_xtensa_be_plt_entry
|
|
: elf_xtensa_le_plt_entry),
|
|
PLT_ENTRY_SIZE);
|
|
bfd_put_16 (output_bfd, l32r_offset (got_base + 0,
|
|
plt_base + code_offset + 3),
|
|
splt->contents + code_offset + 4);
|
|
bfd_put_16 (output_bfd, l32r_offset (got_base + 4,
|
|
plt_base + code_offset + 6),
|
|
splt->contents + code_offset + 7);
|
|
bfd_put_16 (output_bfd, l32r_offset (got_base + lit_offset,
|
|
plt_base + code_offset + 9),
|
|
splt->contents + code_offset + 10);
|
|
|
|
return plt_base + code_offset;
|
return TRUE;
|
}
|
}
|
|
|
|
|
|
#define IS_XTENSA_TLS_RELOC(R_TYPE) \
|
|
((R_TYPE) == R_XTENSA_TLSDESC_FN \
|
|
|| (R_TYPE) == R_XTENSA_TLSDESC_ARG \
|
|
|| (R_TYPE) == R_XTENSA_TLS_DTPOFF \
|
|
|| (R_TYPE) == R_XTENSA_TLS_TPOFF \
|
|
|| (R_TYPE) == R_XTENSA_TLS_FUNC \
|
|
|| (R_TYPE) == R_XTENSA_TLS_ARG \
|
|
|| (R_TYPE) == R_XTENSA_TLS_CALL)
|
|
|
/* Relocate an Xtensa ELF section. This is invoked by the linker for
|
/* Relocate an Xtensa ELF section. This is invoked by the linker for
|
both relocatable and final links. */
|
both relocatable and final links. */
|
|
|
static bfd_boolean
|
static bfd_boolean
|
elf_xtensa_relocate_section (bfd *output_bfd,
|
elf_xtensa_relocate_section (bfd *output_bfd,
|
Line 1953... |
Line 2534... |
Elf_Internal_Rela *rel;
|
Elf_Internal_Rela *rel;
|
Elf_Internal_Rela *relend;
|
Elf_Internal_Rela *relend;
|
struct elf_link_hash_entry **sym_hashes;
|
struct elf_link_hash_entry **sym_hashes;
|
property_table_entry *lit_table = 0;
|
property_table_entry *lit_table = 0;
|
int ltblsize = 0;
|
int ltblsize = 0;
|
|
char *local_got_tls_types;
|
char *error_message = NULL;
|
char *error_message = NULL;
|
bfd_size_type input_size;
|
bfd_size_type input_size;
|
|
int tls_type;
|
|
|
if (!xtensa_default_isa)
|
if (!xtensa_default_isa)
|
xtensa_default_isa = xtensa_isa_init (0, 0);
|
xtensa_default_isa = xtensa_isa_init (0, 0);
|
|
|
|
BFD_ASSERT (is_xtensa_elf (input_bfd));
|
|
|
htab = elf_xtensa_hash_table (info);
|
htab = elf_xtensa_hash_table (info);
|
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
|
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
|
sym_hashes = elf_sym_hashes (input_bfd);
|
sym_hashes = elf_sym_hashes (input_bfd);
|
|
local_got_tls_types = elf_xtensa_local_got_tls_type (input_bfd);
|
|
|
if (elf_hash_table (info)->dynamic_sections_created)
|
if (elf_hash_table (info)->dynamic_sections_created)
|
{
|
{
|
ltblsize = xtensa_read_table_entries (input_bfd, input_section,
|
ltblsize = xtensa_read_table_entries (input_bfd, input_section,
|
&lit_table, XTENSA_LIT_SEC_NAME,
|
&lit_table, XTENSA_LIT_SEC_NAME,
|
Line 1983... |
Line 2569... |
int r_type;
|
int r_type;
|
reloc_howto_type *howto;
|
reloc_howto_type *howto;
|
unsigned long r_symndx;
|
unsigned long r_symndx;
|
struct elf_link_hash_entry *h;
|
struct elf_link_hash_entry *h;
|
Elf_Internal_Sym *sym;
|
Elf_Internal_Sym *sym;
|
|
char sym_type;
|
|
const char *name;
|
asection *sec;
|
asection *sec;
|
bfd_vma relocation;
|
bfd_vma relocation;
|
bfd_reloc_status_type r;
|
bfd_reloc_status_type r;
|
bfd_boolean is_weak_undef;
|
bfd_boolean is_weak_undef;
|
bfd_boolean unresolved_reloc;
|
bfd_boolean unresolved_reloc;
|
bfd_boolean warned;
|
bfd_boolean warned;
|
|
bfd_boolean dynamic_symbol;
|
|
|
r_type = ELF32_R_TYPE (rel->r_info);
|
r_type = ELF32_R_TYPE (rel->r_info);
|
if (r_type == (int) R_XTENSA_GNU_VTINHERIT
|
if (r_type == (int) R_XTENSA_GNU_VTINHERIT
|
|| r_type == (int) R_XTENSA_GNU_VTENTRY)
|
|| r_type == (int) R_XTENSA_GNU_VTENTRY)
|
continue;
|
continue;
|
Line 2024... |
Line 2613... |
}
|
}
|
|
|
if (r_symndx < symtab_hdr->sh_info)
|
if (r_symndx < symtab_hdr->sh_info)
|
{
|
{
|
sym = local_syms + r_symndx;
|
sym = local_syms + r_symndx;
|
|
sym_type = ELF32_ST_TYPE (sym->st_info);
|
sec = local_sections[r_symndx];
|
sec = local_sections[r_symndx];
|
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
|
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
|
}
|
}
|
else
|
else
|
{
|
{
|
Line 2038... |
Line 2628... |
|
|
if (relocation == 0
|
if (relocation == 0
|
&& !unresolved_reloc
|
&& !unresolved_reloc
|
&& h->root.type == bfd_link_hash_undefweak)
|
&& h->root.type == bfd_link_hash_undefweak)
|
is_weak_undef = TRUE;
|
is_weak_undef = TRUE;
|
|
|
|
sym_type = h->type;
|
}
|
}
|
|
|
if (sec != NULL && elf_discarded_section (sec))
|
if (sec != NULL && elf_discarded_section (sec))
|
{
|
{
|
/* For relocs against symbols from removed linkonce sections,
|
/* For relocs against symbols from removed linkonce sections,
|
Line 2149... |
Line 2741... |
input_bfd, input_section, rel->r_offset, input_size);
|
input_bfd, input_section, rel->r_offset, input_size);
|
bfd_set_error (bfd_error_bad_value);
|
bfd_set_error (bfd_error_bad_value);
|
return FALSE;
|
return FALSE;
|
}
|
}
|
|
|
/* Generate dynamic relocations. */
|
if (h != NULL)
|
if (elf_hash_table (info)->dynamic_sections_created)
|
name = h->root.root.string;
|
|
else
|
{
|
{
|
bfd_boolean dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info);
|
name = (bfd_elf_string_from_elf_section
|
|
(input_bfd, symtab_hdr->sh_link, sym->st_name));
|
|
if (name == NULL || *name == '\0')
|
|
name = bfd_section_name (input_bfd, sec);
|
|
}
|
|
|
if (dynamic_symbol && (is_operand_relocation (r_type)
|
if (r_symndx != 0
|
|| r_type == R_XTENSA_32_PCREL))
|
&& r_type != R_XTENSA_NONE
|
|
&& (h == NULL
|
|
|| h->root.type == bfd_link_hash_defined
|
|
|| h->root.type == bfd_link_hash_defweak)
|
|
&& IS_XTENSA_TLS_RELOC (r_type) != (sym_type == STT_TLS))
|
{
|
{
|
const char *name = h->root.root.string;
|
(*_bfd_error_handler)
|
error_message =
|
((sym_type == STT_TLS
|
vsprint_msg ("invalid relocation for dynamic symbol", ": %s",
|
? _("%B(%A+0x%lx): %s used with TLS symbol %s")
|
strlen (name) + 2, name);
|
: _("%B(%A+0x%lx): %s used with non-TLS symbol %s")),
|
if (!((*info->callbacks->reloc_dangerous)
|
input_bfd,
|
(info, error_message, input_bfd, input_section,
|
input_section,
|
rel->r_offset)))
|
(long) rel->r_offset,
|
return FALSE;
|
howto->name,
|
continue;
|
name);
|
}
|
}
|
else if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT)
|
|
|
dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info);
|
|
|
|
tls_type = GOT_UNKNOWN;
|
|
if (h)
|
|
tls_type = elf_xtensa_hash_entry (h)->tls_type;
|
|
else if (local_got_tls_types)
|
|
tls_type = local_got_tls_types [r_symndx];
|
|
|
|
switch (r_type)
|
|
{
|
|
case R_XTENSA_32:
|
|
case R_XTENSA_PLT:
|
|
if (elf_hash_table (info)->dynamic_sections_created
|
&& (input_section->flags & SEC_ALLOC) != 0
|
&& (input_section->flags & SEC_ALLOC) != 0
|
&& (dynamic_symbol || info->shared))
|
&& (dynamic_symbol || info->shared))
|
{
|
{
|
Elf_Internal_Rela outrel;
|
Elf_Internal_Rela outrel;
|
bfd_byte *loc;
|
bfd_byte *loc;
|
Line 2253... |
Line 2867... |
/* This should only happen for non-PIC code, which is not
|
/* This should only happen for non-PIC code, which is not
|
supposed to be used on systems with dynamic linking.
|
supposed to be used on systems with dynamic linking.
|
Just ignore these relocations. */
|
Just ignore these relocations. */
|
continue;
|
continue;
|
}
|
}
|
|
break;
|
|
|
|
case R_XTENSA_TLS_TPOFF:
|
|
/* Switch to LE model for local symbols in an executable. */
|
|
if (! info->shared && ! dynamic_symbol)
|
|
{
|
|
relocation = tpoff (info, relocation);
|
|
break;
|
|
}
|
|
/* fall through */
|
|
|
|
case R_XTENSA_TLSDESC_FN:
|
|
case R_XTENSA_TLSDESC_ARG:
|
|
{
|
|
if (r_type == R_XTENSA_TLSDESC_FN)
|
|
{
|
|
if (! info->shared || (tls_type & GOT_TLS_IE) != 0)
|
|
r_type = R_XTENSA_NONE;
|
|
}
|
|
else if (r_type == R_XTENSA_TLSDESC_ARG)
|
|
{
|
|
if (info->shared)
|
|
{
|
|
if ((tls_type & GOT_TLS_IE) != 0)
|
|
r_type = R_XTENSA_TLS_TPOFF;
|
|
}
|
|
else
|
|
{
|
|
r_type = R_XTENSA_TLS_TPOFF;
|
|
if (! dynamic_symbol)
|
|
{
|
|
relocation = tpoff (info, relocation);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r_type == R_XTENSA_NONE)
|
|
/* Nothing to do here; skip to the next reloc. */
|
|
continue;
|
|
|
|
if (! elf_hash_table (info)->dynamic_sections_created)
|
|
{
|
|
error_message =
|
|
_("TLS relocation invalid without dynamic sections");
|
|
if (!((*info->callbacks->reloc_dangerous)
|
|
(info, error_message, input_bfd, input_section,
|
|
rel->r_offset)))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
Elf_Internal_Rela outrel;
|
|
bfd_byte *loc;
|
|
asection *srel = htab->srelgot;
|
|
int indx;
|
|
|
|
outrel.r_offset = (input_section->output_section->vma
|
|
+ input_section->output_offset
|
|
+ rel->r_offset);
|
|
|
|
/* Complain if the relocation is in a read-only section
|
|
and not in a literal pool. */
|
|
if ((input_section->flags & SEC_READONLY) != 0
|
|
&& ! elf_xtensa_in_literal_pool (lit_table, ltblsize,
|
|
outrel.r_offset))
|
|
{
|
|
error_message =
|
|
_("dynamic relocation in read-only section");
|
|
if (!((*info->callbacks->reloc_dangerous)
|
|
(info, error_message, input_bfd, input_section,
|
|
rel->r_offset)))
|
|
return FALSE;
|
|
}
|
|
|
|
indx = h && h->dynindx != -1 ? h->dynindx : 0;
|
|
if (indx == 0)
|
|
outrel.r_addend = relocation - dtpoff_base (info);
|
|
else
|
|
outrel.r_addend = 0;
|
|
rel->r_addend = 0;
|
|
|
|
outrel.r_info = ELF32_R_INFO (indx, r_type);
|
|
relocation = 0;
|
|
unresolved_reloc = FALSE;
|
|
|
|
BFD_ASSERT (srel);
|
|
loc = (srel->contents
|
|
+ srel->reloc_count++ * sizeof (Elf32_External_Rela));
|
|
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
|
|
BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count
|
|
<= srel->size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case R_XTENSA_TLS_DTPOFF:
|
|
if (! info->shared)
|
|
/* Switch from LD model to LE model. */
|
|
relocation = tpoff (info, relocation);
|
|
else
|
|
relocation -= dtpoff_base (info);
|
|
break;
|
|
|
|
case R_XTENSA_TLS_FUNC:
|
|
case R_XTENSA_TLS_ARG:
|
|
case R_XTENSA_TLS_CALL:
|
|
/* Check if optimizing to IE or LE model. */
|
|
if ((tls_type & GOT_TLS_IE) != 0)
|
|
{
|
|
bfd_boolean is_ld_model =
|
|
(h && elf_xtensa_hash_entry (h) == htab->tlsbase);
|
|
if (! replace_tls_insn (rel, input_bfd, input_section, contents,
|
|
is_ld_model, &error_message))
|
|
{
|
|
if (!((*info->callbacks->reloc_dangerous)
|
|
(info, error_message, input_bfd, input_section,
|
|
rel->r_offset)))
|
|
return FALSE;
|
|
}
|
|
|
|
if (r_type != R_XTENSA_TLS_ARG || is_ld_model)
|
|
{
|
|
/* Skip subsequent relocations on the same instruction. */
|
|
while (rel + 1 < relend && rel[1].r_offset == rel->r_offset)
|
|
rel++;
|
|
}
|
|
}
|
|
continue;
|
|
|
|
default:
|
|
if (elf_hash_table (info)->dynamic_sections_created
|
|
&& dynamic_symbol && (is_operand_relocation (r_type)
|
|
|| r_type == R_XTENSA_32_PCREL))
|
|
{
|
|
error_message =
|
|
vsprint_msg ("invalid relocation for dynamic symbol", ": %s",
|
|
strlen (name) + 2, name);
|
|
if (!((*info->callbacks->reloc_dangerous)
|
|
(info, error_message, input_bfd, input_section,
|
|
rel->r_offset)))
|
|
return FALSE;
|
|
continue;
|
|
}
|
|
break;
|
}
|
}
|
|
|
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
|
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
|
because such sections are not SEC_ALLOC and thus ld.so will
|
because such sections are not SEC_ALLOC and thus ld.so will
|
not process them. */
|
not process them. */
|
Line 2268... |
Line 3027... |
(_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
|
(_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
|
input_bfd,
|
input_bfd,
|
input_section,
|
input_section,
|
(long) rel->r_offset,
|
(long) rel->r_offset,
|
howto->name,
|
howto->name,
|
h->root.root.string);
|
name);
|
return FALSE;
|
return FALSE;
|
}
|
}
|
|
|
|
/* TLS optimizations may have changed r_type; update "howto". */
|
|
howto = &elf_howto_table[r_type];
|
|
|
/* There's no point in calling bfd_perform_relocation here.
|
/* There's no point in calling bfd_perform_relocation here.
|
Just go directly to our "special function". */
|
Just go directly to our "special function". */
|
r = elf_xtensa_do_reloc (howto, input_bfd, input_section,
|
r = elf_xtensa_do_reloc (howto, input_bfd, input_section,
|
relocation + rel->r_addend,
|
relocation + rel->r_addend,
|
contents, rel->r_offset, is_weak_undef,
|
contents, rel->r_offset, is_weak_undef,
|
&error_message);
|
&error_message);
|
|
|
if (r != bfd_reloc_ok && !warned)
|
if (r != bfd_reloc_ok && !warned)
|
{
|
{
|
const char *name;
|
|
|
|
BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other);
|
BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other);
|
BFD_ASSERT (error_message != NULL);
|
BFD_ASSERT (error_message != NULL);
|
|
|
if (h)
|
|
name = h->root.root.string;
|
|
else
|
|
{
|
|
name = bfd_elf_string_from_elf_section
|
|
(input_bfd, symtab_hdr->sh_link, sym->st_name);
|
|
if (name && *name == '\0')
|
|
name = bfd_section_name (input_bfd, sec);
|
|
}
|
|
if (name)
|
|
{
|
|
if (rel->r_addend == 0)
|
if (rel->r_addend == 0)
|
error_message = vsprint_msg (error_message, ": %s",
|
error_message = vsprint_msg (error_message, ": %s",
|
strlen (name) + 2, name);
|
strlen (name) + 2, name);
|
else
|
else
|
error_message = vsprint_msg (error_message, ": (%s+0x%x)",
|
error_message = vsprint_msg (error_message, ": (%s+0x%x)",
|
strlen (name) + 22,
|
strlen (name) + 22,
|
name, (int)rel->r_addend);
|
name, (int)rel->r_addend);
|
}
|
|
|
|
if (!((*info->callbacks->reloc_dangerous)
|
if (!((*info->callbacks->reloc_dangerous)
|
(info, error_message, input_bfd, input_section,
|
(info, error_message, input_bfd, input_section,
|
rel->r_offset)))
|
rel->r_offset)))
|
return FALSE;
|
return FALSE;
|
Line 3108... |
Line 3856... |
|| opcode == callx8_op
|
|| opcode == callx8_op
|
|| opcode == callx12_op);
|
|| opcode == callx12_op);
|
}
|
}
|
|
|
|
|
|
static bfd_boolean
|
|
get_indirect_call_dest_reg (xtensa_opcode opcode, unsigned *pdst)
|
|
{
|
|
unsigned dst = (unsigned) -1;
|
|
|
|
init_call_opcodes ();
|
|
if (opcode == callx0_op)
|
|
dst = 0;
|
|
else if (opcode == callx4_op)
|
|
dst = 4;
|
|
else if (opcode == callx8_op)
|
|
dst = 8;
|
|
else if (opcode == callx12_op)
|
|
dst = 12;
|
|
|
|
if (dst == (unsigned) -1)
|
|
return FALSE;
|
|
|
|
*pdst = dst;
|
|
return TRUE;
|
|
}
|
|
|
|
|
static xtensa_opcode
|
static xtensa_opcode
|
get_const16_opcode (void)
|
get_const16_opcode (void)
|
{
|
{
|
static bfd_boolean done_lookup = FALSE;
|
static bfd_boolean done_lookup = FALSE;
|
static xtensa_opcode const16_opcode = XTENSA_UNDEFINED;
|
static xtensa_opcode const16_opcode = XTENSA_UNDEFINED;
|
Line 4701... |
Line 5472... |
return;
|
return;
|
|
|
for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next)
|
for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next)
|
{
|
{
|
text_action *t = *m_p;
|
text_action *t = *m_p;
|
|
|
|
if (action == ta_fill)
|
|
{
|
/* When the action is another fill at the same address,
|
/* When the action is another fill at the same address,
|
just increase the size. */
|
just increase the size. */
|
if (t->offset == offset && t->action == ta_fill && action == ta_fill)
|
if (t->offset == offset && t->action == ta_fill)
|
{
|
{
|
t->removed_bytes += removed;
|
t->removed_bytes += removed;
|
return;
|
return;
|
}
|
}
|
|
/* Fills need to happen before widens so that we don't
|
|
insert fill bytes into the instruction stream. */
|
|
if (t->offset == offset && t->action == ta_widen_insn)
|
|
break;
|
|
}
|
}
|
}
|
|
|
/* Create a new record and fill it up. */
|
/* Create a new record and fill it up. */
|
ta = (text_action *) bfd_zmalloc (sizeof (text_action));
|
ta = (text_action *) bfd_zmalloc (sizeof (text_action));
|
ta->action = action;
|
ta->action = action;
|
Line 9230... |
Line 10009... |
}
|
}
|
|
|
if (remove_this_rel)
|
if (remove_this_rel)
|
{
|
{
|
offset_rel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
|
offset_rel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
|
/* In case this is the last entry, move the relocation offset
|
|
to the previous entry, if there is one. */
|
|
if (offset_rel->r_offset >= bytes_to_remove)
|
|
offset_rel->r_offset -= bytes_to_remove;
|
|
else
|
|
offset_rel->r_offset = 0;
|
offset_rel->r_offset = 0;
|
}
|
}
|
|
|
if (bytes_to_remove != 0)
|
if (bytes_to_remove != 0)
|
{
|
{
|
Line 9501... |
Line 10275... |
isymbuf = retrieve_local_syms (abfd);
|
isymbuf = retrieve_local_syms (abfd);
|
section_index = isymbuf[r_symndx].st_shndx;
|
section_index = isymbuf[r_symndx].st_shndx;
|
|
|
if (section_index == SHN_UNDEF)
|
if (section_index == SHN_UNDEF)
|
target_sec = bfd_und_section_ptr;
|
target_sec = bfd_und_section_ptr;
|
else if (section_index > 0 && section_index < SHN_LORESERVE)
|
|
target_sec = bfd_section_from_elf_index (abfd, section_index);
|
|
else if (section_index == SHN_ABS)
|
else if (section_index == SHN_ABS)
|
target_sec = bfd_abs_section_ptr;
|
target_sec = bfd_abs_section_ptr;
|
else if (section_index == SHN_COMMON)
|
else if (section_index == SHN_COMMON)
|
target_sec = bfd_com_section_ptr;
|
target_sec = bfd_com_section_ptr;
|
else
|
else
|
/* Who knows? */
|
target_sec = bfd_section_from_elf_index (abfd, section_index);
|
target_sec = NULL;
|
|
}
|
}
|
else
|
else
|
{
|
{
|
unsigned long indx = r_symndx - symtab_hdr->sh_info;
|
unsigned long indx = r_symndx - symtab_hdr->sh_info;
|
struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx];
|
struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx];
|
Line 9717... |
Line 10488... |
}
|
}
|
|
|
|
|
static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
|
static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
|
|
|
asection *
|
static char *
|
xtensa_get_property_section (asection *sec, const char *base_name)
|
xtensa_property_section_name (asection *sec, const char *base_name)
|
{
|
{
|
const char *suffix, *group_name;
|
const char *suffix, *group_name;
|
char *prop_sec_name;
|
char *prop_sec_name;
|
asection *prop_sec;
|
|
|
|
group_name = elf_group_name (sec);
|
group_name = elf_group_name (sec);
|
if (group_name)
|
if (group_name)
|
{
|
{
|
suffix = strrchr (sec->name, '.');
|
suffix = strrchr (sec->name, '.');
|
Line 9764... |
Line 10534... |
strcat (prop_sec_name + linkonce_len, suffix);
|
strcat (prop_sec_name + linkonce_len, suffix);
|
}
|
}
|
else
|
else
|
prop_sec_name = strdup (base_name);
|
prop_sec_name = strdup (base_name);
|
|
|
|
return prop_sec_name;
|
|
}
|
|
|
|
|
|
static asection *
|
|
xtensa_get_property_section (asection *sec, const char *base_name)
|
|
{
|
|
char *prop_sec_name;
|
|
asection *prop_sec;
|
|
|
|
prop_sec_name = xtensa_property_section_name (sec, base_name);
|
|
prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name,
|
|
match_section_group,
|
|
(void *) elf_group_name (sec));
|
|
free (prop_sec_name);
|
|
return prop_sec;
|
|
}
|
|
|
|
|
|
asection *
|
|
xtensa_make_property_section (asection *sec, const char *base_name)
|
|
{
|
|
char *prop_sec_name;
|
|
asection *prop_sec;
|
|
|
/* Check if the section already exists. */
|
/* Check if the section already exists. */
|
|
prop_sec_name = xtensa_property_section_name (sec, base_name);
|
prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name,
|
prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name,
|
match_section_group,
|
match_section_group,
|
(void *) group_name);
|
(void *) elf_group_name (sec));
|
/* If not, create it. */
|
/* If not, create it. */
|
if (! prop_sec)
|
if (! prop_sec)
|
{
|
{
|
flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY);
|
flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY);
|
flags |= (bfd_get_section_flags (sec->owner, sec)
|
flags |= (bfd_get_section_flags (sec->owner, sec)
|
Line 9780... |
Line 10576... |
prop_sec = bfd_make_section_anyway_with_flags
|
prop_sec = bfd_make_section_anyway_with_flags
|
(sec->owner, strdup (prop_sec_name), flags);
|
(sec->owner, strdup (prop_sec_name), flags);
|
if (! prop_sec)
|
if (! prop_sec)
|
return 0;
|
return 0;
|
|
|
elf_group_name (prop_sec) = group_name;
|
elf_group_name (prop_sec) = elf_group_name (sec);
|
}
|
}
|
|
|
free (prop_sec_name);
|
free (prop_sec_name);
|
return prop_sec;
|
return prop_sec;
|
}
|
}
|
Line 9940... |
Line 10736... |
#define elf_backend_want_dynbss 0
|
#define elf_backend_want_dynbss 0
|
#define elf_backend_want_got_plt 1
|
#define elf_backend_want_got_plt 1
|
|
|
#define elf_info_to_howto elf_xtensa_info_to_howto_rela
|
#define elf_info_to_howto elf_xtensa_info_to_howto_rela
|
|
|
|
#define bfd_elf32_mkobject elf_xtensa_mkobject
|
|
|
#define bfd_elf32_bfd_merge_private_bfd_data elf_xtensa_merge_private_bfd_data
|
#define bfd_elf32_bfd_merge_private_bfd_data elf_xtensa_merge_private_bfd_data
|
#define bfd_elf32_new_section_hook elf_xtensa_new_section_hook
|
#define bfd_elf32_new_section_hook elf_xtensa_new_section_hook
|
#define bfd_elf32_bfd_print_private_bfd_data elf_xtensa_print_private_bfd_data
|
#define bfd_elf32_bfd_print_private_bfd_data elf_xtensa_print_private_bfd_data
|
#define bfd_elf32_bfd_relax_section elf_xtensa_relax_section
|
#define bfd_elf32_bfd_relax_section elf_xtensa_relax_section
|
#define bfd_elf32_bfd_reloc_type_lookup elf_xtensa_reloc_type_lookup
|
#define bfd_elf32_bfd_reloc_type_lookup elf_xtensa_reloc_type_lookup
|
Line 9967... |
Line 10765... |
#define elf_backend_hide_symbol elf_xtensa_hide_symbol
|
#define elf_backend_hide_symbol elf_xtensa_hide_symbol
|
#define elf_backend_object_p elf_xtensa_object_p
|
#define elf_backend_object_p elf_xtensa_object_p
|
#define elf_backend_reloc_type_class elf_xtensa_reloc_type_class
|
#define elf_backend_reloc_type_class elf_xtensa_reloc_type_class
|
#define elf_backend_relocate_section elf_xtensa_relocate_section
|
#define elf_backend_relocate_section elf_xtensa_relocate_section
|
#define elf_backend_size_dynamic_sections elf_xtensa_size_dynamic_sections
|
#define elf_backend_size_dynamic_sections elf_xtensa_size_dynamic_sections
|
|
#define elf_backend_always_size_sections elf_xtensa_always_size_sections
|
#define elf_backend_omit_section_dynsym \
|
#define elf_backend_omit_section_dynsym \
|
((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
|
((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
|
#define elf_backend_special_sections elf_xtensa_special_sections
|
#define elf_backend_special_sections elf_xtensa_special_sections
|
#define elf_backend_action_discarded elf_xtensa_action_discarded
|
#define elf_backend_action_discarded elf_xtensa_action_discarded
|
|
#define elf_backend_copy_indirect_symbol elf_xtensa_copy_indirect_symbol
|
|
|
#include "elf32-target.h"
|
#include "elf32-target.h"
|
|
|
No newline at end of file
|
No newline at end of file
|