/* MIPS-specific support for ELF
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
<ian@cygnus.com>.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
/* This file handles functionality common to the different MIPS ABI's. */
-#include "bfd.h"
#include "sysdep.h"
+#include "bfd.h"
#include "libbfd.h"
#include "libiberty.h"
#include "elf-bfd.h"
bfd_boolean mips16_stubs_seen;
/* True if we're generating code for VxWorks. */
bfd_boolean is_vxworks;
+ /* True if we already reported the small-data section overflow. */
+ bfd_boolean small_data_overflow_reported;
/* Shortcuts to some dynamic sections, or NULL if they are not
being used. */
asection *srelbss;
\f
static struct mips_got_entry *mips_elf_create_local_got_entry
(bfd *, struct bfd_link_info *, bfd *, struct mips_got_info *, asection *,
- asection *, bfd_vma, unsigned long, struct mips_elf_link_hash_entry *, int);
+ bfd_vma, unsigned long, struct mips_elf_link_hash_entry *, int);
static bfd_boolean mips_elf_sort_hash_table_f
(struct mips_elf_link_hash_entry *, void *);
static bfd_vma mips_elf_high
(bfd_vma);
-static bfd_boolean mips_elf_stub_section_p
+static bfd_boolean mips16_stub_section_p
(bfd *, asection *);
static bfd_boolean mips_elf_create_dynamic_relocation
(bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
#define FN_STUB ".mips16.fn."
#define CALL_STUB ".mips16.call."
#define CALL_FP_STUB ".mips16.call.fp."
+
+#define FN_STUB_P(name) CONST_STRNEQ (name, FN_STUB)
+#define CALL_STUB_P(name) CONST_STRNEQ (name, CALL_STUB)
+#define CALL_FP_STUB_P(name) CONST_STRNEQ (name, CALL_FP_STUB)
\f
/* The format of the first PLT entry in a VxWorks executable. */
static const bfd_vma mips_vxworks_exec_plt0_entry[] = {
{
Elf_Internal_Rela int_reloc1;
Elf_Internal_Rela int_reloc2;
+ int diff;
bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg1, &int_reloc1);
bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg2, &int_reloc2);
- return ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
+ diff = ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
+ if (diff != 0)
+ return diff;
+
+ if (int_reloc1.r_offset < int_reloc2.r_offset)
+ return -1;
+ if (int_reloc1.r_offset > int_reloc2.r_offset)
+ return 1;
+ return 0;
}
/* Like sort_dynamic_relocs, but used for elf64 relocations. */
(*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
(reldyn_sorting_bfd, arg2, int_reloc2);
- return (ELF64_R_SYM (int_reloc1[0].r_info)
- - ELF64_R_SYM (int_reloc2[0].r_info));
+ if (ELF64_R_SYM (int_reloc1[0].r_info) < ELF64_R_SYM (int_reloc2[0].r_info))
+ return -1;
+ if (ELF64_R_SYM (int_reloc1[0].r_info) > ELF64_R_SYM (int_reloc2[0].r_info))
+ return 1;
+
+ if (int_reloc1[0].r_offset < int_reloc2[0].r_offset)
+ return -1;
+ if (int_reloc1[0].r_offset > int_reloc2[0].r_offset)
+ return 1;
+ return 0;
#else
abort ();
#endif
return got_address - got_value;
}
-/* Return the GOT offset for address VALUE, which was derived from
- a symbol belonging to INPUT_SECTION. If there is not yet a GOT
+/* Return the GOT offset for address VALUE. If there is not yet a GOT
entry for this value, create one. If R_SYMNDX refers to a TLS symbol,
create a TLS GOT entry instead. Return -1 if no satisfactory GOT
offset can be found. */
static bfd_vma
mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
- asection *input_section, bfd_vma value,
- unsigned long r_symndx,
+ bfd_vma value, unsigned long r_symndx,
struct mips_elf_link_hash_entry *h, int r_type)
{
asection *sgot;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot,
- input_section, value,
- r_symndx, h, r_type);
+ value, r_symndx, h, r_type);
if (!entry)
return MINUS_ONE;
return index;
}
-/* Find a GOT page entry that points to within 32KB of VALUE, which was
- calculated from a symbol belonging to INPUT_SECTION. These entries
- are supposed to be placed at small offsets in the GOT, i.e., within
- 32KB of GP. Return the index of the GOT entry, or -1 if no entry
- could be created. If OFFSETP is nonnull, use it to return the
+/* Find a GOT page entry that points to within 32KB of VALUE. These
+ entries are supposed to be placed at small offsets in the GOT, i.e.,
+ within 32KB of GP. Return the index of the GOT entry, or -1 if no
+ entry could be created. If OFFSETP is nonnull, use it to return the
offset of the GOT entry from VALUE. */
static bfd_vma
mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
- asection *input_section, bfd_vma value, bfd_vma *offsetp)
+ bfd_vma value, bfd_vma *offsetp)
{
asection *sgot;
struct mips_got_info *g;
page = (value + 0x8000) & ~(bfd_vma) 0xffff;
entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot,
- input_section, page, 0,
- NULL, R_MIPS_GOT_PAGE);
+ page, 0, NULL, R_MIPS_GOT_PAGE);
if (!entry)
return MINUS_ONE;
return index;
}
-/* Find a local GOT entry for an R_MIPS_GOT16 relocation against VALUE,
- which was calculated from a symbol belonging to INPUT_SECTION.
+/* Find a local GOT entry for an R_MIPS_GOT16 relocation against VALUE.
EXTERNAL is true if the relocation was against a global symbol
that has been forced local. */
static bfd_vma
mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
- asection *input_section, bfd_vma value,
- bfd_boolean external)
+ bfd_vma value, bfd_boolean external)
{
asection *sgot;
struct mips_got_info *g;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot,
- input_section, value, 0,
- NULL, R_MIPS_GOT16);
+ value, 0, NULL, R_MIPS_GOT16);
if (entry)
return entry->gotidx;
else
static struct mips_got_entry *
mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
bfd *ibfd, struct mips_got_info *gg,
- asection *sgot, asection *input_section,
- bfd_vma value, unsigned long r_symndx,
+ asection *sgot, bfd_vma value,
+ unsigned long r_symndx,
struct mips_elf_link_hash_entry *h,
int r_type)
{
MIPS_ELF_PUT_WORD (abfd, value,
(sgot->contents + entry.gotidx));
- /* These GOT entries need a dynamic relocation on VxWorks. Because
- the offset between segments is not fixed, the relocation must be
- against a symbol in the same segment as the original symbol.
- The easiest way to do this is to take INPUT_SECTION's output
- section and emit a relocation against its section symbol. */
+ /* These GOT entries need a dynamic relocation on VxWorks. */
if (htab->is_vxworks)
{
Elf_Internal_Rela outrel;
- asection *s, *output_section;
+ asection *s;
bfd_byte *loc;
bfd_vma got_address;
- int dynindx;
s = mips_elf_rel_dyn_section (info, FALSE);
- output_section = input_section->output_section;
- dynindx = elf_section_data (output_section)->dynindx;
got_address = (sgot->output_section->vma
+ sgot->output_offset
+ entry.gotidx);
loc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela));
outrel.r_offset = got_address;
- outrel.r_info = ELF32_R_INFO (dynindx, R_MIPS_32);
- outrel.r_addend = value - output_section->vma;
+ outrel.r_info = ELF32_R_INFO (STN_UNDEF, R_MIPS_32);
+ outrel.r_addend = value;
bfd_elf32_swap_reloca_out (abfd, &outrel, loc);
}
if (tcount > 0)
{
unsigned int primary_total = lcount + tcount + arg->global_count;
- if (primary_total * MIPS_ELF_GOT_SIZE (bfd2got->bfd)
- >= MIPS_ELF_GOT_MAX_SIZE (arg->info))
+ if (primary_total > maxcnt)
too_many_for_tls = TRUE;
}
struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
struct mips_got_info *g = p;
bfd_vma next_index;
+ unsigned char tls_type;
/* We're only interested in TLS symbols. */
if (entry->tls_type == 0)
return 1;
entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
entry->d.h->tls_got_offset = next_index;
+ tls_type = entry->d.h->tls_type;
}
else
{
g->tls_ldm_offset = next_index;
}
entry->gotidx = next_index;
+ tls_type = entry->tls_type;
}
/* Account for the entries we've just allocated. */
- if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+ if (tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
g->tls_assigned_gotno += 2;
- if (entry->tls_type & GOT_TLS_IE)
+ if (tls_type & GOT_TLS_IE)
g->tls_assigned_gotno += 1;
return 1;
}
/* We didn't find it. */
- bfd_set_error (bfd_error_bad_value);
return NULL;
}
a stub. */
if (r_type != R_MIPS16_26 && !info->relocatable
&& ((h != NULL && h->fn_stub != NULL)
- || (local_p && elf_tdata (input_bfd)->local_stubs != NULL
+ || (local_p
+ && elf_tdata (input_bfd)->local_stubs != NULL
&& elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
- && !mips_elf_stub_section_p (input_bfd, input_section))
+ && !mips16_stub_section_p (input_bfd, input_section))
{
/* This is a 32- or 64-bit call to a 16-bit function. We should
have already noticed that we were going to need the
/* If this is a 16-bit call to a 32- or 64-bit function with a stub, we
need to redirect the call to the stub. */
else if (r_type == R_MIPS16_26 && !info->relocatable
- && h != NULL
- && (h->call_stub != NULL || h->call_fp_stub != NULL)
+ && ((h != NULL && (h->call_stub != NULL || h->call_fp_stub != NULL))
+ || (local_p
+ && elf_tdata (input_bfd)->local_call_stubs != NULL
+ && elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
&& !target_is_16_bit_code_p)
{
- /* If both call_stub and call_fp_stub are defined, we can figure
- out which one to use by seeing which one appears in the input
- file. */
- if (h->call_stub != NULL && h->call_fp_stub != NULL)
+ if (local_p)
+ sec = elf_tdata (input_bfd)->local_call_stubs[r_symndx];
+ else
{
- asection *o;
-
- sec = NULL;
- for (o = input_bfd->sections; o != NULL; o = o->next)
+ /* If both call_stub and call_fp_stub are defined, we can figure
+ out which one to use by checking which one appears in the input
+ file. */
+ if (h->call_stub != NULL && h->call_fp_stub != NULL)
{
- if (CONST_STRNEQ (bfd_get_section_name (input_bfd, o),
- CALL_FP_STUB))
+ asection *o;
+
+ sec = NULL;
+ for (o = input_bfd->sections; o != NULL; o = o->next)
{
- sec = h->call_fp_stub;
- break;
+ if (CALL_FP_STUB_P (bfd_get_section_name (input_bfd, o)))
+ {
+ sec = h->call_fp_stub;
+ break;
+ }
}
+ if (sec == NULL)
+ sec = h->call_stub;
}
- if (sec == NULL)
+ else if (h->call_stub != NULL)
sec = h->call_stub;
- }
- else if (h->call_stub != NULL)
- sec = h->call_stub;
- else
- sec = h->call_fp_stub;
+ else
+ sec = h->call_fp_stub;
+ }
BFD_ASSERT (sec->size > 0);
symbol = sec->output_section->vma + sec->output_offset;
if (r_type == R_MIPS_TLS_LDM)
{
g = mips_elf_local_got_index (abfd, input_bfd, info,
- sec, 0, 0, NULL, r_type);
+ 0, 0, NULL, r_type);
if (g == MINUS_ONE)
return bfd_reloc_outofrange;
}
break;
else
{
- g = mips_elf_local_got_index (abfd, input_bfd, info, sec,
+ g = mips_elf_local_got_index (abfd, input_bfd, info,
symbol + addend, r_symndx, h, r_type);
if (g == MINUS_ONE)
return bfd_reloc_outofrange;
outrel.r_info = ELF32_R_INFO (h->root.dynindx, r_type);
outrel.r_addend = addend;
bfd_elf32_swap_reloca_out (abfd, &outrel, loc);
+
+ /* If we've written this relocation for a readonly section,
+ we need to set DF_TEXTREL again, so that we do not delete the
+ DT_TEXTREL tag. */
+ if (MIPS_ELF_READONLY_SECTION (input_section))
+ info->flags |= DF_TEXTREL;
+
*valuep = 0;
return bfd_reloc_ok;
}
break;
case R_MIPS_TLS_DTPREL_LO16:
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_DTPREL64:
value = (symbol + addend - dtprel_base (info)) & howto->dst_mask;
break;
forced = ! mips_elf_local_relocation_p (input_bfd, relocation,
local_sections, FALSE);
- value = mips_elf_got16_entry (abfd, input_bfd, info, sec,
+ value = mips_elf_got16_entry (abfd, input_bfd, info,
symbol + addend, forced);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
0. */
if (! local_p)
goto got_disp;
- value = mips_elf_got_page (abfd, input_bfd, info, sec,
- symbol + addend, NULL);
+ value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
value = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, value);
case R_MIPS_GOT_OFST:
if (local_p)
- mips_elf_got_page (abfd, input_bfd, info, sec,
- symbol + addend, &value);
+ mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value);
else
value = addend;
overflowed_p = mips_elf_overflow_p (value, 16);
/* Returns TRUE if SECTION is a MIPS16 stub section. */
static bfd_boolean
-mips_elf_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section)
+mips16_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section)
{
const char *name = bfd_get_section_name (abfd, section);
- return (CONST_STRNEQ (name, FN_STUB)
- || CONST_STRNEQ (name, CALL_STUB)
- || CONST_STRNEQ (name, CALL_FP_STUB));
+ return FN_STUB_P (name) || CALL_STUB_P (name) || CALL_FP_STUB_P (name);
}
\f
/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
outrel[0].r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section, rel[0].r_offset);
- outrel[1].r_offset =
- _bfd_elf_section_offset (output_bfd, info, input_section, rel[1].r_offset);
- outrel[2].r_offset =
- _bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
+ if (ABI_64_P (output_bfd))
+ {
+ outrel[1].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[1].r_offset);
+ outrel[2].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
+ }
if (outrel[0].r_offset == MINUS_ONE)
/* The relocation field has been deleted. */
else
{
indx = elf_section_data (sec->output_section)->dynindx;
+ if (indx == 0)
+ {
+ asection *osec = htab->root.text_index_section;
+ indx = elf_section_data (osec)->dynindx;
+ }
if (indx == 0)
abort ();
}
bfd_boolean
_bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
{
- register const char *name;
- unsigned int sh_type;
-
- name = bfd_get_section_name (abfd, sec);
- sh_type = hdr->sh_type;
+ const char *name = bfd_get_section_name (abfd, sec);
if (strcmp (name, ".liblist") == 0)
{
hdr->sh_entsize = 8;
}
- /* In the unlikely event a special section is empty it has to lose its
- special meaning. This may happen e.g. when using `strip' with the
- "--only-keep-debug" option. */
- if (sec->size > 0 && !(sec->flags & SEC_HAS_CONTENTS))
- hdr->sh_type = sh_type;
-
/* 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
non-default kind of relocations here, but only NewABI would use
/* Check for the mips16 stub sections. */
name = bfd_get_section_name (abfd, sec);
- if (CONST_STRNEQ (name, FN_STUB))
+ if (FN_STUB_P (name))
{
unsigned long r_symndx;
/* We can ignore stub sections when looking for relocs. */
if ((o->flags & SEC_RELOC) == 0
|| o->reloc_count == 0
- || CONST_STRNEQ (bfd_get_section_name (abfd, o), FN_STUB)
- || CONST_STRNEQ (bfd_get_section_name (abfd, o), CALL_STUB)
- || CONST_STRNEQ (bfd_get_section_name (abfd, o), CALL_FP_STUB))
+ || mips16_stub_section_p (abfd, o))
continue;
sec_relocs
elf_tdata (abfd)->local_stubs = n;
}
+ sec->flags |= SEC_KEEP;
elf_tdata (abfd)->local_stubs[r_symndx] = sec;
/* We don't need to set mips16_stubs_seen in this case.
/* H is the symbol this stub is for. */
+ /* If we already have an appropriate stub for this function, we
+ don't need another one, so we can discard this one. Since
+ this function is called before the linker maps input sections
+ to output sections, we can easily discard it by setting the
+ SEC_EXCLUDE flag. */
+ if (h->fn_stub != NULL)
+ {
+ sec->flags |= SEC_EXCLUDE;
+ return TRUE;
+ }
+
+ sec->flags |= SEC_KEEP;
h->fn_stub = sec;
mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
}
}
- else if (CONST_STRNEQ (name, CALL_STUB)
- || CONST_STRNEQ (name, CALL_FP_STUB))
+ else if (CALL_STUB_P (name) || CALL_FP_STUB_P (name))
{
unsigned long r_symndx;
struct mips_elf_link_hash_entry *h;
if (r_symndx < extsymoff
|| sym_hashes[r_symndx - extsymoff] == NULL)
{
- /* This stub was actually built for a static symbol defined
- in the same file. We assume that all static symbols in
- mips16 code are themselves mips16, so we can simply
- discard this stub. Since this function is called before
- the linker maps input sections to output sections, we can
- easily discard it by setting the SEC_EXCLUDE flag. */
- sec->flags |= SEC_EXCLUDE;
- return TRUE;
- }
+ asection *o;
- h = ((struct mips_elf_link_hash_entry *)
- sym_hashes[r_symndx - extsymoff]);
+ /* This stub is for a local symbol. This stub will only be
+ needed if there is some relocation (R_MIPS16_26) in this BFD
+ that refers to this symbol. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ Elf_Internal_Rela *sec_relocs;
+ const Elf_Internal_Rela *r, *rend;
- /* H is the symbol this stub is for. */
+ /* We can ignore stub sections when looking for relocs. */
+ if ((o->flags & SEC_RELOC) == 0
+ || o->reloc_count == 0
+ || mips16_stub_section_p (abfd, o))
+ continue;
- if (CONST_STRNEQ (name, CALL_FP_STUB))
- loc = &h->call_fp_stub;
- else
- loc = &h->call_stub;
-
- /* If we already have an appropriate stub for this function, we
- don't need another one, so we can discard this one. Since
- this function is called before the linker maps input sections
- to output sections, we can easily discard it by setting the
- SEC_EXCLUDE flag. We can also discard this section if we
- happen to already know that this is a mips16 function; it is
- not necessary to check this here, as it is checked later, but
- it is slightly faster to check now. */
- if (*loc != NULL || h->root.other == STO_MIPS16)
- {
- sec->flags |= SEC_EXCLUDE;
- return TRUE;
+ sec_relocs
+ = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL,
+ info->keep_memory);
+ if (sec_relocs == NULL)
+ return FALSE;
+
+ rend = sec_relocs + o->reloc_count;
+ for (r = sec_relocs; r < rend; r++)
+ if (ELF_R_SYM (abfd, r->r_info) == r_symndx
+ && ELF_R_TYPE (abfd, r->r_info) == R_MIPS16_26)
+ break;
+
+ if (elf_section_data (o)->relocs != sec_relocs)
+ free (sec_relocs);
+
+ if (r < rend)
+ break;
+ }
+
+ if (o == NULL)
+ {
+ /* There is no non-call reloc for this stub, so we do
+ not need it. Since this function is called before
+ the linker maps input sections to output sections, we
+ can easily discard it by setting the SEC_EXCLUDE
+ flag. */
+ sec->flags |= SEC_EXCLUDE;
+ return TRUE;
+ }
+
+ /* Record this stub in an array of local symbol call_stubs for
+ this BFD. */
+ if (elf_tdata (abfd)->local_call_stubs == NULL)
+ {
+ unsigned long symcount;
+ asection **n;
+ bfd_size_type amt;
+
+ if (elf_bad_symtab (abfd))
+ symcount = NUM_SHDR_ENTRIES (symtab_hdr);
+ else
+ symcount = symtab_hdr->sh_info;
+ amt = symcount * sizeof (asection *);
+ n = bfd_zalloc (abfd, amt);
+ if (n == NULL)
+ return FALSE;
+ elf_tdata (abfd)->local_call_stubs = n;
+ }
+
+ sec->flags |= SEC_KEEP;
+ elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
+
+ /* We don't need to set mips16_stubs_seen in this case.
+ That flag is used to see whether we need to look through
+ the global symbol table for stubs. We don't need to set
+ it here, because we just have a local stub. */
}
+ else
+ {
+ h = ((struct mips_elf_link_hash_entry *)
+ sym_hashes[r_symndx - extsymoff]);
+
+ /* H is the symbol this stub is for. */
+
+ if (CALL_FP_STUB_P (name))
+ loc = &h->call_fp_stub;
+ else
+ loc = &h->call_stub;
+
+ /* If we already have an appropriate stub for this function, we
+ don't need another one, so we can discard this one. Since
+ this function is called before the linker maps input sections
+ to output sections, we can easily discard it by setting the
+ SEC_EXCLUDE flag. */
+ if (*loc != NULL)
+ {
+ sec->flags |= SEC_EXCLUDE;
+ return TRUE;
+ }
- *loc = sec;
- mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
+ sec->flags |= SEC_KEEP;
+ *loc = sec;
+ mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
+ }
}
if (dynobj == NULL)
return FALSE;
}
mips_elf_allocate_dynamic_relocations (dynobj, info, 1);
+ if (MIPS_ELF_READONLY_SECTION (sec))
+ /* We tell the dynamic linker that there are
+ relocations against the text segment. */
+ info->flags |= DF_TEXTREL;
}
}
else if (r_type == R_MIPS_CALL_LO16
References from a stub section do not count. */
if (h != NULL
&& r_type != R_MIPS16_26
- && ! CONST_STRNEQ (bfd_get_section_name (abfd, sec), FN_STUB)
- && ! CONST_STRNEQ (bfd_get_section_name (abfd, sec), CALL_STUB)
- && ! CONST_STRNEQ (bfd_get_section_name (abfd, sec), CALL_FP_STUB))
+ && !mips16_stub_section_p (abfd, sec))
{
struct mips_elf_link_hash_entry *mh;
bfd *dynobj;
struct mips_elf_link_hash_entry *hmips;
struct mips_elf_link_hash_table *htab;
- unsigned int power_of_two;
htab = mips_elf_hash_table (info);
dynobj = elf_hash_table (info)->dynobj;
h->needs_copy = 1;
}
- /* We need to figure out the alignment required for this symbol. */
- power_of_two = bfd_log2 (h->size);
- if (power_of_two > 4)
- power_of_two = 4;
-
- /* Apply the required alignment. */
- htab->sdynbss->size = BFD_ALIGN (htab->sdynbss->size,
- (bfd_size_type) 1 << power_of_two);
- if (power_of_two > bfd_get_section_alignment (dynobj, htab->sdynbss)
- && !bfd_set_section_alignment (dynobj, htab->sdynbss, power_of_two))
- return FALSE;
-
- /* Define the symbol as being at this point in the section. */
- h->root.u.def.section = htab->sdynbss;
- h->root.u.def.value = htab->sdynbss->size;
-
- /* Increment the section size to make room for the symbol. */
- htab->sdynbss->size += h->size;
-
- return TRUE;
+ return _bfd_elf_adjust_dynamic_copy (h, htab->sdynbss);
}
\f
/* Return the number of dynamic section symbols required by OUTPUT_BFD.
/* Add some entries to the .dynamic section. We fill in the
values later, in _bfd_mips_elf_finish_dynamic_sections, but we
must add the entries now so that we get the correct size for
- the .dynamic section. The DT_DEBUG entry is filled in by the
- dynamic linker and used by the debugger. */
- if (! info->shared)
- {
- /* SGI object has the equivalence of DT_DEBUG in the
- DT_MIPS_RLD_MAP entry. */
- if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_MAP, 0))
- return FALSE;
- if (!SGI_COMPAT (output_bfd))
- {
- if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
- return FALSE;
- }
- }
- else
- {
- /* Shared libraries on traditional mips have DT_DEBUG. */
- if (!SGI_COMPAT (output_bfd))
- {
- if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
- return FALSE;
- }
- }
+ the .dynamic section. */
+
+ /* SGI object has the equivalence of DT_DEBUG in the
+ DT_MIPS_RLD_MAP entry. This must come first because glibc
+ only fills in DT_MIPS_RLD_MAP (not DT_DEBUG) and GDB only
+ looks at the first one it sees. */
+ if (!info->shared
+ && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_MAP, 0))
+ return FALSE;
+
+ /* The DT_DEBUG entry may be filled in by the dynamic linker and
+ used by the debugger. */
+ if (info->executable
+ && !SGI_COMPAT (output_bfd)
+ && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+ return FALSE;
if (reltext && (SGI_COMPAT (output_bfd) || htab->is_vxworks))
info->flags |= DF_TEXTREL;
bfd_boolean rela_relocation_p = TRUE;
unsigned int r_type = ELF_R_TYPE (output_bfd, rel->r_info);
const char *msg;
+ unsigned long r_symndx;
+ asection *sec;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry *h;
/* Find the relocation howto for this relocation. */
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type,
+ NEWABI_P (input_bfd)
+ && (MIPS_RELOC_RELA_P
+ (input_bfd, input_section,
+ rel - relocs)));
+
+ r_symndx = ELF_R_SYM (input_bfd, rel->r_info);
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ if (mips_elf_local_relocation_p (input_bfd, rel, local_sections, FALSE))
+ {
+ sec = local_sections[r_symndx];
+ h = NULL;
+ }
+ else
+ {
+ unsigned long extsymoff;
+
+ extsymoff = 0;
+ if (!elf_bad_symtab (input_bfd))
+ extsymoff = symtab_hdr->sh_info;
+ h = elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ sec = NULL;
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ sec = h->root.u.def.section;
+ }
+
+ if (sec != NULL && elf_discarded_section (sec))
+ {
+ /* For relocs against symbols from removed linkonce sections,
+ or sections discarded by a linker script, we just want the
+ section contents zeroed. Avoid any special processing. */
+ _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+ rel->r_info = 0;
+ rel->r_addend = 0;
+ continue;
+ }
+
if (r_type == R_MIPS_64 && ! NEWABI_P (input_bfd))
{
/* Some 32-bit code uses R_MIPS_64. In particular, people use
if (bfd_big_endian (input_bfd))
rel->r_offset += 4;
}
- else
- /* NewABI defaults to RELA relocations. */
- howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type,
- NEWABI_P (input_bfd)
- && (MIPS_RELOC_RELA_P
- (input_bfd, input_section,
- rel - relocs)));
if (!use_saved_addend_p)
{
&& mips_elf_local_relocation_p (input_bfd, rel,
local_sections, FALSE)))
{
- bfd_vma l;
const Elf_Internal_Rela *lo16_relocation;
reloc_howto_type *lo16_howto;
- bfd_byte *lo16_location;
int lo16_type;
if (r_type == R_MIPS16_HI16)
several relocations for the same address. In
that case, the R_MIPS_LO16 relocation may occur
as one of these. We permit a similar extension
- in general, as that is useful for GCC. */
+ in general, as that is useful for GCC.
+
+ In some cases GCC dead code elimination removes
+ the LO16 but keeps the corresponding HI16. This
+ is strictly speaking a violation of the ABI but
+ not immediately harmful. */
lo16_relocation = mips_elf_next_relocation (input_bfd,
lo16_type,
rel, relend);
if (lo16_relocation == NULL)
- return FALSE;
-
- lo16_location = contents + lo16_relocation->r_offset;
-
- /* Obtain the addend kept there. */
- lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd,
- lo16_type, FALSE);
- _bfd_mips16_elf_reloc_unshuffle (input_bfd, lo16_type, FALSE,
- lo16_location);
- l = mips_elf_obtain_contents (lo16_howto, lo16_relocation,
- input_bfd, contents);
- _bfd_mips16_elf_reloc_shuffle (input_bfd, lo16_type, FALSE,
- lo16_location);
- l &= lo16_howto->src_mask;
- l <<= lo16_howto->rightshift;
- l = _bfd_mips_elf_sign_extend (l, 16);
-
- addend <<= 16;
-
- /* Compute the combined addend. */
- addend += l;
+ {
+ const char *name;
+
+ if (h)
+ name = h->root.root.string;
+ else
+ name = bfd_elf_sym_name (input_bfd, symtab_hdr,
+ local_syms + r_symndx,
+ sec);
+ (*_bfd_error_handler)
+ (_("%B: Can't find matching LO16 reloc against `%s' for %s at 0x%lx in section `%A'"),
+ input_bfd, input_section, name, howto->name,
+ rel->r_offset);
+ }
+ else
+ {
+ bfd_byte *lo16_location;
+ bfd_vma l;
+
+ lo16_location = contents + lo16_relocation->r_offset;
+
+ /* Obtain the addend kept there. */
+ lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd,
+ lo16_type, FALSE);
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, lo16_type,
+ FALSE, lo16_location);
+ l = mips_elf_obtain_contents (lo16_howto,
+ lo16_relocation,
+ input_bfd, contents);
+ _bfd_mips16_elf_reloc_shuffle (input_bfd, lo16_type,
+ FALSE, lo16_location);
+ l &= lo16_howto->src_mask;
+ l <<= lo16_howto->rightshift;
+ l = _bfd_mips_elf_sign_extend (l, 16);
+
+ addend <<= 16;
+
+ /* Compute the combined addend. */
+ addend += l;
+ }
}
else
addend <<= howto->rightshift;
;
else
{
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
BFD_ASSERT (name != NULL);
+ if (!htab->small_data_overflow_reported
+ && (howto->type == R_MIPS_GPREL16
+ || howto->type == R_MIPS_LITERAL))
+ {
+ const char *msg =
+ _("small-data section exceeds 64KB;"
+ " lower small-data size limit (see option -G)");
+
+ htab->small_data_overflow_reported = TRUE;
+ (*info->callbacks->einfo) ("%P: %s\n", msg);
+ }
if (! ((*info->callbacks->reloc_overflow)
(info, NULL, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
* (ABI_64_P (output_bfd)
? sizeof (Elf64_Mips_External_Rel)
: sizeof (Elf32_External_Rel)));
+ /* Adjust the section size too. Tools like the prelinker
+ can reasonably expect the values to the same. */
+ elf_section_data (s->output_section)->this_hdr.sh_size
+ = dyn.d_un.d_val;
break;
default:
&& bfd_get_section_by_name (abfd, ".mdebug"))
++ret;
+ /* Allocate a PT_NULL header in dynamic objects. See
+ _bfd_mips_elf_modify_segment_map for details. */
+ if (!SGI_COMPAT (abfd)
+ && bfd_get_section_by_name (abfd, ".dynamic"))
+ ++ret;
+
return ret;
}
m->p_flags_valid = 1;
}
}
- if (m != NULL
- && m->count == 1 && strcmp (m->sections[0]->name, ".dynamic") == 0)
+ /* GNU/Linux binaries do not need the extended PT_DYNAMIC section.
+ glibc's dynamic linker has traditionally derived the number of
+ tags from the p_filesz field, and sometimes allocates stack
+ arrays of that size. An overly-big PT_DYNAMIC segment can
+ be actively harmful in such cases. Making PT_DYNAMIC contain
+ other sections can also make life hard for the prelinker,
+ which might move one of the other sections to a different
+ PT_LOAD segment. */
+ if (SGI_COMPAT (abfd)
+ && m != NULL
+ && m->count == 1
+ && strcmp (m->sections[0]->name, ".dynamic") == 0)
{
static const char *sec_names[] =
{
}
}
+ /* Allocate a spare program header in dynamic objects so that tools
+ like the prelinker can add an extra PT_LOAD entry.
+
+ If the prelinker needs to make room for a new PT_LOAD entry, its
+ standard procedure is to move the first (read-only) sections into
+ the new (writable) segment. However, the MIPS ABI requires
+ .dynamic to be in a read-only segment, and the section will often
+ start within sizeof (ElfNN_Phdr) bytes of the last program header.
+
+ Although the prelinker could in principle move .dynamic to a
+ writable segment, it seems better to allocate a spare program
+ header instead, and avoid the need to move any sections.
+ There is a long tradition of allocating spare dynamic tags,
+ so allocating a spare program header seems like a natural
+ extension. */
+ if (!SGI_COMPAT (abfd)
+ && bfd_get_section_by_name (abfd, ".dynamic"))
+ {
+ for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next)
+ if ((*pm)->p_type == PT_NULL)
+ break;
+ if (*pm == NULL)
+ {
+ m = bfd_zalloc (abfd, sizeof (*m));
+ if (m == NULL)
+ return FALSE;
+
+ m->p_type = PT_NULL;
+ *pm = m;
+ }
+ }
+
return TRUE;
}
\f
asection *
_bfd_mips_elf_gc_mark_hook (asection *sec,
- struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info,
Elf_Internal_Rela *rel,
struct elf_link_hash_entry *h,
Elf_Internal_Sym *sym)
/* ??? Do mips16 stub sections need to be handled special? */
if (h != NULL)
- {
- switch (ELF_R_TYPE (sec->owner, rel->r_info))
- {
- case R_MIPS_GNU_VTINHERIT:
- case R_MIPS_GNU_VTENTRY:
- break;
-
- default:
- switch (h->root.type)
- {
- case bfd_link_hash_defined:
- case bfd_link_hash_defweak:
- return h->root.u.def.section;
-
- case bfd_link_hash_common:
- return h->root.u.c.p->section;
-
- default:
- break;
- }
- }
- }
- else
- return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+ switch (ELF_R_TYPE (sec->owner, rel->r_info))
+ {
+ case R_MIPS_GNU_VTINHERIT:
+ case R_MIPS_GNU_VTENTRY:
+ return NULL;
+ }
- return NULL;
+ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
}
/* Update the got entry reference counts for the section being removed. */
}
bfd_boolean
-_bfd_mips_elf_write_section (bfd *output_bfd, asection *sec,
- bfd_byte *contents)
+_bfd_mips_elf_write_section (bfd *output_bfd,
+ struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ asection *sec, bfd_byte *contents)
{
bfd_byte *to, *from, *end;
int i;
ret->rld_value = 0;
ret->mips16_stubs_seen = FALSE;
ret->is_vxworks = FALSE;
+ ret->small_data_overflow_reported = FALSE;
ret->srelbss = NULL;
ret->sdynbss = NULL;
ret->srelplt = NULL;
}
+/* Merge object attributes from IBFD into OBFD. Raise an error if
+ there are conflicting attributes. */
+static bfd_boolean
+mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
+{
+ obj_attribute *in_attr;
+ obj_attribute *out_attr;
+
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ elf_known_obj_attributes_proc (obfd)[0].i = 1;
+
+ return TRUE;
+ }
+
+ /* Check for conflicting Tag_GNU_MIPS_ABI_FP attributes and merge
+ non-conflicting ones. */
+ in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+ out_attr = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+ if (in_attr[Tag_GNU_MIPS_ABI_FP].i != out_attr[Tag_GNU_MIPS_ABI_FP].i)
+ {
+ out_attr[Tag_GNU_MIPS_ABI_FP].type = 1;
+ if (out_attr[Tag_GNU_MIPS_ABI_FP].i == 0)
+ out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+ else if (in_attr[Tag_GNU_MIPS_ABI_FP].i == 0)
+ ;
+ else if (in_attr[Tag_GNU_MIPS_ABI_FP].i > 3)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown floating point ABI %d"), ibfd,
+ in_attr[Tag_GNU_MIPS_ABI_FP].i);
+ else if (out_attr[Tag_GNU_MIPS_ABI_FP].i > 3)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown floating point ABI %d"), obfd,
+ out_attr[Tag_GNU_MIPS_ABI_FP].i);
+ else
+ switch (out_attr[Tag_GNU_MIPS_ABI_FP].i)
+ {
+ case 1:
+ switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+ {
+ case 2:
+ _bfd_error_handler
+ (_("Warning: %B uses -msingle-float, %B uses -mdouble-float"),
+ obfd, ibfd);
+
+ case 3:
+ _bfd_error_handler
+ (_("Warning: %B uses hard float, %B uses soft float"),
+ obfd, ibfd);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case 2:
+ switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+ {
+ case 1:
+ _bfd_error_handler
+ (_("Warning: %B uses -msingle-float, %B uses -mdouble-float"),
+ ibfd, obfd);
+
+ case 3:
+ _bfd_error_handler
+ (_("Warning: %B uses hard float, %B uses soft float"),
+ obfd, ibfd);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case 3:
+ switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+ {
+ case 1:
+ case 2:
+ _bfd_error_handler
+ (_("Warning: %B uses hard float, %B uses soft float"),
+ ibfd, obfd);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+ return TRUE;
+}
+
/* Merge backend specific data from an object file to the output
object file when linking. */
return FALSE;
}
+ if (!mips_elf_merge_obj_attributes (ibfd, obfd))
+ return FALSE;
+
new_flags = elf_elfheader (ibfd)->e_flags;
elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
old_flags = elf_elfheader (obfd)->e_flags;
fprintf (file, _(" [no abi set]"));
if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1)
- fprintf (file, _(" [mips1]"));
+ fprintf (file, " [mips1]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2)
- fprintf (file, _(" [mips2]"));
+ fprintf (file, " [mips2]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_3)
- fprintf (file, _(" [mips3]"));
+ fprintf (file, " [mips3]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_4)
- fprintf (file, _(" [mips4]"));
+ fprintf (file, " [mips4]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_5)
- fprintf (file, _(" [mips5]"));
+ fprintf (file, " [mips5]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32)
- fprintf (file, _(" [mips32]"));
+ fprintf (file, " [mips32]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64)
- fprintf (file, _(" [mips64]"));
+ fprintf (file, " [mips64]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2)
- fprintf (file, _(" [mips32r2]"));
+ fprintf (file, " [mips32r2]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64R2)
- fprintf (file, _(" [mips64r2]"));
+ fprintf (file, " [mips64r2]");
else
fprintf (file, _(" [unknown ISA]"));
if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX)
- fprintf (file, _(" [mdmx]"));
+ fprintf (file, " [mdmx]");
if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16)
- fprintf (file, _(" [mips16]"));
+ fprintf (file, " [mips16]");
if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
- fprintf (file, _(" [32bitmode]"));
+ fprintf (file, " [32bitmode]");
else
fprintf (file, _(" [not 32bitmode]"));
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_NOREORDER)
+ fprintf (file, " [noreorder]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_PIC)
+ fprintf (file, " [PIC]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_CPIC)
+ fprintf (file, " [CPIC]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_XGOT)
+ fprintf (file, " [XGOT]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_UCODE)
+ fprintf (file, " [UCODE]");
+
fputc ('\n', file);
return TRUE;