/* MIPS-specific support for ELF
- Copyright (C) 1993-2018 Free Software Foundation, Inc.
+ Copyright (C) 1993-2020 Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
<ian@cygnus.com>.
#include "libbfd.h"
#include "libiberty.h"
#include "elf-bfd.h"
+#include "ecoff-bfd.h"
#include "elfxx-mips.h"
#include "elf/mips.h"
#include "elf-vxworks.h"
#define LA25_LUI(VAL) (0x3c190000 | (VAL)) /* lui t9,VAL */
#define LA25_J(VAL) (0x08000000 | (((VAL) >> 2) & 0x3ffffff)) /* j VAL */
+#define LA25_BC(VAL) (0xc8000000 | (((VAL) >> 2) & 0x3ffffff)) /* bc VAL */
#define LA25_ADDIU(VAL) (0x27390000 | (VAL)) /* addiu t9,t9,VAL */
#define LA25_LUI_MICROMIPS(VAL) \
(0x41b90000 | (VAL)) /* lui t9,VAL */
/* The greatest dynamic symbol table index corresponding to an external
symbol without a GOT entry. */
bfd_size_type max_non_got_dynindx;
+ /* If non-NULL, output BFD for .MIPS.xhash finalization. */
+ bfd *output_bfd;
+ /* If non-NULL, pointer to contents of .MIPS.xhash for filling in
+ real final dynindx. */
+ bfd_byte *mipsxhash;
};
/* We make up to two PLT entries if needed, one for standard MIPS code
being called returns a floating point value. */
asection *call_fp_stub;
+ /* If non-zero, location in .MIPS.xhash to write real final dynindx. */
+ bfd_vma mipsxhash_loc;
+
/* The highest GGA_* value that satisfies all references to this symbol. */
unsigned int global_got_area : 2;
/* True if we suppress checks for invalid branches between ISA modes. */
bfd_boolean ignore_branch_isa;
+ /* True if we are targetting R6 compact branches. */
+ bfd_boolean compact_branches;
+
/* True if we're generating code for VxWorks. */
bfd_boolean is_vxworks;
#define STUB_MOVE 0x03e07825 /* or t7,ra,zero */
#define STUB_LUI(VAL) (0x3c180000 + (VAL)) /* lui t8,VAL */
#define STUB_JALR 0x0320f809 /* jalr ra,t9 */
+#define STUB_JALRC 0xf8190000 /* jalrc ra,t9 */
#define STUB_ORI(VAL) (0x37180000 + (VAL)) /* ori t8,t8,VAL */
#define STUB_LI16U(VAL) (0x34180000 + (VAL)) /* ori t8,zero,VAL unsigned */
#define STUB_LI16S(abfd, VAL) \
0x2718fffe /* subu $24, $24, 2 */
};
+/* The format of the first PLT entry in an O32 executable using compact
+ jumps. */
+static const bfd_vma mipsr6_o32_exec_plt0_entry_compact[] =
+{
+ 0x3c1c0000, /* lui $28, %hi(&GOTPLT[0]) */
+ 0x8f990000, /* lw $25, %lo(&GOTPLT[0])($28) */
+ 0x279c0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */
+ 0x031cc023, /* subu $24, $24, $28 */
+ 0x03e07821, /* move $15, $31 # 32-bit move (addu) */
+ 0x0018c082, /* srl $24, $24, 2 */
+ 0x2718fffe, /* subu $24, $24, 2 */
+ 0xf8190000 /* jalrc $25 */
+};
+
/* The format of the first PLT entry in an N32 executable. Different
because gp ($28) is not available; we use t2 ($14) instead. */
static const bfd_vma mips_n32_exec_plt0_entry[] =
0x2718fffe /* subu $24, $24, 2 */
};
+/* The format of the first PLT entry in an N32 executable using compact
+ jumps. Different because gp ($28) is not available; we use t2 ($14)
+ instead. */
+static const bfd_vma mipsr6_n32_exec_plt0_entry_compact[] =
+{
+ 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */
+ 0x8dd90000, /* lw $25, %lo(&GOTPLT[0])($14) */
+ 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */
+ 0x030ec023, /* subu $24, $24, $14 */
+ 0x03e07821, /* move $15, $31 # 32-bit move (addu) */
+ 0x0018c082, /* srl $24, $24, 2 */
+ 0x2718fffe, /* subu $24, $24, 2 */
+ 0xf8190000 /* jalrc $25 */
+};
+
/* The format of the first PLT entry in an N64 executable. Different
from N32 because of the increased size of GOT entries. */
static const bfd_vma mips_n64_exec_plt0_entry[] =
0x2718fffe /* subu $24, $24, 2 */
};
+/* The format of the first PLT entry in an N64 executable using compact
+ jumps. Different from N32 because of the increased size of GOT
+ entries. */
+static const bfd_vma mipsr6_n64_exec_plt0_entry_compact[] =
+{
+ 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */
+ 0xddd90000, /* ld $25, %lo(&GOTPLT[0])($14) */
+ 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */
+ 0x030ec023, /* subu $24, $24, $14 */
+ 0x03e0782d, /* move $15, $31 # 64-bit move (daddu) */
+ 0x0018c0c2, /* srl $24, $24, 3 */
+ 0x2718fffe, /* subu $24, $24, 2 */
+ 0xf8190000 /* jalrc $25 */
+};
+
+
/* The format of the microMIPS first PLT entry in an O32 executable.
We rely on v0 ($2) rather than t8 ($24) to contain the address
of the GOTPLT entry handled, so this stub may only be used when
0x03200008 /* jr $25 */
};
-/* In the following PLT entry the JR and ADDIU instructions will
- be swapped in _bfd_mips_elf_finish_dynamic_symbol because
- LOAD_INTERLOCKS_P will be true for MIPS R6. */
static const bfd_vma mipsr6_exec_plt_entry[] =
{
0x3c0f0000, /* lui $15, %hi(.got.plt entry) */
0x03200009 /* jr $25 */
};
+static const bfd_vma mipsr6_exec_plt_entry_compact[] =
+{
+ 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */
+ 0x01f90000, /* l[wd] $25, %lo(.got.plt entry)($15) */
+ 0x25f80000, /* addiu $24, $15, %lo(.got.plt entry) */
+ 0xd8190000 /* jic $25, 0 */
+};
+
/* The format of subsequent MIPS16 o32 PLT entries. We use v0 ($2)
and v1 ($3) as temporaries because t8 ($24) and t9 ($25) are not
directly addressable. */
ret->fn_stub = NULL;
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
+ ret->mipsxhash_loc = 0;
ret->global_got_area = GGA_NONE;
ret->got_only_for_calls = TRUE;
ret->readonly_reloc = FALSE;
if (!sec->used_by_bfd)
{
struct _mips_elf_section_data *sdata;
- bfd_size_type amt = sizeof (*sdata);
+ size_t amt = sizeof (*sdata);
sdata = bfd_zalloc (abfd, amt);
if (sdata == NULL)
/* The symbolic header contains absolute file offsets and sizes to
read. */
#define READ(ptr, offset, count, size, type) \
- if (symhdr->count == 0) \
- debug->ptr = NULL; \
- else \
+ do \
{ \
- bfd_size_type amt = (bfd_size_type) size * symhdr->count; \
- debug->ptr = bfd_malloc (amt); \
- if (debug->ptr == NULL) \
+ size_t amt; \
+ debug->ptr = NULL; \
+ if (symhdr->count == 0) \
+ break; \
+ if (_bfd_mul_overflow (size, symhdr->count, &amt)) \
+ { \
+ bfd_set_error (bfd_error_file_too_big); \
+ goto error_return; \
+ } \
+ if (bfd_seek (abfd, symhdr->offset, SEEK_SET) != 0) \
goto error_return; \
- if (bfd_seek (abfd, symhdr->offset, SEEK_SET) != 0 \
- || bfd_bread (debug->ptr, amt, abfd) != amt) \
+ debug->ptr = (type) _bfd_malloc_and_read (abfd, amt, amt); \
+ if (debug->ptr == NULL) \
goto error_return; \
- }
+ } while (0)
READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, void *);
return TRUE;
error_return:
- if (ext_hdr != NULL)
- free (ext_hdr);
- if (debug->line != NULL)
- free (debug->line);
- if (debug->external_dnr != NULL)
- free (debug->external_dnr);
- if (debug->external_pdr != NULL)
- free (debug->external_pdr);
- if (debug->external_sym != NULL)
- free (debug->external_sym);
- if (debug->external_opt != NULL)
- free (debug->external_opt);
- if (debug->external_aux != NULL)
- free (debug->external_aux);
- if (debug->ss != NULL)
- free (debug->ss);
- if (debug->ssext != NULL)
- free (debug->ssext);
- if (debug->external_fdr != NULL)
- free (debug->external_fdr);
- if (debug->external_rfd != NULL)
- free (debug->external_rfd);
- if (debug->external_ext != NULL)
- free (debug->external_ext);
+ free (ext_hdr);
+ free (debug->line);
+ free (debug->external_dnr);
+ free (debug->external_pdr);
+ free (debug->external_sym);
+ free (debug->external_opt);
+ free (debug->external_aux);
+ free (debug->ss);
+ free (debug->ssext);
+ free (debug->external_fdr);
+ free (debug->external_rfd);
+ free (debug->external_ext);
return FALSE;
}
\f
matters, but someday it might). */
s->map_head.link_order = NULL;
- if (epdr != NULL)
- free (epdr);
- if (rpdr != NULL)
- free (rpdr);
- if (esym != NULL)
- free (esym);
- if (ss != NULL)
- free (ss);
- if (sv != NULL)
- free (sv);
-
+ free (epdr);
+ free (rpdr);
+ free (esym);
+ free (ss);
+ free (sv);
return TRUE;
error_return:
- if (epdr != NULL)
- free (epdr);
- if (rpdr != NULL)
- free (rpdr);
- if (esym != NULL)
- free (esym);
- if (ss != NULL)
- free (ss);
- if (sv != NULL)
- free (sv);
+ free (epdr);
+ free (rpdr);
+ free (esym);
+ free (ss);
+ free (sv);
return FALSE;
}
\f
{
const char *name;
- name = bfd_get_section_name (section->owner, section);
+ name = bfd_section_name (section);
return (FN_STUB_P (name)
|| CALL_STUB_P (name)
|| CALL_FP_STUB_P (name)
/* Make sure that any padding goes before the stub. */
align = input_section->alignment_power;
- if (!bfd_set_section_alignment (s->owner, s, align))
+ if (!bfd_set_section_alignment (s, align))
return FALSE;
if (align > 3)
s->size = (1 << align) - 8;
asection *input_section = stub->h->root.root.u.def.section;
s = htab->add_stub_section (".text", NULL,
input_section->output_section);
- if (s == NULL || !bfd_set_section_alignment (s->owner, s, 4))
+ if (s == NULL || !bfd_set_section_alignment (s, 4))
return FALSE;
htab->strampoline = s;
}
bfd *output_bfd, char **error_message)
{
if ((symbol->flags & (BSF_GLOBAL | BSF_WEAK)) != 0
- || bfd_is_und_section (bfd_get_section (symbol))
- || bfd_is_com_section (bfd_get_section (symbol)))
+ || bfd_is_und_section (bfd_asymbol_section (symbol))
+ || bfd_is_com_section (bfd_asymbol_section (symbol)))
/* The relocation is against a global symbol. */
return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data,
input_section, output_bfd,
h->esym.asym.sc = scUndefined;
else
{
- name = bfd_section_name (output_section->owner, output_section);
+ name = bfd_section_name (output_section);
if (strcmp (name, ".text") == 0)
h->esym.asym.sc = scText;
| SEC_LINKER_CREATED
| SEC_READONLY));
if (sreloc == NULL
- || ! bfd_set_section_alignment (dynobj, sreloc,
- MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
+ || !bfd_set_section_alignment (sreloc,
+ MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
return NULL;
}
return sreloc;
at the head of the table; see `_bfd_elf_link_renumber_dynsyms'. */
hsd.max_local_dynindx = count_section_dynsyms (abfd, info) + 1;
hsd.max_non_got_dynindx = htab->root.local_dynsymcount + 1;
+ hsd.output_bfd = abfd;
+ if (htab->root.dynobj != NULL
+ && htab->root.dynamic_sections_created
+ && info->emit_gnu_hash)
+ {
+ asection *s = bfd_get_linker_section (htab->root.dynobj, ".MIPS.xhash");
+ BFD_ASSERT (s != NULL);
+ hsd.mipsxhash = s->contents;
+ BFD_ASSERT (hsd.mipsxhash != NULL);
+ }
+ else
+ hsd.mipsxhash = NULL;
mips_elf_link_hash_traverse (htab, mips_elf_sort_hash_table_f, &hsd);
/* There should have been enough room in the symbol table to
break;
}
+ /* Populate the .MIPS.xhash translation table entry with
+ the symbol dynindx. */
+ if (h->mipsxhash_loc != 0 && hsd->mipsxhash != NULL)
+ bfd_put_32 (hsd->output_bfd, h->root.dynindx,
+ hsd->mipsxhash + h->mipsxhash_loc);
+
return TRUE;
}
s = bfd_make_section_anyway_with_flags (abfd, ".compact_rel", flags);
if (s == NULL
- || ! bfd_set_section_alignment (abfd, s,
- MIPS_ELF_LOG_FILE_ALIGN (abfd)))
+ || !bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
s->size = sizeof (Elf32_External_compact_rel);
in the function stub generation and in the linker script. */
s = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
if (s == NULL
- || ! bfd_set_section_alignment (abfd, s, 4))
+ || !bfd_set_section_alignment (s, 4))
return FALSE;
htab->root.sgot = s;
symtab_hdr->sh_link,
sym->st_name);
if (*namep == NULL || **namep == '\0')
- *namep = bfd_section_name (input_bfd, sec);
+ *namep = bfd_section_name (sec);
/* For relocations against a section symbol and ones against no
symbol (absolute relocations) infer the ISA mode from the addend. */
}
else
{
- bfd_boolean reject_undefined
- = (info->unresolved_syms_in_objects == RM_GENERATE_ERROR
- || ELF_ST_VISIBILITY (h->root.other) != STV_DEFAULT);
+ bfd_boolean reject_undefined
+ = (info->unresolved_syms_in_objects == RM_DIAGNOSE
+ && !info->warn_unresolved_syms)
+ || ELF_ST_VISIBILITY (h->root.other) != STV_DEFAULT;
- (*info->callbacks->undefined_symbol)
+ info->callbacks->undefined_symbol
(info, h->root.root.root.string, input_bfd,
input_section, relocation->r_offset, reject_undefined);
sec = NULL;
for (o = input_bfd->sections; o != NULL; o = o->next)
{
- if (CALL_FP_STUB_P (bfd_get_section_name (input_bfd, o)))
+ if (CALL_FP_STUB_P (bfd_section_name (o)))
{
sec = h->call_fp_stub;
break;
case R_MIPS_PCHI16:
value = mips_elf_high (symbol + addend - p);
- if (was_local_p || h->root.root.type != bfd_link_hash_undefweak)
- overflowed_p = mips_elf_overflow_p (value, 16);
value &= howto->dst_mask;
break;
}
/* Make this the JALX opcode. */
- x = (x & ~(0x3f << 26)) | (jalx_opcode << 26);
+ x = (x & ~(0x3fu << 26)) | (jalx_opcode << 26);
}
else if (cross_mode_jump_p && b_reloc_p (r_type))
{
if (hdr->bfd_section != NULL)
{
- const char *name = bfd_get_section_name (abfd, hdr->bfd_section);
+ const char *name = bfd_section_name (hdr->bfd_section);
/* .sbss is not handled specially here because the GNU/Linux
prelinker can convert .sbss from NOBITS to PROGBITS and
/* Handle a MIPS specific section when reading an object file. This
is called when elfcode.h finds a section with an unknown type.
- This routine supports both the 32-bit and 64-bit ELF ABI.
-
- FIXME: We need to handle the SHF_MIPS_GPREL flag, but I'm not sure
- how to. */
+ This routine supports both the 32-bit and 64-bit ELF ABI. */
bfd_boolean
_bfd_mips_elf_section_from_shdr (bfd *abfd,
&& ! CONST_STRNEQ (name, ".MIPS.post_rel"))
return FALSE;
break;
+ case SHT_MIPS_XHASH:
+ if (strcmp (name, ".MIPS.xhash") != 0)
+ return FALSE;
default:
break;
}
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
return FALSE;
+ if (hdr->sh_flags & SHF_MIPS_GPREL)
+ flags |= SEC_SMALL_DATA;
+
if (flags)
{
- if (! bfd_set_section_flags (abfd, hdr->bfd_section,
- (bfd_get_section_flags (abfd,
- hdr->bfd_section)
- | flags)))
+ if (!bfd_set_section_flags (hdr->bfd_section,
+ (bfd_section_flags (hdr->bfd_section)
+ | flags)))
return FALSE;
}
bfd_boolean
_bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
{
- const char *name = bfd_get_section_name (abfd, sec);
+ const char *name = bfd_section_name (sec);
if (strcmp (name, ".liblist") == 0)
{
hdr->sh_flags |= SHF_ALLOC;
hdr->sh_entsize = 8;
}
+ else if (strcmp (name, ".MIPS.xhash") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_XHASH;
+ hdr->sh_flags |= SHF_ALLOC;
+ hdr->sh_entsize = get_elf_backend_data(abfd)->s->arch_size == 64 ? 0 : 4;
+ }
/* The generic elf_fake_sections will set up REL_HDR using the default
kind of relocations. We used to set up a second header for the
_bfd_mips_elf_section_from_bfd_section (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec, int *retval)
{
- if (strcmp (bfd_get_section_name (abfd, sec), ".scommon") == 0)
+ if (strcmp (bfd_section_name (sec), ".scommon") == 0)
{
*retval = SHN_MIPS_SCOMMON;
return TRUE;
}
- if (strcmp (bfd_get_section_name (abfd, sec), ".acommon") == 0)
+ if (strcmp (bfd_section_name (sec), ".acommon") == 0)
{
*retval = SHN_MIPS_ACOMMON;
return TRUE;
{
asymbol *elf_text_symbol;
asection *elf_text_section;
- bfd_size_type amt = sizeof (asection);
+ size_t amt = sizeof (asection);
elf_text_section = bfd_zalloc (abfd, amt);
if (elf_text_section == NULL)
{
asymbol *elf_data_symbol;
asection *elf_data_section;
- bfd_size_type amt = sizeof (asection);
+ size_t amt = sizeof (asection);
elf_data_section = bfd_zalloc (abfd, amt);
if (elf_data_section == NULL)
s = bfd_get_linker_section (abfd, ".dynamic");
if (s != NULL)
{
- if (! bfd_set_section_flags (abfd, s, flags))
+ if (!bfd_set_section_flags (s, flags))
return FALSE;
}
}
MIPS_ELF_STUB_SECTION_NAME (abfd),
flags | SEC_CODE);
if (s == NULL
- || ! bfd_set_section_alignment (abfd, s,
- MIPS_ELF_LOG_FILE_ALIGN (abfd)))
+ || !bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
htab->sstubs = s;
s = bfd_make_section_anyway_with_flags (abfd, ".rld_map",
flags &~ (flagword) SEC_READONLY);
if (s == NULL
- || ! bfd_set_section_alignment (abfd, s,
- MIPS_ELF_LOG_FILE_ALIGN (abfd)))
+ || !bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
}
+ /* Create .MIPS.xhash section. */
+ if (info->emit_gnu_hash)
+ s = bfd_make_section_anyway_with_flags (abfd, ".MIPS.xhash",
+ flags | SEC_READONLY);
+
/* On IRIX5, we adjust add some additional symbols and change the
alignments of several sections. There is no ABI documentation
indicating that this is necessary on IRIX6, nor any evidence that
/* Change alignments of some sections. */
s = bfd_get_linker_section (abfd, ".hash");
if (s != NULL)
- (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
s = bfd_get_linker_section (abfd, ".dynsym");
if (s != NULL)
- (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
s = bfd_get_linker_section (abfd, ".dynstr");
if (s != NULL)
- (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
/* ??? */
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s != NULL)
- (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
s = bfd_get_linker_section (abfd, ".dynamic");
if (s != NULL)
- (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ bfd_set_section_alignment (s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
}
if (bfd_link_executable (info))
/* Check for the mips16 stub sections. */
- name = bfd_get_section_name (abfd, sec);
+ name = bfd_section_name (sec);
if (FN_STUB_P (name))
{
unsigned long r_symndx;
/* This relocation describes which C++ vtable entries are actually
used. Record for later use during GC. */
case R_MIPS_GNU_VTENTRY:
- BFD_ASSERT (h != NULL);
- if (h != NULL
- && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
+ if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
return FALSE;
break;
{
switch (r_type)
{
+ case R_MIPS_TLS_TPREL_HI16:
+ case R_MIPS16_TLS_TPREL_HI16:
+ case R_MICROMIPS_TLS_TPREL_HI16:
+ case R_MIPS_TLS_TPREL_LO16:
+ case R_MIPS16_TLS_TPREL_LO16:
+ case R_MICROMIPS_TLS_TPREL_LO16:
+ /* These are okay in PIE, but not in a shared library. */
+ if (bfd_link_executable (info))
+ break;
+
+ /* FALLTHROUGH */
+
case R_MIPS16_HI16:
case R_MIPS_HI16:
case R_MIPS_HIGHER:
if (r_symndx == STN_UNDEF)
break;
+ /* Likewise an absolute symbol. */
+ if (h != NULL && bfd_is_abs_symbol (&h->root))
+ break;
+
/* R_MIPS_HI16 against _gp_disp is used for $gp setup,
and has a special meaning. */
if (!NEWABI_P (abfd) && h != NULL
case R_MIPS16_26:
case R_MIPS_26:
case R_MICROMIPS_26_S1:
- howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, r_type, FALSE);
- _bfd_error_handler
- /* xgettext:c-format */
- (_("%pB: relocation %s against `%s' can not be used"
- " when making a shared object; recompile with -fPIC"),
- abfd, howto->name,
- (h) ? h->root.root.string : "a local symbol");
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, r_type, NEWABI_P (abfd));
+ /* An error for unsupported relocations is raised as part
+ of the above search, so we can skip the following. */
+ if (howto != NULL)
+ info->callbacks->einfo
+ /* xgettext:c-format */
+ (_("%X%H: relocation %s against `%s' cannot be used"
+ " when making a shared object; recompile with -fPIC\n"),
+ abfd, sec, rel->r_offset, howto->name,
+ (h) ? h->root.root.string : "a local symbol");
+ break;
default:
break;
}
hmips = (struct mips_elf_link_hash_entry *) h;
/* Make sure we know what is going on here. */
- BFD_ASSERT (dynobj != NULL
- && (h->needs_plt
- || h->is_weakalias
- || (h->def_dynamic
- && h->ref_regular
- && !h->def_regular)));
+ if (dynobj == NULL
+ || (! h->needs_plt
+ && ! h->is_weakalias
+ && (! h->def_dynamic
+ || ! h->ref_regular
+ || h->def_regular)))
+ {
+ if (h->type == STT_GNU_IFUNC)
+ _bfd_error_handler (_("IFUNC symbol %s in dynamic symbol table - IFUNCS are not supported"),
+ h->root.root.string);
+ else
+ _bfd_error_handler (_("non-dynamic symbol %s in dynamic symbol table"),
+ h->root.root.string);
+ return TRUE;
+ }
hmips = (struct mips_elf_link_hash_entry *) h;
Encourage better cache usage by aligning. We do this
lazily to avoid pessimizing traditional objects. */
if (!htab->is_vxworks
- && !bfd_set_section_alignment (dynobj, htab->root.splt, 5))
+ && !bfd_set_section_alignment (htab->root.splt, 5))
return FALSE;
/* Make sure that .got.plt is word-aligned. We do this lazily
for the same reason as above. */
- if (!bfd_set_section_alignment (dynobj, htab->root.sgotplt,
+ if (!bfd_set_section_alignment (htab->root.sgotplt,
MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
return FALSE;
sect = bfd_get_section_by_name (output_bfd, ".reginfo");
if (sect != NULL)
{
- bfd_set_section_size (output_bfd, sect, sizeof (Elf32_External_RegInfo));
+ bfd_set_section_size (sect, sizeof (Elf32_External_RegInfo));
sect->flags |= SEC_FIXED_SIZE | SEC_HAS_CONTENTS;
}
sect = bfd_get_section_by_name (output_bfd, ".MIPS.abiflags");
if (sect != NULL)
{
- bfd_set_section_size (output_bfd, sect,
- sizeof (Elf_External_ABIFlags_v0));
+ bfd_set_section_size (sect, sizeof (Elf_External_ABIFlags_v0));
sect->flags |= SEC_FIXED_SIZE | SEC_HAS_CONTENTS;
}
/* It's OK to base decisions on the section name, because none
of the dynobj section names depend upon the input files. */
- name = bfd_get_section_name (dynobj, s);
+ name = bfd_section_name (s);
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
assert a DT_TEXTREL entry rather than testing whether
there exists a relocation to a read only section or
not. */
- outname = bfd_get_section_name (output_bfd,
- s->output_section);
+ outname = bfd_section_name (s->output_section);
target = bfd_get_section_by_name (output_bfd, outname + 4);
if ((target != NULL
&& (target->flags & SEC_READONLY) != 0
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
return FALSE;
+ if (info->emit_gnu_hash
+ && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_XHASH, 0))
+ return FALSE;
+
if (IRIX_COMPAT (dynobj) == ict_irix5
&& ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0))
return FALSE;
asection *s;
bfd_byte *loc;
bfd_vma offset, target, target_high, target_low;
+ bfd_vma branch_pc;
+ bfd_signed_vma pcrel_offset = 0;
stub = (struct mips_elf_la25_stub *) *slot;
hti = (struct mips_htab_traverse_info *) data;
/* Work out where in the section this stub should go. */
offset = stub->offset;
+ /* We add 8 here to account for the LUI/ADDIU instructions
+ before the branch instruction. This cannot be moved down to
+ where pcrel_offset is calculated as 's' is updated in
+ mips_elf_get_la25_target. */
+ branch_pc = s->output_section->vma + s->output_offset + offset + 8;
+
/* Work out the target address. */
target = mips_elf_get_la25_target (stub, &s);
target += s->output_section->vma + s->output_offset;
target_high = ((target + 0x8000) >> 16) & 0xffff;
target_low = (target & 0xffff);
+ /* Calculate the PC of the compact branch instruction (for the case where
+ compact branches are used for either microMIPSR6 or MIPSR6 with
+ compact branches. Add 4-bytes to account for BC using the PC of the
+ next instruction as the base. */
+ pcrel_offset = target - (branch_pc + 4);
+
if (stub->stub_section != htab->strampoline)
{
/* This is a simple LUI/ADDIU stub. Zero out the beginning
else
{
bfd_put_32 (hti->output_bfd, LA25_LUI (target_high), loc);
- bfd_put_32 (hti->output_bfd, LA25_J (target), loc + 4);
- bfd_put_32 (hti->output_bfd, LA25_ADDIU (target_low), loc + 8);
+ if (MIPSR6_P (hti->output_bfd) && htab->compact_branches)
+ {
+ bfd_put_32 (hti->output_bfd, LA25_ADDIU (target_low), loc + 4);
+ bfd_put_32 (hti->output_bfd, LA25_BC (pcrel_offset), loc + 8);
+ }
+ else
+ {
+ bfd_put_32 (hti->output_bfd, LA25_J (target), loc + 4);
+ bfd_put_32 (hti->output_bfd, LA25_ADDIU (target_low), loc + 8);
+ }
bfd_put_32 (hti->output_bfd, 0, loc + 12);
}
}
/* Fill in the PLT entry itself. */
if (MIPSR6_P (output_bfd))
- plt_entry = mipsr6_exec_plt_entry;
+ plt_entry = htab->compact_branches ? mipsr6_exec_plt_entry_compact
+ : mipsr6_exec_plt_entry;
else
plt_entry = mips_exec_plt_entry;
bfd_put_32 (output_bfd, plt_entry[0] | got_address_high, loc);
bfd_put_32 (output_bfd, plt_entry[1] | got_address_low | load,
loc + 4);
- if (! LOAD_INTERLOCKS_P (output_bfd))
+ if (! LOAD_INTERLOCKS_P (output_bfd)
+ || (MIPSR6_P (output_bfd) && htab->compact_branches))
{
bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 8);
bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
stub + idx);
idx += 4;
}
- bfd_put_32 (output_bfd, STUB_JALR, stub + idx);
- idx += 4;
+
+ if (!(MIPSR6_P (output_bfd) && htab->compact_branches))
+ {
+ bfd_put_32 (output_bfd, STUB_JALR, stub + idx);
+ idx += 4;
+ }
/* If a large stub is not required and sign extension is not a
problem, then use legacy code in the stub. */
else
bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx),
stub + idx);
+ idx += 4;
+
+ if (MIPSR6_P (output_bfd) && htab->compact_branches)
+ bfd_put_32 (output_bfd, STUB_JALRC, stub + idx);
}
BFD_ASSERT (h->plt.plist->stub_offset <= htab->sstubs->size);
BFD_ASSERT (htab != NULL);
if (ABI_64_P (output_bfd))
- plt_entry = mips_n64_exec_plt0_entry;
+ plt_entry = (htab->compact_branches
+ ? mipsr6_n64_exec_plt0_entry_compact
+ : mips_n64_exec_plt0_entry);
else if (ABI_N32_P (output_bfd))
- plt_entry = mips_n32_exec_plt0_entry;
+ plt_entry = (htab->compact_branches
+ ? mipsr6_n32_exec_plt0_entry_compact
+ : mips_n32_exec_plt0_entry);
else if (!htab->plt_header_is_comp)
- plt_entry = mips_o32_exec_plt0_entry;
+ plt_entry = (htab->compact_branches
+ ? mipsr6_o32_exec_plt0_entry_compact
+ : mips_o32_exec_plt0_entry);
else if (htab->insn32)
plt_entry = micromips_insn32_o32_exec_plt0_entry;
else
swap_out_p = FALSE;
break;
+ case DT_MIPS_XHASH:
+ name = ".MIPS.xhash";
+ s = bfd_get_linker_section (dynobj, name);
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+ break;
+
default:
swap_out_p = FALSE;
if (htab->is_vxworks
switch (bfd_get_mach (abfd))
{
default:
+ if (ABI_N32_P (abfd) || ABI_64_P (abfd))
+ val = E_MIPS_ARCH_3;
+ else
+ val = E_MIPS_ARCH_1;
+ break;
+
case bfd_mach_mips3000:
val = E_MIPS_ARCH_1;
break;
number. This is used by both the 32-bit and the 64-bit ABI. */
void
-_bfd_mips_elf_final_write_processing (bfd *abfd,
- bfd_boolean linker ATTRIBUTE_UNUSED)
+_bfd_mips_final_write_processing (bfd *abfd)
{
unsigned int i;
Elf_Internal_Shdr **hdrpp;
case SHT_MIPS_GPTAB:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
- name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ name = bfd_section_name ((*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL
&& CONST_STRNEQ (name, ".gptab."));
sec = bfd_get_section_by_name (abfd, name + sizeof ".gptab" - 1);
case SHT_MIPS_CONTENT:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
- name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ name = bfd_section_name ((*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL
&& CONST_STRNEQ (name, ".MIPS.content"));
sec = bfd_get_section_by_name (abfd,
case SHT_MIPS_EVENTS:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
- name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ name = bfd_section_name ((*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL);
if (CONST_STRNEQ (name, ".MIPS.events"))
sec = bfd_get_section_by_name (abfd,
(*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
break;
+ case SHT_MIPS_XHASH:
+ sec = bfd_get_section_by_name (abfd, ".dynsym");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
}
}
}
+
+bfd_boolean
+_bfd_mips_elf_final_write_processing (bfd *abfd)
+{
+ _bfd_mips_final_write_processing (abfd);
+ return _bfd_elf_final_write_processing (abfd);
+}
\f
/* When creating an IRIX5 executable, we need REGINFO and RTPROC
segments. */
{
asection *s;
struct elf_segment_map *m, **pm;
- bfd_size_type amt;
+ size_t amt;
/* If there is a .reginfo section, we need a PT_MIPS_REGINFO
segment. */
&& s->vma + s->size <= high)
++c;
- amt = sizeof *n + (bfd_size_type) (c - 1) * sizeof (asection *);
+ amt = sizeof *n - sizeof (asection *) + c * sizeof (asection *);
n = bfd_zalloc (abfd, amt);
if (n == NULL)
return FALSE;
for (o = sub->sections; o != NULL; o = o->next)
if (!o->gc_mark
- && MIPS_ELF_ABIFLAGS_SECTION_NAME_P
- (bfd_get_section_name (sub, o)))
+ && MIPS_ELF_ABIFLAGS_SECTION_NAME_P (bfd_section_name (o)))
{
if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
return FALSE;
filename_ptr, functionname_ptr,
line_ptr, discriminator_ptr,
dwarf_debug_sections,
- ABI_64_P (abfd) ? 8 : 0,
&elf_tdata (abfd)->dwarf2_find_line_info)
- || _bfd_dwarf1_find_nearest_line (abfd, symbols, section, offset,
- filename_ptr, functionname_ptr,
- line_ptr))
- {
- /* PR 22789: If the function name or filename was not found through
- the debug information, then try an ordinary lookup instead. */
- if ((functionname_ptr != NULL && *functionname_ptr == NULL)
- || (filename_ptr != NULL && *filename_ptr == NULL))
- {
- /* Do not override already discovered names. */
- if (functionname_ptr != NULL && *functionname_ptr != NULL)
- functionname_ptr = NULL;
-
- if (filename_ptr != NULL && *filename_ptr != NULL)
- filename_ptr = NULL;
-
- _bfd_elf_find_function (abfd, symbols, section, offset,
- filename_ptr, functionname_ptr);
- }
+ == 1)
+ return TRUE;
+ if (_bfd_dwarf1_find_nearest_line (abfd, symbols, section, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr))
+ {
+ if (!*functionname_ptr)
+ _bfd_elf_find_function (abfd, symbols, section, offset,
+ *filename_ptr ? NULL : filename_ptr,
+ functionname_ptr);
return TRUE;
}
if (elf_section_data (section) == NULL)
{
- bfd_size_type amt = sizeof (struct bfd_elf_section_data);
+ size_t amt = sizeof (struct bfd_elf_section_data);
section->used_by_bfd = bfd_zalloc (abfd, amt);
if (elf_section_data (section) == NULL)
return FALSE;
}
}
}
- if (reloc_vector != NULL)
- free (reloc_vector);
+ free (reloc_vector);
return data;
-error_return:
- if (reloc_vector != NULL)
- free (reloc_vector);
+ error_return:
+ free (reloc_vector);
return NULL;
}
\f
}
}
- if (internal_relocs != NULL
- && elf_section_data (sec)->relocs != internal_relocs)
+ if (elf_section_data (sec)->relocs != internal_relocs)
free (internal_relocs);
return TRUE;
error_return:
- if (isymbuf != NULL
- && symtab_hdr->contents != (unsigned char *) isymbuf)
+ if (symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
- if (contents != NULL
- && elf_section_data (sec)->this_hdr.contents != contents)
+ if (elf_section_data (sec)->this_hdr.contents != contents)
free (contents);
- if (internal_relocs != NULL
- && elf_section_data (sec)->relocs != internal_relocs)
+ if (elf_section_data (sec)->relocs != internal_relocs)
free (internal_relocs);
return FALSE;
_bfd_mips_elf_link_hash_table_create (bfd *abfd)
{
struct mips_elf_link_hash_table *ret;
- bfd_size_type amt = sizeof (struct mips_elf_link_hash_table);
+ size_t amt = sizeof (struct mips_elf_link_hash_table);
ret = bfd_zmalloc (amt);
if (ret == NULL)
mips_elf_hash_table (info)->ignore_branch_isa = ignore_branch_isa;
mips_elf_hash_table (info)->gnu_target = gnu_target;
}
+
+/* A function that the linker calls to enable use of compact branches in
+ linker generated code for MIPSR6. */
+
+void
+_bfd_mips_elf_compact_branches (struct bfd_link_info *info, bfd_boolean on)
+{
+ mips_elf_hash_table (info)->compact_branches = on;
+}
+
\f
/* Structure for saying that BFD machine EXTENSION extends BASE. */
".rtproc",
flags);
if (rtproc_sec == NULL
- || ! bfd_set_section_alignment (abfd, rtproc_sec, 4))
+ || !bfd_set_section_alignment (rtproc_sec, 4))
return FALSE;
}
/* Check to see if the input BFD actually contains any sections. If not,
then it has no attributes, and its flags may not have been initialized
either, but it cannot actually cause any incompatibility. */
+ /* FIXME: This excludes any input shared library from consideration. */
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
{
/* Ignore synthetic sections and empty .text, .data and .bss sections
return "DT_MIPS_PLTGOT";
case DT_MIPS_RWPLT:
return "DT_MIPS_RWPLT";
+ case DT_MIPS_XHASH:
+ return "DT_MIPS_XHASH";
}
}
{ STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
{ STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
{ STRING_COMMA_LEN (".ucode"), 0, SHT_MIPS_UCODE, 0 },
+ { STRING_COMMA_LEN (".MIPS.xhash"), 0, SHT_MIPS_XHASH, SHF_ALLOC },
{ NULL, 0, 0, 0, 0 }
};
MIPS_LIBC_ABI_UNIQUE,
MIPS_LIBC_ABI_MIPS_O32_FP64,
MIPS_LIBC_ABI_ABSOLUTE,
+ MIPS_LIBC_ABI_XHASH,
MIPS_LIBC_ABI_MAX
};
-void
-_bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
+bfd_boolean
+_bfd_mips_init_file_header (bfd *abfd, struct bfd_link_info *link_info)
{
struct mips_elf_link_hash_table *htab = NULL;
Elf_Internal_Ehdr *i_ehdrp;
+ if (!_bfd_elf_init_file_header (abfd, link_info))
+ return FALSE;
+
i_ehdrp = elf_elfheader (abfd);
if (link_info)
{
if (htab != NULL && htab->use_absolute_zero && htab->gnu_target)
i_ehdrp->e_ident[EI_ABIVERSION] = MIPS_LIBC_ABI_ABSOLUTE;
- _bfd_elf_post_process_headers (abfd, link_info);
+ /* Mark that we need support for .MIPS.xhash in the dynamic linker,
+ if it is the only hash section that will be created. */
+ if (link_info && link_info->emit_gnu_hash && !link_info->emit_hash)
+ i_ehdrp->e_ident[EI_ABIVERSION] = MIPS_LIBC_ABI_XHASH;
+ return TRUE;
}
int
{
return COMPACT_EH_CANT_UNWIND_OPCODE;
}
+
+/* Record a position XLAT_LOC in the xlat translation table, associated with
+ the hash entry H. The entry in the translation table will later be
+ populated with the real symbol dynindx. */
+
+void
+_bfd_mips_elf_record_xhash_symbol (struct elf_link_hash_entry *h,
+ bfd_vma xlat_loc)
+{
+ struct mips_elf_link_hash_entry *hmips;
+
+ hmips = (struct mips_elf_link_hash_entry *) h;
+ hmips->mipsxhash_loc = xlat_loc;
+}