#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elfxx-mips.h"
#include "elf/mips.h"
#include "coff/sym.h"
#include "coff/symconst.h"
#include "coff/ecoff.h"
#include "coff/mips.h"
struct mips_got_info
{
struct elf_link_hash_entry *global_gotsym;
unsigned int global_gotno;
unsigned int local_gotno;
unsigned int assigned_gotno;
};
struct mips_elf_hash_sort_data
{
struct elf_link_hash_entry *low;
long min_got_dynindx;
long max_non_got_dynindx;
};
struct mips_elf_link_hash_entry
{
struct elf_link_hash_entry root;
EXTR esym;
unsigned int possibly_dynamic_relocs;
boolean readonly_reloc;
unsigned int min_dyn_reloc_index;
boolean no_fn_stub;
asection *fn_stub;
boolean need_fn_stub;
asection *call_stub;
asection *call_fp_stub;
boolean forced_local;
};
struct mips_elf_link_hash_table
{
struct elf_link_hash_table root;
#if 0
bfd_size_type dynsym_sec_strindex[SIZEOF_MIPS_DYNSYM_SECNAMES];
#endif
bfd_size_type procedure_count;
bfd_size_type compact_rel_size;
boolean use_rld_obj_head;
bfd_vma rld_value;
boolean mips16_stubs_seen;
};
struct extsym_info
{
bfd *abfd;
struct bfd_link_info *info;
struct ecoff_debug_info *debug;
const struct ecoff_debug_swap *swap;
boolean failed;
};
static const char * const mips_elf_dynsym_rtproc_names[] =
{
"_procedure_table",
"_procedure_string_table",
"_procedure_table_size",
NULL
};
typedef struct
{
unsigned long id1;
unsigned long num;
unsigned long id2;
unsigned long offset;
unsigned long reserved0;
unsigned long reserved1;
} Elf32_compact_rel;
typedef struct
{
bfd_byte id1[4];
bfd_byte num[4];
bfd_byte id2[4];
bfd_byte offset[4];
bfd_byte reserved0[4];
bfd_byte reserved1[4];
} Elf32_External_compact_rel;
typedef struct
{
unsigned int ctype : 1;
unsigned int rtype : 4;
unsigned int dist2to : 8;
unsigned int relvaddr : 19;
unsigned long konst;
unsigned long vaddr;
} Elf32_crinfo;
typedef struct
{
unsigned int ctype : 1;
unsigned int rtype : 4;
unsigned int dist2to : 8;
unsigned int relvaddr : 19;
unsigned long konst;
} Elf32_crinfo2;
typedef struct
{
bfd_byte info[4];
bfd_byte konst[4];
bfd_byte vaddr[4];
} Elf32_External_crinfo;
typedef struct
{
bfd_byte info[4];
bfd_byte konst[4];
} Elf32_External_crinfo2;
#define CRINFO_CTYPE (0x1)
#define CRINFO_CTYPE_SH (31)
#define CRINFO_RTYPE (0xf)
#define CRINFO_RTYPE_SH (27)
#define CRINFO_DIST2TO (0xff)
#define CRINFO_DIST2TO_SH (19)
#define CRINFO_RELVADDR (0x7ffff)
#define CRINFO_RELVADDR_SH (0)
#define CRF_MIPS_LONG 1
#define CRF_MIPS_SHORT 0
#define CRT_MIPS_REL32 0xa
#define CRT_MIPS_WORD 0xb
#define CRT_MIPS_GPHI_LO 0xc
#define CRT_MIPS_JMPAD 0xd
#define mips_elf_set_cr_format(x,format) ((x).ctype = (format))
#define mips_elf_set_cr_type(x,type) ((x).rtype = (type))
#define mips_elf_set_cr_dist2to(x,v) ((x).dist2to = (v))
#define mips_elf_set_cr_relvaddr(x,d) ((x).relvaddr = (d)<<2)
typedef struct runtime_pdr {
bfd_vma adr;
long regmask;
long regoffset;
long fregmask;
long fregoffset;
long frameoffset;
short framereg;
short pcreg;
long irpss;
long reserved;
struct exception_info *exception_info;
} RPDR, *pRPDR;
#define cbRPDR sizeof (RPDR)
#define rpdNil ((pRPDR) 0)
static struct bfd_hash_entry *mips_elf_link_hash_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static void ecoff_swap_rpdr_out
PARAMS ((bfd *, const RPDR *, struct rpdr_ext *));
static boolean mips_elf_create_procedure_table
PARAMS ((PTR, bfd *, struct bfd_link_info *, asection *,
struct ecoff_debug_info *));
static boolean mips_elf_check_mips16_stubs
PARAMS ((struct mips_elf_link_hash_entry *, PTR));
static void bfd_mips_elf32_swap_gptab_in
PARAMS ((bfd *, const Elf32_External_gptab *, Elf32_gptab *));
static void bfd_mips_elf32_swap_gptab_out
PARAMS ((bfd *, const Elf32_gptab *, Elf32_External_gptab *));
static void bfd_elf32_swap_compact_rel_out
PARAMS ((bfd *, const Elf32_compact_rel *, Elf32_External_compact_rel *));
static void bfd_elf32_swap_crinfo_out
PARAMS ((bfd *, const Elf32_crinfo *, Elf32_External_crinfo *));
#if 0
static void bfd_mips_elf_swap_msym_in
PARAMS ((bfd *, const Elf32_External_Msym *, Elf32_Internal_Msym *));
#endif
static void bfd_mips_elf_swap_msym_out
PARAMS ((bfd *, const Elf32_Internal_Msym *, Elf32_External_Msym *));
static int sort_dynamic_relocs
PARAMS ((const void *, const void *));
static boolean mips_elf_output_extsym
PARAMS ((struct mips_elf_link_hash_entry *, PTR));
static int gptab_compare PARAMS ((const void *, const void *));
static asection * mips_elf_got_section PARAMS ((bfd *));
static struct mips_got_info *mips_elf_got_info
PARAMS ((bfd *, asection **));
static bfd_vma mips_elf_local_got_index
PARAMS ((bfd *, struct bfd_link_info *, bfd_vma));
static bfd_vma mips_elf_global_got_index
PARAMS ((bfd *, struct elf_link_hash_entry *));
static bfd_vma mips_elf_got_page
PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *));
static bfd_vma mips_elf_got16_entry
PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, boolean));
static bfd_vma mips_elf_got_offset_from_index
PARAMS ((bfd *, bfd *, bfd_vma));
static bfd_vma mips_elf_create_local_got_entry
PARAMS ((bfd *, struct mips_got_info *, asection *, bfd_vma));
static boolean mips_elf_sort_hash_table
PARAMS ((struct bfd_link_info *, unsigned long));
static boolean mips_elf_sort_hash_table_f
PARAMS ((struct mips_elf_link_hash_entry *, PTR));
static boolean mips_elf_record_global_got_symbol
PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *,
struct mips_got_info *));
static const Elf_Internal_Rela *mips_elf_next_relocation
PARAMS ((bfd *, unsigned int, const Elf_Internal_Rela *,
const Elf_Internal_Rela *));
static boolean mips_elf_local_relocation_p
PARAMS ((bfd *, const Elf_Internal_Rela *, asection **, boolean));
static bfd_vma mips_elf_sign_extend PARAMS ((bfd_vma, int));
static boolean mips_elf_overflow_p PARAMS ((bfd_vma, int));
static bfd_vma mips_elf_high PARAMS ((bfd_vma));
static bfd_vma mips_elf_higher PARAMS ((bfd_vma));
static bfd_vma mips_elf_highest PARAMS ((bfd_vma));
static boolean mips_elf_create_compact_rel_section
PARAMS ((bfd *, struct bfd_link_info *));
static boolean mips_elf_create_got_section
PARAMS ((bfd *, struct bfd_link_info *));
static asection *mips_elf_create_msym_section
PARAMS ((bfd *));
static bfd_reloc_status_type mips_elf_calculate_relocation
PARAMS ((bfd *, bfd *, asection *, struct bfd_link_info *,
const Elf_Internal_Rela *, bfd_vma, reloc_howto_type *,
Elf_Internal_Sym *, asection **, bfd_vma *, const char **,
boolean *, boolean));
static bfd_vma mips_elf_obtain_contents
PARAMS ((reloc_howto_type *, const Elf_Internal_Rela *, bfd *, bfd_byte *));
static boolean mips_elf_perform_relocation
PARAMS ((struct bfd_link_info *, reloc_howto_type *,
const Elf_Internal_Rela *, bfd_vma, bfd *, asection *, bfd_byte *,
boolean));
static boolean mips_elf_stub_section_p
PARAMS ((bfd *, asection *));
static void mips_elf_allocate_dynamic_relocations
PARAMS ((bfd *, unsigned int));
static boolean mips_elf_create_dynamic_relocation
PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
struct mips_elf_link_hash_entry *, asection *,
bfd_vma, bfd_vma *, asection *));
static INLINE int elf_mips_isa PARAMS ((flagword));
static INLINE char* elf_mips_abi_name PARAMS ((bfd *));
static void mips_elf_irix6_finish_dynamic_symbol
PARAMS ((bfd *, const char *, Elf_Internal_Sym *));
static boolean _bfd_mips_elf_mach_extends_p PARAMS ((flagword, flagword));
static bfd *reldyn_sorting_bfd;
#define ABI_N32_P(abfd) \
((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
#define ABI_64_P(abfd) \
(get_elf_backend_data (abfd)->s->elfclass == ELFCLASS64)
#define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd))
#define IRIX_COMPAT(abfd) \
(get_elf_backend_data (abfd)->elf_backend_mips_irix_compat (abfd))
#define SGI_COMPAT(abfd) \
(IRIX_COMPAT (abfd) != ict_none)
#define MIPS_ELF_OPTIONS_SECTION_NAME(abfd) \
(ABI_64_P (abfd) ? ".MIPS.options" : ".options")
#define MIPS_ELF_STUB_SECTION_NAME(abfd) \
(ABI_64_P (abfd) ? ".MIPS.stubs" : ".stub")
#define MIPS_ELF_REL_SIZE(abfd) \
(get_elf_backend_data (abfd)->s->sizeof_rel)
#define MIPS_ELF_DYN_SIZE(abfd) \
(get_elf_backend_data (abfd)->s->sizeof_dyn)
#define MIPS_ELF_GOT_SIZE(abfd) \
(get_elf_backend_data (abfd)->s->arch_size / 8)
#define MIPS_ELF_SYM_SIZE(abfd) \
(get_elf_backend_data (abfd)->s->sizeof_sym)
#define MIPS_ELF_LOG_FILE_ALIGN(abfd) \
(get_elf_backend_data (abfd)->s->file_align == 8 ? 3 : 2)
#define MIPS_ELF_GET_WORD(abfd, ptr) \
(ABI_64_P (abfd) ? bfd_get_64 (abfd, ptr) : bfd_get_32 (abfd, ptr))
#define MIPS_ELF_PUT_WORD(abfd, val, ptr) \
(ABI_64_P (abfd) \
? bfd_put_64 (abfd, val, ptr) \
: bfd_put_32 (abfd, val, ptr))
#ifdef BFD64
#define MIPS_ELF_ADD_DYNAMIC_ENTRY(info, tag, val) \
(ABI_64_P (elf_hash_table (info)->dynobj) \
? bfd_elf64_add_dynamic_entry (info, (bfd_vma) tag, (bfd_vma) val) \
: bfd_elf32_add_dynamic_entry (info, (bfd_vma) tag, (bfd_vma) val))
#else
#define MIPS_ELF_ADD_DYNAMIC_ENTRY(info, tag, val) \
(ABI_64_P (elf_hash_table (info)->dynobj) \
? (boolean) (abort (), false) \
: bfd_elf32_add_dynamic_entry (info, (bfd_vma) tag, (bfd_vma) val))
#endif
#define MIPS_ELF_RTYPE_TO_HOWTO(abfd, rtype, rela) \
(get_elf_backend_data (abfd)->elf_backend_mips_rtype_to_howto (rtype, rela))
#define MINUS_ONE (((bfd_vma)0) - 1)
#define MIPS_RESERVED_GOTNO (2)
#define ELF_MIPS_GP_OFFSET(abfd) (SGI_COMPAT (abfd) ? 0x7ff0 : 0x8000)
#define STUB_LW(abfd) \
(SGI_COMPAT (abfd) \
? (ABI_64_P (abfd) \
? 0xdf998010 \
: 0x8f998010) \
: 0x8f998010)
#define STUB_MOVE(abfd) \
(SGI_COMPAT (abfd) ? 0x03e07825 : 0x03e07821)
#define STUB_JALR 0x0320f809
#define STUB_LI16(abfd) \
(SGI_COMPAT (abfd) ? 0x34180000 : 0x24180000)
#define MIPS_FUNCTION_STUB_SIZE (16)
#define ELF_DYNAMIC_INTERPRETER(abfd) \
(ABI_N32_P (abfd) ? "/usr/lib32/libc.so.1" \
: ABI_64_P (abfd) ? "/usr/lib64/libc.so.1" \
: "/usr/lib/libc.so.1")
#ifdef BFD64
#define ELF_R_SYM(bfd, i) \
(ABI_64_P (bfd) ? ELF64_R_SYM (i) : ELF32_R_SYM (i))
#define ELF_R_TYPE(bfd, i) \
(ABI_64_P (bfd) ? ELF64_MIPS_R_TYPE (i) : ELF32_R_TYPE (i))
#define ELF_R_INFO(bfd, s, t) \
(ABI_64_P (bfd) ? ELF64_R_INFO (s, t) : ELF32_R_INFO (s, t))
#else
#define ELF_R_SYM(bfd, i) \
(ELF32_R_SYM (i))
#define ELF_R_TYPE(bfd, i) \
(ELF32_R_TYPE (i))
#define ELF_R_INFO(bfd, s, t) \
(ELF32_R_INFO (s, t))
#endif
#define FN_STUB ".mips16.fn."
#define CALL_STUB ".mips16.call."
#define CALL_FP_STUB ".mips16.call.fp."
#define mips_elf_link_hash_lookup(table, string, create, copy, follow) \
((struct mips_elf_link_hash_entry *) \
elf_link_hash_lookup (&(table)->root, (string), (create), \
(copy), (follow)))
#define mips_elf_link_hash_traverse(table, func, info) \
(elf_link_hash_traverse \
(&(table)->root, \
(boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
(info)))
#define mips_elf_hash_table(p) \
((struct mips_elf_link_hash_table *) ((p)->hash))
static struct bfd_hash_entry *
mips_elf_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct mips_elf_link_hash_entry *ret =
(struct mips_elf_link_hash_entry *) entry;
if (ret == (struct mips_elf_link_hash_entry *) NULL)
ret = ((struct mips_elf_link_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct mips_elf_link_hash_entry)));
if (ret == (struct mips_elf_link_hash_entry *) NULL)
return (struct bfd_hash_entry *) ret;
ret = ((struct mips_elf_link_hash_entry *)
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
table, string));
if (ret != (struct mips_elf_link_hash_entry *) NULL)
{
memset (&ret->esym, 0, sizeof (EXTR));
ret->esym.ifd = -2;
ret->possibly_dynamic_relocs = 0;
ret->readonly_reloc = false;
ret->min_dyn_reloc_index = 0;
ret->no_fn_stub = false;
ret->fn_stub = NULL;
ret->need_fn_stub = false;
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
ret->forced_local = false;
}
return (struct bfd_hash_entry *) ret;
}
boolean
_bfd_mips_elf_read_ecoff_info (abfd, section, debug)
bfd *abfd;
asection *section;
struct ecoff_debug_info *debug;
{
HDRR *symhdr;
const struct ecoff_debug_swap *swap;
char *ext_hdr = NULL;
swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
memset (debug, 0, sizeof (*debug));
ext_hdr = (char *) bfd_malloc (swap->external_hdr_size);
if (ext_hdr == NULL && swap->external_hdr_size != 0)
goto error_return;
if (! bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0,
swap->external_hdr_size))
goto error_return;
symhdr = &debug->symbolic_header;
(*swap->swap_hdr_in) (abfd, ext_hdr, symhdr);
#define READ(ptr, offset, count, size, type) \
if (symhdr->count == 0) \
debug->ptr = NULL; \
else \
{ \
bfd_size_type amt = (bfd_size_type) size * symhdr->count; \
debug->ptr = (type) bfd_malloc (amt); \
if (debug->ptr == NULL) \
goto error_return; \
if (bfd_seek (abfd, (file_ptr) symhdr->offset, SEEK_SET) != 0 \
|| bfd_bread (debug->ptr, amt, abfd) != amt) \
goto error_return; \
}
READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR);
READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR);
READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR);
READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR);
READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
union aux_ext *);
READ (ss, cbSsOffset, issMax, sizeof (char), char *);
READ (ssext, cbSsExtOffset, issExtMax, sizeof (char), char *);
READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR);
READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR);
READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size, PTR);
#undef READ
debug->fdr = NULL;
debug->adjust = NULL;
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);
return false;
}
static void
ecoff_swap_rpdr_out (abfd, in, ex)
bfd *abfd;
const RPDR *in;
struct rpdr_ext *ex;
{
H_PUT_S32 (abfd, in->adr, ex->p_adr);
H_PUT_32 (abfd, in->regmask, ex->p_regmask);
H_PUT_32 (abfd, in->regoffset, ex->p_regoffset);
H_PUT_32 (abfd, in->fregmask, ex->p_fregmask);
H_PUT_32 (abfd, in->fregoffset, ex->p_fregoffset);
H_PUT_32 (abfd, in->frameoffset, ex->p_frameoffset);
H_PUT_16 (abfd, in->framereg, ex->p_framereg);
H_PUT_16 (abfd, in->pcreg, ex->p_pcreg);
H_PUT_32 (abfd, in->irpss, ex->p_irpss);
#if 0
H_PUT_S32 (abfd, in->exception_info, ex->p_exception_info);
#endif
}
static boolean
mips_elf_create_procedure_table (handle, abfd, info, s, debug)
PTR handle;
bfd *abfd;
struct bfd_link_info *info;
asection *s;
struct ecoff_debug_info *debug;
{
const struct ecoff_debug_swap *swap;
HDRR *hdr = &debug->symbolic_header;
RPDR *rpdr, *rp;
struct rpdr_ext *erp;
PTR rtproc;
struct pdr_ext *epdr;
struct sym_ext *esym;
char *ss, **sv;
char *str;
bfd_size_type size;
bfd_size_type count;
unsigned long sindex;
unsigned long i;
PDR pdr;
SYMR sym;
const char *no_name_func = _("static procedure (no name)");
epdr = NULL;
rpdr = NULL;
esym = NULL;
ss = NULL;
sv = NULL;
swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
sindex = strlen (no_name_func) + 1;
count = hdr->ipdMax;
if (count > 0)
{
size = swap->external_pdr_size;
epdr = (struct pdr_ext *) bfd_malloc (size * count);
if (epdr == NULL)
goto error_return;
if (! _bfd_ecoff_get_accumulated_pdr (handle, (PTR) epdr))
goto error_return;
size = sizeof (RPDR);
rp = rpdr = (RPDR *) bfd_malloc (size * count);
if (rpdr == NULL)
goto error_return;
size = sizeof (char *);
sv = (char **) bfd_malloc (size * count);
if (sv == NULL)
goto error_return;
count = hdr->isymMax;
size = swap->external_sym_size;
esym = (struct sym_ext *) bfd_malloc (size * count);
if (esym == NULL)
goto error_return;
if (! _bfd_ecoff_get_accumulated_sym (handle, (PTR) esym))
goto error_return;
count = hdr->issMax;
ss = (char *) bfd_malloc (count);
if (ss == NULL)
goto error_return;
if (! _bfd_ecoff_get_accumulated_ss (handle, (PTR) ss))
goto error_return;
count = hdr->ipdMax;
for (i = 0; i < (unsigned long) count; i++, rp++)
{
(*swap->swap_pdr_in) (abfd, (PTR) (epdr + i), &pdr);
(*swap->swap_sym_in) (abfd, (PTR) &esym[pdr.isym], &sym);
rp->adr = sym.value;
rp->regmask = pdr.regmask;
rp->regoffset = pdr.regoffset;
rp->fregmask = pdr.fregmask;
rp->fregoffset = pdr.fregoffset;
rp->frameoffset = pdr.frameoffset;
rp->framereg = pdr.framereg;
rp->pcreg = pdr.pcreg;
rp->irpss = sindex;
sv[i] = ss + sym.iss;
sindex += strlen (sv[i]) + 1;
}
}
size = sizeof (struct rpdr_ext) * (count + 2) + sindex;
size = BFD_ALIGN (size, 16);
rtproc = (PTR) bfd_alloc (abfd, size);
if (rtproc == NULL)
{
mips_elf_hash_table (info)->procedure_count = 0;
goto error_return;
}
mips_elf_hash_table (info)->procedure_count = count + 2;
erp = (struct rpdr_ext *) rtproc;
memset (erp, 0, sizeof (struct rpdr_ext));
erp++;
str = (char *) rtproc + sizeof (struct rpdr_ext) * (count + 2);
strcpy (str, no_name_func);
str += strlen (no_name_func) + 1;
for (i = 0; i < count; i++)
{
ecoff_swap_rpdr_out (abfd, rpdr + i, erp + i);
strcpy (str, sv[i]);
str += strlen (sv[i]) + 1;
}
H_PUT_S32 (abfd, -1, (erp + count)->p_adr);
s->_raw_size = size;
s->contents = (bfd_byte *) rtproc;
s->link_order_head = (struct bfd_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);
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);
return false;
}
static boolean
mips_elf_check_mips16_stubs (h, data)
struct mips_elf_link_hash_entry *h;
PTR data ATTRIBUTE_UNUSED;
{
if (h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
if (h->fn_stub != NULL
&& ! h->need_fn_stub)
{
h->fn_stub->_raw_size = 0;
h->fn_stub->_cooked_size = 0;
h->fn_stub->flags &= ~SEC_RELOC;
h->fn_stub->reloc_count = 0;
h->fn_stub->flags |= SEC_EXCLUDE;
}
if (h->call_stub != NULL
&& h->root.other == STO_MIPS16)
{
h->call_stub->_raw_size = 0;
h->call_stub->_cooked_size = 0;
h->call_stub->flags &= ~SEC_RELOC;
h->call_stub->reloc_count = 0;
h->call_stub->flags |= SEC_EXCLUDE;
}
if (h->call_fp_stub != NULL
&& h->root.other == STO_MIPS16)
{
h->call_fp_stub->_raw_size = 0;
h->call_fp_stub->_cooked_size = 0;
h->call_fp_stub->flags &= ~SEC_RELOC;
h->call_fp_stub->reloc_count = 0;
h->call_fp_stub->flags |= SEC_EXCLUDE;
}
return true;
}
bfd_reloc_status_type
_bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry, input_section,
relocateable, data, gp)
bfd *abfd;
asymbol *symbol;
arelent *reloc_entry;
asection *input_section;
boolean relocateable;
PTR data;
bfd_vma gp;
{
bfd_vma relocation;
unsigned long insn;
unsigned long val;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
if (reloc_entry->howto->src_mask == 0)
{
val = reloc_entry->addend;
}
else
{
val = ((insn & 0xffff) + reloc_entry->addend) & 0xffff;
if (val & 0x8000)
val -= 0x10000;
}
if (! relocateable
|| (symbol->flags & BSF_SECTION_SYM) != 0)
val += relocation - gp;
insn = (insn & ~0xffff) | (val & 0xffff);
bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
if (relocateable)
reloc_entry->address += input_section->output_offset;
else if ((long) val >= 0x8000 || (long) val < -0x8000)
return bfd_reloc_overflow;
return bfd_reloc_ok;
}
static void
bfd_mips_elf32_swap_gptab_in (abfd, ex, in)
bfd *abfd;
const Elf32_External_gptab *ex;
Elf32_gptab *in;
{
in->gt_entry.gt_g_value = H_GET_32 (abfd, ex->gt_entry.gt_g_value);
in->gt_entry.gt_bytes = H_GET_32 (abfd, ex->gt_entry.gt_bytes);
}
static void
bfd_mips_elf32_swap_gptab_out (abfd, in, ex)
bfd *abfd;
const Elf32_gptab *in;
Elf32_External_gptab *ex;
{
H_PUT_32 (abfd, in->gt_entry.gt_g_value, ex->gt_entry.gt_g_value);
H_PUT_32 (abfd, in->gt_entry.gt_bytes, ex->gt_entry.gt_bytes);
}
static void
bfd_elf32_swap_compact_rel_out (abfd, in, ex)
bfd *abfd;
const Elf32_compact_rel *in;
Elf32_External_compact_rel *ex;
{
H_PUT_32 (abfd, in->id1, ex->id1);
H_PUT_32 (abfd, in->num, ex->num);
H_PUT_32 (abfd, in->id2, ex->id2);
H_PUT_32 (abfd, in->offset, ex->offset);
H_PUT_32 (abfd, in->reserved0, ex->reserved0);
H_PUT_32 (abfd, in->reserved1, ex->reserved1);
}
static void
bfd_elf32_swap_crinfo_out (abfd, in, ex)
bfd *abfd;
const Elf32_crinfo *in;
Elf32_External_crinfo *ex;
{
unsigned long l;
l = (((in->ctype & CRINFO_CTYPE) << CRINFO_CTYPE_SH)
| ((in->rtype & CRINFO_RTYPE) << CRINFO_RTYPE_SH)
| ((in->dist2to & CRINFO_DIST2TO) << CRINFO_DIST2TO_SH)
| ((in->relvaddr & CRINFO_RELVADDR) << CRINFO_RELVADDR_SH));
H_PUT_32 (abfd, l, ex->info);
H_PUT_32 (abfd, in->konst, ex->konst);
H_PUT_32 (abfd, in->vaddr, ex->vaddr);
}
#if 0
static void
bfd_mips_elf_swap_msym_in (abfd, ex, in)
bfd *abfd;
const Elf32_External_Msym *ex;
Elf32_Internal_Msym *in;
{
in->ms_hash_value = H_GET_32 (abfd, ex->ms_hash_value);
in->ms_info = H_GET_32 (abfd, ex->ms_info);
}
#endif
static void
bfd_mips_elf_swap_msym_out (abfd, in, ex)
bfd *abfd;
const Elf32_Internal_Msym *in;
Elf32_External_Msym *ex;
{
H_PUT_32 (abfd, in->ms_hash_value, ex->ms_hash_value);
H_PUT_32 (abfd, in->ms_info, ex->ms_info);
}
void
bfd_mips_elf32_swap_reginfo_in (abfd, ex, in)
bfd *abfd;
const Elf32_External_RegInfo *ex;
Elf32_RegInfo *in;
{
in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask);
in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]);
in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]);
in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]);
in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]);
in->ri_gp_value = H_GET_32 (abfd, ex->ri_gp_value);
}
void
bfd_mips_elf32_swap_reginfo_out (abfd, in, ex)
bfd *abfd;
const Elf32_RegInfo *in;
Elf32_External_RegInfo *ex;
{
H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask);
H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]);
H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]);
H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]);
H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]);
H_PUT_32 (abfd, in->ri_gp_value, ex->ri_gp_value);
}
void
bfd_mips_elf64_swap_reginfo_in (abfd, ex, in)
bfd *abfd;
const Elf64_External_RegInfo *ex;
Elf64_Internal_RegInfo *in;
{
in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask);
in->ri_pad = H_GET_32 (abfd, ex->ri_pad);
in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]);
in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]);
in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]);
in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]);
in->ri_gp_value = H_GET_64 (abfd, ex->ri_gp_value);
}
void
bfd_mips_elf64_swap_reginfo_out (abfd, in, ex)
bfd *abfd;
const Elf64_Internal_RegInfo *in;
Elf64_External_RegInfo *ex;
{
H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask);
H_PUT_32 (abfd, in->ri_pad, ex->ri_pad);
H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]);
H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]);
H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]);
H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]);
H_PUT_64 (abfd, in->ri_gp_value, ex->ri_gp_value);
}
void
bfd_mips_elf_swap_options_in (abfd, ex, in)
bfd *abfd;
const Elf_External_Options *ex;
Elf_Internal_Options *in;
{
in->kind = H_GET_8 (abfd, ex->kind);
in->size = H_GET_8 (abfd, ex->size);
in->section = H_GET_16 (abfd, ex->section);
in->info = H_GET_32 (abfd, ex->info);
}
void
bfd_mips_elf_swap_options_out (abfd, in, ex)
bfd *abfd;
const Elf_Internal_Options *in;
Elf_External_Options *ex;
{
H_PUT_8 (abfd, in->kind, ex->kind);
H_PUT_8 (abfd, in->size, ex->size);
H_PUT_16 (abfd, in->section, ex->section);
H_PUT_32 (abfd, in->info, ex->info);
}
static int
sort_dynamic_relocs (arg1, arg2)
const PTR arg1;
const PTR arg2;
{
const Elf32_External_Rel *ext_reloc1 = (const Elf32_External_Rel *) arg1;
const Elf32_External_Rel *ext_reloc2 = (const Elf32_External_Rel *) arg2;
Elf_Internal_Rel int_reloc1;
Elf_Internal_Rel int_reloc2;
bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, ext_reloc1, &int_reloc1);
bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, ext_reloc2, &int_reloc2);
return (ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info));
}
static boolean
mips_elf_output_extsym (h, data)
struct mips_elf_link_hash_entry *h;
PTR data;
{
struct extsym_info *einfo = (struct extsym_info *) data;
boolean strip;
asection *sec, *output_section;
if (h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
if (h->root.indx == -2)
strip = false;
else if (((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
|| (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
strip = true;
else if (einfo->info->strip == strip_all
|| (einfo->info->strip == strip_some
&& bfd_hash_lookup (einfo->info->keep_hash,
h->root.root.root.string,
false, false) == NULL))
strip = true;
else
strip = false;
if (strip)
return true;
if (h->esym.ifd == -2)
{
h->esym.jmptbl = 0;
h->esym.cobol_main = 0;
h->esym.weakext = 0;
h->esym.reserved = 0;
h->esym.ifd = ifdNil;
h->esym.asym.value = 0;
h->esym.asym.st = stGlobal;
if (h->root.root.type == bfd_link_hash_undefined
|| h->root.root.type == bfd_link_hash_undefweak)
{
const char *name;
name = h->root.root.root.string;
if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0
|| strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0)
{
h->esym.asym.sc = scData;
h->esym.asym.st = stLabel;
h->esym.asym.value = 0;
}
else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0)
{
h->esym.asym.sc = scAbs;
h->esym.asym.st = stLabel;
h->esym.asym.value =
mips_elf_hash_table (einfo->info)->procedure_count;
}
else if (strcmp (name, "_gp_disp") == 0 && ! NEWABI_P (einfo->abfd))
{
h->esym.asym.sc = scAbs;
h->esym.asym.st = stLabel;
h->esym.asym.value = elf_gp (einfo->abfd);
}
else
h->esym.asym.sc = scUndefined;
}
else if (h->root.root.type != bfd_link_hash_defined
&& h->root.root.type != bfd_link_hash_defweak)
h->esym.asym.sc = scAbs;
else
{
const char *name;
sec = h->root.root.u.def.section;
output_section = sec->output_section;
if (output_section == NULL)
h->esym.asym.sc = scUndefined;
else
{
name = bfd_section_name (output_section->owner, output_section);
if (strcmp (name, ".text") == 0)
h->esym.asym.sc = scText;
else if (strcmp (name, ".data") == 0)
h->esym.asym.sc = scData;
else if (strcmp (name, ".sdata") == 0)
h->esym.asym.sc = scSData;
else if (strcmp (name, ".rodata") == 0
|| strcmp (name, ".rdata") == 0)
h->esym.asym.sc = scRData;
else if (strcmp (name, ".bss") == 0)
h->esym.asym.sc = scBss;
else if (strcmp (name, ".sbss") == 0)
h->esym.asym.sc = scSBss;
else if (strcmp (name, ".init") == 0)
h->esym.asym.sc = scInit;
else if (strcmp (name, ".fini") == 0)
h->esym.asym.sc = scFini;
else
h->esym.asym.sc = scAbs;
}
}
h->esym.asym.reserved = 0;
h->esym.asym.index = indexNil;
}
if (h->root.root.type == bfd_link_hash_common)
h->esym.asym.value = h->root.root.u.c.size;
else if (h->root.root.type == bfd_link_hash_defined
|| h->root.root.type == bfd_link_hash_defweak)
{
if (h->esym.asym.sc == scCommon)
h->esym.asym.sc = scBss;
else if (h->esym.asym.sc == scSCommon)
h->esym.asym.sc = scSBss;
sec = h->root.root.u.def.section;
output_section = sec->output_section;
if (output_section != NULL)
h->esym.asym.value = (h->root.root.u.def.value
+ sec->output_offset
+ output_section->vma);
else
h->esym.asym.value = 0;
}
else if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
{
struct mips_elf_link_hash_entry *hd = h;
boolean no_fn_stub = h->no_fn_stub;
while (hd->root.root.type == bfd_link_hash_indirect)
{
hd = (struct mips_elf_link_hash_entry *)h->root.root.u.i.link;
no_fn_stub = no_fn_stub || hd->no_fn_stub;
}
if (!no_fn_stub)
{
h->esym.asym.st = stProc;
sec = hd->root.root.u.def.section;
if (sec == NULL)
h->esym.asym.value = 0;
else
{
output_section = sec->output_section;
if (output_section != NULL)
h->esym.asym.value = (hd->root.plt.offset
+ sec->output_offset
+ output_section->vma);
else
h->esym.asym.value = 0;
}
#if 0
h->esym.ifd = 0;
#endif
}
}
if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
h->root.root.root.string,
&h->esym))
{
einfo->failed = true;
return false;
}
return true;
}
static int
gptab_compare (p1, p2)
const PTR p1;
const PTR p2;
{
const Elf32_gptab *a1 = (const Elf32_gptab *) p1;
const Elf32_gptab *a2 = (const Elf32_gptab *) p2;
return a1->gt_entry.gt_g_value - a2->gt_entry.gt_g_value;
}
static asection *
mips_elf_got_section (abfd)
bfd *abfd;
{
return bfd_get_section_by_name (abfd, ".got");
}
static struct mips_got_info *
mips_elf_got_info (abfd, sgotp)
bfd *abfd;
asection **sgotp;
{
asection *sgot;
struct mips_got_info *g;
sgot = mips_elf_got_section (abfd);
BFD_ASSERT (sgot != NULL);
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
if (sgotp)
*sgotp = sgot;
return g;
}
static bfd_vma
mips_elf_local_got_index (abfd, info, value)
bfd *abfd;
struct bfd_link_info *info;
bfd_vma value;
{
asection *sgot;
struct mips_got_info *g;
bfd_byte *entry;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
for (entry = (sgot->contents
+ MIPS_ELF_GOT_SIZE (abfd) * MIPS_RESERVED_GOTNO);
entry != sgot->contents + MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno;
entry += MIPS_ELF_GOT_SIZE (abfd))
{
bfd_vma address = MIPS_ELF_GET_WORD (abfd, entry);
if (address == value)
return entry - sgot->contents;
}
return mips_elf_create_local_got_entry (abfd, g, sgot, value);
}
static bfd_vma
mips_elf_global_got_index (abfd, h)
bfd *abfd;
struct elf_link_hash_entry *h;
{
bfd_vma index;
asection *sgot;
struct mips_got_info *g;
long global_got_dynindx = 0;
g = mips_elf_got_info (abfd, &sgot);
if (g->global_gotsym != NULL)
global_got_dynindx = g->global_gotsym->dynindx;
BFD_ASSERT (h->dynindx >= global_got_dynindx);
index = ((h->dynindx - global_got_dynindx + g->local_gotno)
* MIPS_ELF_GOT_SIZE (abfd));
BFD_ASSERT (index < sgot->_raw_size);
return index;
}
static bfd_vma
mips_elf_got_page (abfd, info, value, offsetp)
bfd *abfd;
struct bfd_link_info *info;
bfd_vma value;
bfd_vma *offsetp;
{
asection *sgot;
struct mips_got_info *g;
bfd_byte *entry;
bfd_byte *last_entry;
bfd_vma index = 0;
bfd_vma address;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
last_entry = sgot->contents + MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno;
for (entry = (sgot->contents
+ MIPS_ELF_GOT_SIZE (abfd) * MIPS_RESERVED_GOTNO);
entry != last_entry;
entry += MIPS_ELF_GOT_SIZE (abfd))
{
address = MIPS_ELF_GET_WORD (abfd, entry);
if (!mips_elf_overflow_p (value - address, 16))
{
index = entry - sgot->contents;
break;
}
}
if (entry == last_entry)
index = mips_elf_create_local_got_entry (abfd, g, sgot, value);
if (offsetp)
{
address = MIPS_ELF_GET_WORD (abfd, entry);
*offsetp = value - address;
}
return index;
}
static bfd_vma
mips_elf_got16_entry (abfd, info, value, external)
bfd *abfd;
struct bfd_link_info *info;
bfd_vma value;
boolean external;
{
asection *sgot;
struct mips_got_info *g;
bfd_byte *entry;
bfd_byte *last_entry;
bfd_vma index = 0;
bfd_vma address;
if (! external)
{
value = mips_elf_high (value) << 16;
}
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
last_entry = sgot->contents + MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno;
for (entry = (sgot->contents
+ MIPS_ELF_GOT_SIZE (abfd) * MIPS_RESERVED_GOTNO);
entry != last_entry;
entry += MIPS_ELF_GOT_SIZE (abfd))
{
address = MIPS_ELF_GET_WORD (abfd, entry);
if (address == value)
{
index = entry - sgot->contents;
break;
}
}
if (entry == last_entry)
index = mips_elf_create_local_got_entry (abfd, g, sgot, value);
return index;
}
static bfd_vma
mips_elf_got_offset_from_index (dynobj, output_bfd, index)
bfd *dynobj;
bfd *output_bfd;
bfd_vma index;
{
asection *sgot;
bfd_vma gp;
sgot = mips_elf_got_section (dynobj);
gp = _bfd_get_gp_value (output_bfd);
return (sgot->output_section->vma + sgot->output_offset + index -
gp);
}
static bfd_vma
mips_elf_create_local_got_entry (abfd, g, sgot, value)
bfd *abfd;
struct mips_got_info *g;
asection *sgot;
bfd_vma value;
{
if (g->assigned_gotno >= g->local_gotno)
{
(*_bfd_error_handler)
(_("not enough GOT space for local GOT entries"));
bfd_set_error (bfd_error_bad_value);
return (bfd_vma) -1;
}
MIPS_ELF_PUT_WORD (abfd, value,
(sgot->contents
+ MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno));
return MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
}
static boolean
mips_elf_sort_hash_table (info, max_local)
struct bfd_link_info *info;
unsigned long max_local;
{
struct mips_elf_hash_sort_data hsd;
struct mips_got_info *g;
bfd *dynobj;
dynobj = elf_hash_table (info)->dynobj;
hsd.low = NULL;
hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount;
hsd.max_non_got_dynindx = max_local;
mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *)
elf_hash_table (info)),
mips_elf_sort_hash_table_f,
&hsd);
BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx);
g = mips_elf_got_info (dynobj, NULL);
g->global_gotsym = hsd.low;
return true;
}
static boolean
mips_elf_sort_hash_table_f (h, data)
struct mips_elf_link_hash_entry *h;
PTR data;
{
struct mips_elf_hash_sort_data *hsd
= (struct mips_elf_hash_sort_data *) data;
if (h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
if (h->root.dynindx == -1)
return true;
if (h->root.got.offset != 1)
h->root.dynindx = hsd->max_non_got_dynindx++;
else
{
h->root.dynindx = --hsd->min_got_dynindx;
hsd->low = (struct elf_link_hash_entry *) h;
}
return true;
}
static boolean
mips_elf_record_global_got_symbol (h, info, g)
struct elf_link_hash_entry *h;
struct bfd_link_info *info;
struct mips_got_info *g ATTRIBUTE_UNUSED;
{
if (h->dynindx == -1)
{
switch (ELF_ST_VISIBILITY (h->other))
{
case STV_INTERNAL:
case STV_HIDDEN:
_bfd_mips_elf_hide_symbol (info, h, true);
break;
}
if (!bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
}
if (h->got.offset != MINUS_ONE)
return true;
h->got.offset = 1;
return true;
}
static const Elf_Internal_Rela *
mips_elf_next_relocation (abfd, r_type, relocation, relend)
bfd *abfd ATTRIBUTE_UNUSED;
unsigned int r_type;
const Elf_Internal_Rela *relocation;
const Elf_Internal_Rela *relend;
{
while (relocation < relend)
{
if (ELF_R_TYPE (abfd, relocation->r_info) == r_type)
return relocation;
++relocation;
}
bfd_set_error (bfd_error_bad_value);
return NULL;
}
static boolean
mips_elf_local_relocation_p (input_bfd, relocation, local_sections,
check_forced)
bfd *input_bfd;
const Elf_Internal_Rela *relocation;
asection **local_sections;
boolean check_forced;
{
unsigned long r_symndx;
Elf_Internal_Shdr *symtab_hdr;
struct mips_elf_link_hash_entry *h;
size_t extsymoff;
r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
extsymoff = (elf_bad_symtab (input_bfd)) ? 0 : symtab_hdr->sh_info;
if (r_symndx < extsymoff)
return true;
if (elf_bad_symtab (input_bfd) && local_sections[r_symndx] != NULL)
return true;
if (check_forced)
{
h = (struct mips_elf_link_hash_entry *)
elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
while (h->root.root.type == bfd_link_hash_indirect
|| h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
if ((h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
return true;
}
return false;
}
static bfd_vma
mips_elf_sign_extend (value, bits)
bfd_vma value;
int bits;
{
if (value & ((bfd_vma) 1 << (bits - 1)))
value |= ((bfd_vma) - 1) << bits;
return value;
}
static boolean
mips_elf_overflow_p (value, bits)
bfd_vma value;
int bits;
{
bfd_signed_vma svalue = (bfd_signed_vma) value;
if (svalue > (1 << (bits - 1)) - 1)
return true;
else if (svalue < -(1 << (bits - 1)))
return true;
return false;
}
static bfd_vma
mips_elf_high (value)
bfd_vma value;
{
return ((value + (bfd_vma) 0x8000) >> 16) & 0xffff;
}
static bfd_vma
mips_elf_higher (value)
bfd_vma value ATTRIBUTE_UNUSED;
{
#ifdef BFD64
return ((value + (bfd_vma) 0x80008000) >> 32) & 0xffff;
#else
abort ();
return (bfd_vma) -1;
#endif
}
static bfd_vma
mips_elf_highest (value)
bfd_vma value ATTRIBUTE_UNUSED;
{
#ifdef BFD64
return ((value + (bfd_vma) 0x800080008000) >> 48) & 0xffff;
#else
abort ();
return (bfd_vma) -1;
#endif
}
static boolean
mips_elf_create_compact_rel_section (abfd, info)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
{
flagword flags;
register asection *s;
if (bfd_get_section_by_name (abfd, ".compact_rel") == NULL)
{
flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED
| SEC_READONLY);
s = bfd_make_section (abfd, ".compact_rel");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return false;
s->_raw_size = sizeof (Elf32_External_compact_rel);
}
return true;
}
static boolean
mips_elf_create_got_section (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
flagword flags;
register asection *s;
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
struct mips_got_info *g;
bfd_size_type amt;
if (mips_elf_got_section (abfd))
return true;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
s = bfd_make_section (abfd, ".got");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s, 4))
return false;
bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s,
(bfd_vma) 0, (const char *) NULL, false,
get_elf_backend_data (abfd)->collect, &bh)))
return false;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (info->shared
&& ! bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
s->_raw_size = MIPS_RESERVED_GOTNO * MIPS_ELF_GOT_SIZE (abfd);
amt = sizeof (struct mips_got_info);
g = (struct mips_got_info *) bfd_alloc (abfd, amt);
if (g == NULL)
return false;
g->global_gotsym = NULL;
g->local_gotno = MIPS_RESERVED_GOTNO;
g->assigned_gotno = MIPS_RESERVED_GOTNO;
if (elf_section_data (s) == NULL)
{
amt = sizeof (struct bfd_elf_section_data);
s->used_by_bfd = (PTR) bfd_zalloc (abfd, amt);
if (elf_section_data (s) == NULL)
return false;
}
elf_section_data (s)->tdata = (PTR) g;
elf_section_data (s)->this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
return true;
}
static asection *
mips_elf_create_msym_section (abfd)
bfd *abfd;
{
asection *s;
s = bfd_get_section_by_name (abfd, ".msym");
if (!s)
{
s = bfd_make_section (abfd, ".msym");
if (!s
|| !bfd_set_section_flags (abfd, s,
SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_LINKER_CREATED
| SEC_READONLY)
|| !bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return NULL;
}
return s;
}
static bfd_reloc_status_type
mips_elf_calculate_relocation (abfd, input_bfd, input_section, info,
relocation, addend, howto, local_syms,
local_sections, valuep, namep,
require_jalxp, save_addend)
bfd *abfd;
bfd *input_bfd;
asection *input_section;
struct bfd_link_info *info;
const Elf_Internal_Rela *relocation;
bfd_vma addend;
reloc_howto_type *howto;
Elf_Internal_Sym *local_syms;
asection **local_sections;
bfd_vma *valuep;
const char **namep;
boolean *require_jalxp;
boolean save_addend;
{
bfd_vma value;
bfd_vma symbol = 0;
bfd_vma gp = MINUS_ONE;
bfd_vma p;
bfd_vma gp0 = MINUS_ONE;
bfd_vma g = MINUS_ONE;
asection *sec = NULL;
struct mips_elf_link_hash_entry *h = NULL;
boolean local_p, was_local_p;
boolean gp_disp_p = false;
Elf_Internal_Shdr *symtab_hdr;
size_t extsymoff;
unsigned long r_symndx;
int r_type;
boolean overflowed_p;
boolean target_is_16_bit_code_p = false;
r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
p = (input_section->output_section->vma
+ input_section->output_offset
+ relocation->r_offset);
overflowed_p = false;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
local_p = mips_elf_local_relocation_p (input_bfd, relocation,
local_sections, false);
was_local_p = local_p;
if (! elf_bad_symtab (input_bfd))
extsymoff = symtab_hdr->sh_info;
else
{
extsymoff = 0;
}
if (local_p)
{
Elf_Internal_Sym *sym;
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
symbol = sec->output_section->vma + sec->output_offset;
if (ELF_ST_TYPE (sym->st_info) != STT_SECTION
|| (sec->flags & SEC_MERGE))
symbol += sym->st_value;
if ((sec->flags & SEC_MERGE)
&& ELF_ST_TYPE (sym->st_info) == STT_SECTION)
{
addend = _bfd_elf_rel_local_sym (abfd, sym, &sec, addend);
addend -= symbol;
addend += sec->output_section->vma + sec->output_offset;
}
if (sym->st_other == STO_MIPS16)
++symbol;
*namep = bfd_elf_string_from_elf_section (input_bfd,
symtab_hdr->sh_link,
sym->st_name);
if (*namep == '\0')
*namep = bfd_section_name (input_bfd, sec);
target_is_16_bit_code_p = (sym->st_other == STO_MIPS16);
}
else
{
h = ((struct mips_elf_link_hash_entry *)
elf_sym_hashes (input_bfd) [r_symndx - extsymoff]);
while (h->root.root.type == bfd_link_hash_indirect
|| h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
*namep = h->root.root.root.string;
if (strcmp (h->root.root.root.string, "_gp_disp") == 0
&& ! NEWABI_P (input_bfd))
{
if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16)
return bfd_reloc_notsupported;
gp_disp_p = true;
}
else if ((h->root.root.type == bfd_link_hash_defined
|| h->root.root.type == bfd_link_hash_defweak)
&& h->root.root.u.def.section)
{
sec = h->root.root.u.def.section;
if (sec->output_section)
symbol = (h->root.root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
else
symbol = h->root.root.u.def.value;
}
else if (h->root.root.type == bfd_link_hash_undefweak)
symbol = 0;
else if (info->shared
&& (!info->symbolic || info->allow_shlib_undefined)
&& !info->no_undefined
&& ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
symbol = 0;
else if (strcmp (h->root.root.root.string, "_DYNAMIC_LINK") == 0 ||
strcmp (h->root.root.root.string, "_DYNAMIC_LINKING") == 0)
{
BFD_ASSERT (! info->shared);
BFD_ASSERT (bfd_get_section_by_name (abfd, ".dynamic") == NULL);
symbol = 0;
}
else
{
if (! ((*info->callbacks->undefined_symbol)
(info, h->root.root.root.string, input_bfd,
input_section, relocation->r_offset,
(!info->shared || info->no_undefined
|| ELF_ST_VISIBILITY (h->root.other)))))
return bfd_reloc_undefined;
symbol = 0;
}
target_is_16_bit_code_p = (h->root.other == STO_MIPS16);
}
if (r_type != R_MIPS16_26 && !info->relocateable
&& ((h != NULL && h->fn_stub != 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))
{
if (local_p)
sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
else
{
BFD_ASSERT (h->need_fn_stub);
sec = h->fn_stub;
}
symbol = sec->output_section->vma + sec->output_offset;
}
else if (r_type == R_MIPS16_26 && !info->relocateable
&& h != NULL
&& (h->call_stub != NULL || h->call_fp_stub != NULL)
&& !target_is_16_bit_code_p)
{
if (h->call_stub != NULL && h->call_fp_stub != NULL)
{
asection *o;
sec = NULL;
for (o = input_bfd->sections; o != NULL; o = o->next)
{
if (strncmp (bfd_get_section_name (input_bfd, o),
CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
{
sec = h->call_fp_stub;
break;
}
}
if (sec == NULL)
sec = h->call_stub;
}
else if (h->call_stub != NULL)
sec = h->call_stub;
else
sec = h->call_fp_stub;
BFD_ASSERT (sec->_raw_size > 0);
symbol = sec->output_section->vma + sec->output_offset;
}
*require_jalxp = (!info->relocateable
&& (((r_type == R_MIPS16_26) && !target_is_16_bit_code_p)
|| ((r_type == R_MIPS_26) && target_is_16_bit_code_p)));
local_p = mips_elf_local_relocation_p (input_bfd, relocation,
local_sections, true);
switch (r_type)
{
case R_MIPS_CALL16:
case R_MIPS_GOT16:
case R_MIPS_GOT_DISP:
case R_MIPS_GOT_HI16:
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_CALL_LO16:
if (!local_p)
{
BFD_ASSERT (addend == 0);
g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
(struct elf_link_hash_entry *) h);
if (! elf_hash_table(info)->dynamic_sections_created
|| (info->shared
&& (info->symbolic || h->root.dynindx == -1)
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
{
bfd *tmpbfd = elf_hash_table (info)->dynobj;
asection *sgot = mips_elf_got_section(tmpbfd);
MIPS_ELF_PUT_WORD (tmpbfd, symbol + addend, sgot->contents + g);
}
}
else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16)
break;
else
{
g = mips_elf_local_got_index (abfd, info, symbol + addend);
if (g == MINUS_ONE)
return bfd_reloc_outofrange;
}
g = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
abfd, g);
break;
case R_MIPS_HI16:
case R_MIPS_LO16:
case R_MIPS16_GPREL:
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
case R_MIPS_LITERAL:
gp0 = _bfd_get_gp_value (input_bfd);
gp = _bfd_get_gp_value (abfd);
break;
default:
break;
}
switch (r_type)
{
case R_MIPS_NONE:
return bfd_reloc_continue;
case R_MIPS_16:
value = symbol + mips_elf_sign_extend (addend, 16);
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if ((info->shared
|| (elf_hash_table (info)->dynamic_sections_created
&& h != NULL
&& ((h->root.elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0)
&& ((h->root.elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)))
&& r_symndx != 0
&& (input_section->flags & SEC_ALLOC) != 0)
{
value = addend;
if (!mips_elf_create_dynamic_relocation (abfd,
info,
relocation,
h,
sec,
symbol,
&value,
input_section))
return bfd_reloc_undefined;
}
else
{
if (r_type != R_MIPS_REL32)
value = symbol + addend;
else
value = addend;
}
value &= howto->dst_mask;
break;
case R_MIPS_PC32:
case R_MIPS_PC64:
case R_MIPS_GNU_REL_LO16:
value = symbol + addend - p;
value &= howto->dst_mask;
break;
case R_MIPS_GNU_REL16_S2:
value = symbol + mips_elf_sign_extend (addend << 2, 18) - p;
overflowed_p = mips_elf_overflow_p (value, 18);
value = (value >> 2) & howto->dst_mask;
break;
case R_MIPS_GNU_REL_HI16:
value = mips_elf_high (addend + symbol - p);
value &= howto->dst_mask;
break;
case R_MIPS16_26:
case R_MIPS_26:
if (local_p)
value = (((addend << 2) | ((p + 4) & 0xf0000000)) + symbol) >> 2;
else
value = (mips_elf_sign_extend (addend << 2, 28) + symbol) >> 2;
value &= howto->dst_mask;
break;
case R_MIPS_HI16:
if (!gp_disp_p)
{
value = mips_elf_high (addend + symbol);
value &= howto->dst_mask;
}
else
{
value = mips_elf_high (addend + gp - p);
overflowed_p = mips_elf_overflow_p (value, 16);
}
break;
case R_MIPS_LO16:
if (!gp_disp_p)
value = (symbol + addend) & howto->dst_mask;
else
{
value = addend + gp - p + 4;
}
break;
case R_MIPS_LITERAL:
case R_MIPS16_GPREL:
case R_MIPS_GPREL16:
if (howto->partial_inplace)
addend = mips_elf_sign_extend (addend, 16);
value = symbol + addend - gp;
if (was_local_p)
value += gp0;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_GOT16:
case R_MIPS_CALL16:
if (local_p)
{
boolean forced;
forced = ! mips_elf_local_relocation_p (input_bfd, relocation,
local_sections, false);
value = mips_elf_got16_entry (abfd, info, symbol + addend, forced);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
value
= mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
abfd, value);
overflowed_p = mips_elf_overflow_p (value, 16);
break;
}
case R_MIPS_GOT_DISP:
value = g;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_GPREL32:
value = (addend + symbol + gp0 - gp);
if (!save_addend)
value &= howto->dst_mask;
break;
case R_MIPS_PC16:
value = mips_elf_sign_extend (addend, 16) + symbol - p;
overflowed_p = mips_elf_overflow_p (value, 16);
value = (bfd_vma) ((bfd_signed_vma) value / 4);
break;
case R_MIPS_GOT_HI16:
case R_MIPS_CALL_HI16:
value = g;
value = mips_elf_high (value);
value &= howto->dst_mask;
break;
case R_MIPS_GOT_LO16:
case R_MIPS_CALL_LO16:
value = g & howto->dst_mask;
break;
case R_MIPS_GOT_PAGE:
value = mips_elf_got_page (abfd, info, symbol + addend, NULL);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
abfd, value);
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_GOT_OFST:
mips_elf_got_page (abfd, info, symbol + addend, &value);
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_SUB:
value = symbol - addend;
value &= howto->dst_mask;
break;
case R_MIPS_HIGHER:
value = mips_elf_higher (addend + symbol);
value &= howto->dst_mask;
break;
case R_MIPS_HIGHEST:
value = mips_elf_highest (addend + symbol);
value &= howto->dst_mask;
break;
case R_MIPS_SCN_DISP:
value = symbol + addend - sec->output_offset;
value &= howto->dst_mask;
break;
case R_MIPS_PJUMP:
case R_MIPS_JALR:
return bfd_reloc_continue;
case R_MIPS_GNU_VTINHERIT:
case R_MIPS_GNU_VTENTRY:
return bfd_reloc_continue;
default:
return bfd_reloc_notsupported;
}
*valuep = value;
return overflowed_p ? bfd_reloc_overflow : bfd_reloc_ok;
}
static bfd_vma
mips_elf_obtain_contents (howto, relocation, input_bfd, contents)
reloc_howto_type *howto;
const Elf_Internal_Rela *relocation;
bfd *input_bfd;
bfd_byte *contents;
{
bfd_vma x;
bfd_byte *location = contents + relocation->r_offset;
x = bfd_get ((8 * bfd_get_reloc_size (howto)), input_bfd, location);
if ((ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_26
|| ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_GPREL)
&& bfd_little_endian (input_bfd))
x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
return x;
}
static boolean
mips_elf_perform_relocation (info, howto, relocation, value, input_bfd,
input_section, contents, require_jalx)
struct bfd_link_info *info;
reloc_howto_type *howto;
const Elf_Internal_Rela *relocation;
bfd_vma value;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
boolean require_jalx;
{
bfd_vma x;
bfd_byte *location;
int r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
location = contents + relocation->r_offset;
x = mips_elf_obtain_contents (howto, relocation, input_bfd, contents);
x &= ~howto->dst_mask;
if (r_type == R_MIPS16_26)
{
if (!info->relocateable)
value = (((value & 0x1f0000) << 5)
| ((value & 0x3e00000) >> 5)
| (value & 0xffff));
}
else if (r_type == R_MIPS16_GPREL)
{
value = (((value & 0x7e0) << 16)
| ((value & 0xf800) << 5)
| (value & 0x1f));
}
x |= (value & howto->dst_mask);
if (require_jalx)
{
boolean ok;
bfd_vma opcode = x >> 26;
bfd_vma jalx_opcode;
if (r_type == R_MIPS16_26)
{
ok = ((opcode == 0x6) || (opcode == 0x7));
jalx_opcode = 0x7;
}
else
{
ok = ((opcode == 0x3) || (opcode == 0x1d));
jalx_opcode = 0x1d;
}
if (!ok)
{
(*_bfd_error_handler)
(_("%s: %s+0x%lx: jump to stub routine which is not jal"),
bfd_archive_filename (input_bfd),
input_section->name,
(unsigned long) relocation->r_offset);
bfd_set_error (bfd_error_bad_value);
return false;
}
x = (x & ~(0x3f << 26)) | (jalx_opcode << 26);
}
if ((r_type == R_MIPS16_GPREL || r_type == R_MIPS16_26)
&& bfd_little_endian (input_bfd))
x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
return true;
}
static boolean
mips_elf_stub_section_p (abfd, section)
bfd *abfd ATTRIBUTE_UNUSED;
asection *section;
{
const char *name = bfd_get_section_name (abfd, section);
return (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0
|| strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
|| strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0);
}
static void
mips_elf_allocate_dynamic_relocations (abfd, n)
bfd *abfd;
unsigned int n;
{
asection *s;
s = bfd_get_section_by_name (abfd, ".rel.dyn");
BFD_ASSERT (s != NULL);
if (s->_raw_size == 0)
{
s->_raw_size += MIPS_ELF_REL_SIZE (abfd);
++s->reloc_count;
}
s->_raw_size += n * MIPS_ELF_REL_SIZE (abfd);
}
static boolean
mips_elf_create_dynamic_relocation (output_bfd, info, rel, h, sec,
symbol, addendp, input_section)
bfd *output_bfd;
struct bfd_link_info *info;
const Elf_Internal_Rela *rel;
struct mips_elf_link_hash_entry *h;
asection *sec;
bfd_vma symbol;
bfd_vma *addendp;
asection *input_section;
{
Elf_Internal_Rel outrel[3];
boolean skip;
asection *sreloc;
bfd *dynobj;
int r_type;
r_type = ELF_R_TYPE (output_bfd, rel->r_info);
dynobj = elf_hash_table (info)->dynobj;
sreloc = bfd_get_section_by_name (dynobj, ".rel.dyn");
BFD_ASSERT (sreloc != NULL);
BFD_ASSERT (sreloc->contents != NULL);
BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd)
< sreloc->_raw_size);
skip = false;
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 0
if (elf_section_data (input_section)->sec_info_type != ELF_INFO_TYPE_STABS)
{
outrel[1].r_offset = rel[1].r_offset;
outrel[2].r_offset = rel[2].r_offset;
}
else
{
outrel[1].r_offset = outrel[0].r_offset;
outrel[2].r_offset = outrel[0].r_offset;
if (outrel[0].r_offset == (bfd_vma) -1)
skip = true;
}
#endif
if (outrel[0].r_offset == (bfd_vma) -1)
skip = true;
BFD_ASSERT (outrel[0].r_offset != (bfd_vma) -2);
if (skip)
memset (outrel, 0, sizeof (Elf_Internal_Rel) * 3);
else
{
long indx;
bfd_vma section_offset;
if (h != NULL
&& (! info->symbolic || (h->root.elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
{
indx = h->root.dynindx;
if (indx == -1)
indx = 0;
}
else
{
if (sec != NULL && bfd_is_abs_section (sec))
indx = 0;
else if (sec == NULL || sec->owner == NULL)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
else
{
indx = elf_section_data (sec->output_section)->dynindx;
if (indx == 0)
abort ();
}
section_offset = symbol - sec->output_section->vma;
*addendp += section_offset;
symbol = sec->output_section->vma;
}
if (!indx && r_type != R_MIPS_REL32)
*addendp += symbol;
outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
R_MIPS_REL32);
outrel[1].r_info = ELF_R_INFO (output_bfd, (unsigned long) 0,
R_MIPS_NONE);
outrel[2].r_info = ELF_R_INFO (output_bfd, (unsigned long) 0,
R_MIPS_NONE);
outrel[0].r_offset += (input_section->output_section->vma
+ input_section->output_offset);
outrel[1].r_offset += (input_section->output_section->vma
+ input_section->output_offset);
outrel[2].r_offset += (input_section->output_section->vma
+ input_section->output_offset);
}
if (ABI_64_P (output_bfd))
{
(*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
(output_bfd, &outrel[0],
(sreloc->contents
+ sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
}
else
bfd_elf32_swap_reloc_out (output_bfd, &outrel[0],
(((Elf32_External_Rel *)
sreloc->contents)
+ sreloc->reloc_count));
if (h != NULL
&& (h->min_dyn_reloc_index == 0
|| sreloc->reloc_count < h->min_dyn_reloc_index))
h->min_dyn_reloc_index = sreloc->reloc_count;
++sreloc->reloc_count;
elf_section_data (input_section->output_section)->this_hdr.sh_flags
|= SHF_WRITE;
if (! skip && IRIX_COMPAT (output_bfd) == ict_irix5)
{
asection *scpt = bfd_get_section_by_name (dynobj, ".compact_rel");
bfd_byte *cr;
if (scpt)
{
Elf32_crinfo cptrel;
mips_elf_set_cr_format (cptrel, CRF_MIPS_LONG);
cptrel.vaddr = (rel->r_offset
+ input_section->output_section->vma
+ input_section->output_offset);
if (r_type == R_MIPS_REL32)
mips_elf_set_cr_type (cptrel, CRT_MIPS_REL32);
else
mips_elf_set_cr_type (cptrel, CRT_MIPS_WORD);
mips_elf_set_cr_dist2to (cptrel, 0);
cptrel.konst = *addendp;
cr = (scpt->contents
+ sizeof (Elf32_External_compact_rel));
bfd_elf32_swap_crinfo_out (output_bfd, &cptrel,
((Elf32_External_crinfo *) cr
+ scpt->reloc_count));
++scpt->reloc_count;
}
}
return true;
}
static INLINE int
elf_mips_isa (flags)
flagword flags;
{
switch (flags & EF_MIPS_ARCH)
{
case E_MIPS_ARCH_1:
return 1;
case E_MIPS_ARCH_2:
return 2;
case E_MIPS_ARCH_3:
return 3;
case E_MIPS_ARCH_4:
return 4;
case E_MIPS_ARCH_5:
return 5;
case E_MIPS_ARCH_32:
return 32;
case E_MIPS_ARCH_64:
return 64;
}
return 4;
}
unsigned long
_bfd_elf_mips_mach (flags)
flagword flags;
{
switch (flags & EF_MIPS_MACH)
{
case E_MIPS_MACH_3900:
return bfd_mach_mips3900;
case E_MIPS_MACH_4010:
return bfd_mach_mips4010;
case E_MIPS_MACH_4100:
return bfd_mach_mips4100;
case E_MIPS_MACH_4111:
return bfd_mach_mips4111;
case E_MIPS_MACH_4120:
return bfd_mach_mips4120;
case E_MIPS_MACH_4650:
return bfd_mach_mips4650;
case E_MIPS_MACH_5400:
return bfd_mach_mips5400;
case E_MIPS_MACH_5500:
return bfd_mach_mips5500;
case E_MIPS_MACH_SB1:
return bfd_mach_mips_sb1;
default:
switch (flags & EF_MIPS_ARCH)
{
default:
case E_MIPS_ARCH_1:
return bfd_mach_mips3000;
break;
case E_MIPS_ARCH_2:
return bfd_mach_mips6000;
break;
case E_MIPS_ARCH_3:
return bfd_mach_mips4000;
break;
case E_MIPS_ARCH_4:
return bfd_mach_mips8000;
break;
case E_MIPS_ARCH_5:
return bfd_mach_mips5;
break;
case E_MIPS_ARCH_32:
return bfd_mach_mipsisa32;
break;
case E_MIPS_ARCH_64:
return bfd_mach_mipsisa64;
break;
}
}
return 0;
}
static INLINE char *
elf_mips_abi_name (abfd)
bfd *abfd;
{
flagword flags;
flags = elf_elfheader (abfd)->e_flags;
switch (flags & EF_MIPS_ABI)
{
case 0:
if (ABI_N32_P (abfd))
return "N32";
else if (ABI_64_P (abfd))
return "64";
else
return "none";
case E_MIPS_ABI_O32:
return "O32";
case E_MIPS_ABI_O64:
return "O64";
case E_MIPS_ABI_EABI32:
return "EABI32";
case E_MIPS_ABI_EABI64:
return "EABI64";
default:
return "unknown abi";
}
}
static asection mips_elf_scom_section;
static asymbol mips_elf_scom_symbol;
static asymbol *mips_elf_scom_symbol_ptr;
static asection mips_elf_acom_section;
static asymbol mips_elf_acom_symbol;
static asymbol *mips_elf_acom_symbol_ptr;
void
_bfd_mips_elf_symbol_processing (abfd, asym)
bfd *abfd;
asymbol *asym;
{
elf_symbol_type *elfsym;
elfsym = (elf_symbol_type *) asym;
switch (elfsym->internal_elf_sym.st_shndx)
{
case SHN_MIPS_ACOMMON:
if (mips_elf_acom_section.name == NULL)
{
mips_elf_acom_section.name = ".acommon";
mips_elf_acom_section.flags = SEC_ALLOC;
mips_elf_acom_section.output_section = &mips_elf_acom_section;
mips_elf_acom_section.symbol = &mips_elf_acom_symbol;
mips_elf_acom_section.symbol_ptr_ptr = &mips_elf_acom_symbol_ptr;
mips_elf_acom_symbol.name = ".acommon";
mips_elf_acom_symbol.flags = BSF_SECTION_SYM;
mips_elf_acom_symbol.section = &mips_elf_acom_section;
mips_elf_acom_symbol_ptr = &mips_elf_acom_symbol;
}
asym->section = &mips_elf_acom_section;
break;
case SHN_COMMON:
if (asym->value > elf_gp_size (abfd)
|| IRIX_COMPAT (abfd) == ict_irix6)
break;
case SHN_MIPS_SCOMMON:
if (mips_elf_scom_section.name == NULL)
{
mips_elf_scom_section.name = ".scommon";
mips_elf_scom_section.flags = SEC_IS_COMMON;
mips_elf_scom_section.output_section = &mips_elf_scom_section;
mips_elf_scom_section.symbol = &mips_elf_scom_symbol;
mips_elf_scom_section.symbol_ptr_ptr = &mips_elf_scom_symbol_ptr;
mips_elf_scom_symbol.name = ".scommon";
mips_elf_scom_symbol.flags = BSF_SECTION_SYM;
mips_elf_scom_symbol.section = &mips_elf_scom_section;
mips_elf_scom_symbol_ptr = &mips_elf_scom_symbol;
}
asym->section = &mips_elf_scom_section;
asym->value = elfsym->internal_elf_sym.st_size;
break;
case SHN_MIPS_SUNDEFINED:
asym->section = bfd_und_section_ptr;
break;
#if 0
case SHN_MIPS_TEXT:
asym->section = mips_elf_text_section_ptr;
break;
case SHN_MIPS_DATA:
asym->section = mips_elf_data_section_ptr;
break;
#endif
}
}
boolean
_bfd_mips_elf_section_processing (abfd, hdr)
bfd *abfd;
Elf_Internal_Shdr *hdr;
{
if (hdr->sh_type == SHT_MIPS_REGINFO
&& hdr->sh_size > 0)
{
bfd_byte buf[4];
BFD_ASSERT (hdr->sh_size == sizeof (Elf32_External_RegInfo));
BFD_ASSERT (hdr->contents == NULL);
if (bfd_seek (abfd,
hdr->sh_offset + sizeof (Elf32_External_RegInfo) - 4,
SEEK_SET) != 0)
return false;
H_PUT_32 (abfd, elf_gp (abfd), buf);
if (bfd_bwrite (buf, (bfd_size_type) 4, abfd) != 4)
return false;
}
if (hdr->sh_type == SHT_MIPS_OPTIONS
&& hdr->bfd_section != NULL
&& elf_section_data (hdr->bfd_section) != NULL
&& elf_section_data (hdr->bfd_section)->tdata != NULL)
{
bfd_byte *contents, *l, *lend;
contents = (bfd_byte *) elf_section_data (hdr->bfd_section)->tdata;
l = contents;
lend = contents + hdr->sh_size;
while (l + sizeof (Elf_External_Options) <= lend)
{
Elf_Internal_Options intopt;
bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
&intopt);
if (ABI_64_P (abfd) && intopt.kind == ODK_REGINFO)
{
bfd_byte buf[8];
if (bfd_seek (abfd,
(hdr->sh_offset
+ (l - contents)
+ sizeof (Elf_External_Options)
+ (sizeof (Elf64_External_RegInfo) - 8)),
SEEK_SET) != 0)
return false;
H_PUT_64 (abfd, elf_gp (abfd), buf);
if (bfd_bwrite (buf, (bfd_size_type) 8, abfd) != 8)
return false;
}
else if (intopt.kind == ODK_REGINFO)
{
bfd_byte buf[4];
if (bfd_seek (abfd,
(hdr->sh_offset
+ (l - contents)
+ sizeof (Elf_External_Options)
+ (sizeof (Elf32_External_RegInfo) - 4)),
SEEK_SET) != 0)
return false;
H_PUT_32 (abfd, elf_gp (abfd), buf);
if (bfd_bwrite (buf, (bfd_size_type) 4, abfd) != 4)
return false;
}
l += intopt.size;
}
}
if (hdr->bfd_section != NULL)
{
const char *name = bfd_get_section_name (abfd, hdr->bfd_section);
if (strcmp (name, ".sdata") == 0
|| strcmp (name, ".lit8") == 0
|| strcmp (name, ".lit4") == 0)
{
hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
hdr->sh_type = SHT_PROGBITS;
}
else if (strcmp (name, ".sbss") == 0)
{
hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
hdr->sh_type = SHT_NOBITS;
}
else if (strcmp (name, ".srdata") == 0)
{
hdr->sh_flags |= SHF_ALLOC | SHF_MIPS_GPREL;
hdr->sh_type = SHT_PROGBITS;
}
else if (strcmp (name, ".compact_rel") == 0)
{
hdr->sh_flags = 0;
hdr->sh_type = SHT_PROGBITS;
}
else if (strcmp (name, ".rtproc") == 0)
{
if (hdr->sh_addralign != 0 && hdr->sh_entsize == 0)
{
unsigned int adjust;
adjust = hdr->sh_size % hdr->sh_addralign;
if (adjust != 0)
hdr->sh_size += hdr->sh_addralign - adjust;
}
}
}
return true;
}
boolean
_bfd_mips_elf_section_from_shdr (abfd, hdr, name)
bfd *abfd;
Elf_Internal_Shdr *hdr;
const char *name;
{
flagword flags = 0;
switch (hdr->sh_type)
{
case SHT_MIPS_LIBLIST:
if (strcmp (name, ".liblist") != 0)
return false;
break;
case SHT_MIPS_MSYM:
if (strcmp (name, ".msym") != 0)
return false;
break;
case SHT_MIPS_CONFLICT:
if (strcmp (name, ".conflict") != 0)
return false;
break;
case SHT_MIPS_GPTAB:
if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) != 0)
return false;
break;
case SHT_MIPS_UCODE:
if (strcmp (name, ".ucode") != 0)
return false;
break;
case SHT_MIPS_DEBUG:
if (strcmp (name, ".mdebug") != 0)
return false;
flags = SEC_DEBUGGING;
break;
case SHT_MIPS_REGINFO:
if (strcmp (name, ".reginfo") != 0
|| hdr->sh_size != sizeof (Elf32_External_RegInfo))
return false;
flags = (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_SIZE);
break;
case SHT_MIPS_IFACE:
if (strcmp (name, ".MIPS.interfaces") != 0)
return false;
break;
case SHT_MIPS_CONTENT:
if (strncmp (name, ".MIPS.content", sizeof ".MIPS.content" - 1) != 0)
return false;
break;
case SHT_MIPS_OPTIONS:
if (strcmp (name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) != 0)
return false;
break;
case SHT_MIPS_DWARF:
if (strncmp (name, ".debug_", sizeof ".debug_" - 1) != 0)
return false;
break;
case SHT_MIPS_SYMBOL_LIB:
if (strcmp (name, ".MIPS.symlib") != 0)
return false;
break;
case SHT_MIPS_EVENTS:
if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) != 0
&& strncmp (name, ".MIPS.post_rel",
sizeof ".MIPS.post_rel" - 1) != 0)
return false;
break;
default:
return false;
}
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
return false;
if (flags)
{
if (! bfd_set_section_flags (abfd, hdr->bfd_section,
(bfd_get_section_flags (abfd,
hdr->bfd_section)
| flags)))
return false;
}
if (hdr->sh_type == SHT_MIPS_REGINFO)
{
Elf32_External_RegInfo ext;
Elf32_RegInfo s;
if (! bfd_get_section_contents (abfd, hdr->bfd_section, (PTR) &ext,
(file_ptr) 0,
(bfd_size_type) sizeof ext))
return false;
bfd_mips_elf32_swap_reginfo_in (abfd, &ext, &s);
elf_gp (abfd) = s.ri_gp_value;
}
if (hdr->sh_type == SHT_MIPS_OPTIONS)
{
bfd_byte *contents, *l, *lend;
contents = (bfd_byte *) bfd_malloc (hdr->sh_size);
if (contents == NULL)
return false;
if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents,
(file_ptr) 0, hdr->sh_size))
{
free (contents);
return false;
}
l = contents;
lend = contents + hdr->sh_size;
while (l + sizeof (Elf_External_Options) <= lend)
{
Elf_Internal_Options intopt;
bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
&intopt);
if (ABI_64_P (abfd) && intopt.kind == ODK_REGINFO)
{
Elf64_Internal_RegInfo intreg;
bfd_mips_elf64_swap_reginfo_in
(abfd,
((Elf64_External_RegInfo *)
(l + sizeof (Elf_External_Options))),
&intreg);
elf_gp (abfd) = intreg.ri_gp_value;
}
else if (intopt.kind == ODK_REGINFO)
{
Elf32_RegInfo intreg;
bfd_mips_elf32_swap_reginfo_in
(abfd,
((Elf32_External_RegInfo *)
(l + sizeof (Elf_External_Options))),
&intreg);
elf_gp (abfd) = intreg.ri_gp_value;
}
l += intopt.size;
}
free (contents);
}
return true;
}
boolean
_bfd_mips_elf_fake_sections (abfd, hdr, sec)
bfd *abfd;
Elf32_Internal_Shdr *hdr;
asection *sec;
{
register const char *name;
name = bfd_get_section_name (abfd, sec);
if (strcmp (name, ".liblist") == 0)
{
hdr->sh_type = SHT_MIPS_LIBLIST;
hdr->sh_info = sec->_raw_size / sizeof (Elf32_Lib);
}
else if (strcmp (name, ".conflict") == 0)
hdr->sh_type = SHT_MIPS_CONFLICT;
else if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0)
{
hdr->sh_type = SHT_MIPS_GPTAB;
hdr->sh_entsize = sizeof (Elf32_External_gptab);
}
else if (strcmp (name, ".ucode") == 0)
hdr->sh_type = SHT_MIPS_UCODE;
else if (strcmp (name, ".mdebug") == 0)
{
hdr->sh_type = SHT_MIPS_DEBUG;
if (SGI_COMPAT (abfd) && (abfd->flags & DYNAMIC) != 0)
hdr->sh_entsize = 0;
else
hdr->sh_entsize = 1;
}
else if (strcmp (name, ".reginfo") == 0)
{
hdr->sh_type = SHT_MIPS_REGINFO;
if (SGI_COMPAT (abfd))
{
if ((abfd->flags & DYNAMIC) != 0)
hdr->sh_entsize = sizeof (Elf32_External_RegInfo);
else
hdr->sh_entsize = 1;
}
else
hdr->sh_entsize = sizeof (Elf32_External_RegInfo);
}
else if (SGI_COMPAT (abfd)
&& (strcmp (name, ".hash") == 0
|| strcmp (name, ".dynamic") == 0
|| strcmp (name, ".dynstr") == 0))
{
if (SGI_COMPAT (abfd))
hdr->sh_entsize = 0;
#if 0
hdr->sh_info = SIZEOF_MIPS_DYNSYM_SECNAMES;
#endif
}
else if (strcmp (name, ".got") == 0
|| strcmp (name, ".srdata") == 0
|| strcmp (name, ".sdata") == 0
|| strcmp (name, ".sbss") == 0
|| strcmp (name, ".lit4") == 0
|| strcmp (name, ".lit8") == 0)
hdr->sh_flags |= SHF_MIPS_GPREL;
else if (strcmp (name, ".MIPS.interfaces") == 0)
{
hdr->sh_type = SHT_MIPS_IFACE;
hdr->sh_flags |= SHF_MIPS_NOSTRIP;
}
else if (strncmp (name, ".MIPS.content", strlen (".MIPS.content")) == 0)
{
hdr->sh_type = SHT_MIPS_CONTENT;
hdr->sh_flags |= SHF_MIPS_NOSTRIP;
}
else if (strcmp (name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
{
hdr->sh_type = SHT_MIPS_OPTIONS;
hdr->sh_entsize = 1;
hdr->sh_flags |= SHF_MIPS_NOSTRIP;
}
else if (strncmp (name, ".debug_", sizeof ".debug_" - 1) == 0)
hdr->sh_type = SHT_MIPS_DWARF;
else if (strcmp (name, ".MIPS.symlib") == 0)
{
hdr->sh_type = SHT_MIPS_SYMBOL_LIB;
}
else if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0
|| strncmp (name, ".MIPS.post_rel",
sizeof ".MIPS.post_rel" - 1) == 0)
{
hdr->sh_type = SHT_MIPS_EVENTS;
hdr->sh_flags |= SHF_MIPS_NOSTRIP;
}
else if (strcmp (name, ".msym") == 0)
{
hdr->sh_type = SHT_MIPS_MSYM;
hdr->sh_flags |= SHF_ALLOC;
hdr->sh_entsize = 8;
}
if (! SGI_COMPAT (abfd) && ! NEWABI_P(abfd)
&& (sec->flags & SEC_RELOC) != 0)
{
struct bfd_elf_section_data *esd;
bfd_size_type amt = sizeof (Elf_Internal_Shdr);
esd = elf_section_data (sec);
BFD_ASSERT (esd->rel_hdr2 == NULL);
esd->rel_hdr2 = (Elf_Internal_Shdr *) bfd_zalloc (abfd, amt);
if (!esd->rel_hdr2)
return false;
_bfd_elf_init_reloc_shdr (abfd, esd->rel_hdr2, sec,
!elf_section_data (sec)->use_rela_p);
}
return true;
}
boolean
_bfd_mips_elf_section_from_bfd_section (abfd, sec, retval)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec;
int *retval;
{
if (strcmp (bfd_get_section_name (abfd, sec), ".scommon") == 0)
{
*retval = SHN_MIPS_SCOMMON;
return true;
}
if (strcmp (bfd_get_section_name (abfd, sec), ".acommon") == 0)
{
*retval = SHN_MIPS_ACOMMON;
return true;
}
return false;
}
boolean
_bfd_mips_elf_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
bfd *abfd;
struct bfd_link_info *info;
const Elf_Internal_Sym *sym;
const char **namep;
flagword *flagsp ATTRIBUTE_UNUSED;
asection **secp;
bfd_vma *valp;
{
if (SGI_COMPAT (abfd)
&& (abfd->flags & DYNAMIC) != 0
&& strcmp (*namep, "_rld_new_interface") == 0)
{
*namep = NULL;
return true;
}
switch (sym->st_shndx)
{
case SHN_COMMON:
if (sym->st_size > elf_gp_size (abfd)
|| IRIX_COMPAT (abfd) == ict_irix6)
break;
case SHN_MIPS_SCOMMON:
*secp = bfd_make_section_old_way (abfd, ".scommon");
(*secp)->flags |= SEC_IS_COMMON;
*valp = sym->st_size;
break;
case SHN_MIPS_TEXT:
if (elf_tdata (abfd)->elf_text_section == NULL)
{
asymbol *elf_text_symbol;
asection *elf_text_section;
bfd_size_type amt = sizeof (asection);
elf_text_section = bfd_zalloc (abfd, amt);
if (elf_text_section == NULL)
return false;
amt = sizeof (asymbol);
elf_text_symbol = bfd_zalloc (abfd, amt);
if (elf_text_symbol == NULL)
return false;
elf_tdata (abfd)->elf_text_section = elf_text_section;
elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
elf_text_section->symbol = elf_text_symbol;
elf_text_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_text_symbol;
elf_text_section->name = ".text";
elf_text_section->flags = SEC_NO_FLAGS;
elf_text_section->output_section = NULL;
elf_text_section->owner = abfd;
elf_text_symbol->name = ".text";
elf_text_symbol->flags = BSF_SECTION_SYM | BSF_DYNAMIC;
elf_text_symbol->section = elf_text_section;
}
*secp = elf_tdata (abfd)->elf_text_section;
break;
case SHN_MIPS_ACOMMON:
case SHN_MIPS_DATA:
if (elf_tdata (abfd)->elf_data_section == NULL)
{
asymbol *elf_data_symbol;
asection *elf_data_section;
bfd_size_type amt = sizeof (asection);
elf_data_section = bfd_zalloc (abfd, amt);
if (elf_data_section == NULL)
return false;
amt = sizeof (asymbol);
elf_data_symbol = bfd_zalloc (abfd, amt);
if (elf_data_symbol == NULL)
return false;
elf_tdata (abfd)->elf_data_section = elf_data_section;
elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
elf_data_section->symbol = elf_data_symbol;
elf_data_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_data_symbol;
elf_data_section->name = ".data";
elf_data_section->flags = SEC_NO_FLAGS;
elf_data_section->output_section = NULL;
elf_data_section->owner = abfd;
elf_data_symbol->name = ".data";
elf_data_symbol->flags = BSF_SECTION_SYM | BSF_DYNAMIC;
elf_data_symbol->section = elf_data_section;
}
*secp = elf_tdata (abfd)->elf_data_section;
break;
case SHN_MIPS_SUNDEFINED:
*secp = bfd_und_section_ptr;
break;
}
if (SGI_COMPAT (abfd)
&& ! info->shared
&& info->hash->creator == abfd->xvec
&& strcmp (*namep, "__rld_obj_head") == 0)
{
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, *namep, BSF_GLOBAL, *secp,
(bfd_vma) *valp, (const char *) NULL, false,
get_elf_backend_data (abfd)->collect, &bh)))
return false;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (! bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
mips_elf_hash_table (info)->use_rld_obj_head = true;
}
if (sym->st_other == STO_MIPS16)
++*valp;
return true;
}
boolean
_bfd_mips_elf_link_output_symbol_hook (abfd, info, name, sym, input_sec)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
const char *name ATTRIBUTE_UNUSED;
Elf_Internal_Sym *sym;
asection *input_sec;
{
if (sym->st_shndx == SHN_COMMON
&& strcmp (input_sec->name, ".scommon") == 0)
sym->st_shndx = SHN_MIPS_SCOMMON;
if (sym->st_other == STO_MIPS16
&& (sym->st_value & 1) != 0)
--sym->st_value;
return true;
}
boolean
_bfd_mips_elf_create_dynamic_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
flagword flags;
register asection *s;
const char * const *namep;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED | SEC_READONLY);
s = bfd_get_section_by_name (abfd, ".dynamic");
if (s != NULL)
{
if (! bfd_set_section_flags (abfd, s, flags))
return false;
}
if (! mips_elf_create_got_section (abfd, info))
return false;
if (IRIX_COMPAT (abfd) == ict_irix6
&& !mips_elf_create_msym_section (abfd))
return false;
if (bfd_get_section_by_name (abfd,
MIPS_ELF_STUB_SECTION_NAME (abfd)) == NULL)
{
s = bfd_make_section (abfd, MIPS_ELF_STUB_SECTION_NAME (abfd));
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_CODE)
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return false;
}
if ((IRIX_COMPAT (abfd) == ict_irix5 || IRIX_COMPAT (abfd) == ict_none)
&& !info->shared
&& bfd_get_section_by_name (abfd, ".rld_map") == NULL)
{
s = bfd_make_section (abfd, ".rld_map");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags &~ (flagword) SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return false;
}
if (IRIX_COMPAT (abfd) == ict_irix5)
{
for (namep = mips_elf_dynsym_rtproc_names; *namep != NULL; namep++)
{
bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, *namep, BSF_GLOBAL, bfd_und_section_ptr,
(bfd_vma) 0, (const char *) NULL, false,
get_elf_backend_data (abfd)->collect, &bh)))
return false;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_SECTION;
if (! bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
}
if (SGI_COMPAT (abfd))
{
if (!mips_elf_create_compact_rel_section (abfd, info))
return false;
}
s = bfd_get_section_by_name (abfd, ".hash");
if (s != NULL)
bfd_set_section_alignment (abfd, s, 4);
s = bfd_get_section_by_name (abfd, ".dynsym");
if (s != NULL)
bfd_set_section_alignment (abfd, s, 4);
s = bfd_get_section_by_name (abfd, ".dynstr");
if (s != NULL)
bfd_set_section_alignment (abfd, s, 4);
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s != NULL)
bfd_set_section_alignment (abfd, s, 4);
s = bfd_get_section_by_name (abfd, ".dynamic");
if (s != NULL)
bfd_set_section_alignment (abfd, s, 4);
}
if (!info->shared)
{
const char *name;
name = SGI_COMPAT (abfd) ? "_DYNAMIC_LINK" : "_DYNAMIC_LINKING";
bh = NULL;
if (!(_bfd_generic_link_add_one_symbol
(info, abfd, name, BSF_GLOBAL, bfd_abs_section_ptr,
(bfd_vma) 0, (const char *) NULL, false,
get_elf_backend_data (abfd)->collect, &bh)))
return false;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_SECTION;
if (! bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
if (! mips_elf_hash_table (info)->use_rld_obj_head)
{
s = bfd_get_section_by_name (abfd, ".rld_map");
BFD_ASSERT (s != NULL);
name = SGI_COMPAT (abfd) ? "__rld_map" : "__RLD_MAP";
bh = NULL;
if (!(_bfd_generic_link_add_one_symbol
(info, abfd, name, BSF_GLOBAL, s,
(bfd_vma) 0, (const char *) NULL, false,
get_elf_backend_data (abfd)->collect, &bh)))
return false;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (! bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
}
}
return true;
}
boolean
_bfd_mips_elf_check_relocs (abfd, info, sec, relocs)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
const Elf_Internal_Rela *relocs;
{
const char *name;
bfd *dynobj;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
struct mips_got_info *g;
size_t extsymoff;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *sgot;
asection *sreloc;
struct elf_backend_data *bed;
if (info->relocateable)
return true;
dynobj = elf_hash_table (info)->dynobj;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
name = bfd_get_section_name (abfd, sec);
if (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0)
{
unsigned long r_symndx;
r_symndx = ELF_R_SYM (abfd, relocs->r_info);
if (r_symndx < extsymoff
|| sym_hashes[r_symndx - extsymoff] == NULL)
{
asection *o;
for (o = abfd->sections; o != NULL; o = o->next)
{
Elf_Internal_Rela *sec_relocs;
const Elf_Internal_Rela *r, *rend;
if ((o->flags & SEC_RELOC) == 0
|| o->reloc_count == 0
|| strncmp (bfd_get_section_name (abfd, o), FN_STUB,
sizeof FN_STUB - 1) == 0
|| strncmp (bfd_get_section_name (abfd, o), CALL_STUB,
sizeof CALL_STUB - 1) == 0
|| strncmp (bfd_get_section_name (abfd, o), CALL_FP_STUB,
sizeof CALL_FP_STUB - 1) == 0)
continue;
sec_relocs = (_bfd_elf32_link_read_relocs
(abfd, o, (PTR) NULL,
(Elf_Internal_Rela *) 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)
{
sec->flags |= SEC_EXCLUDE;
return true;
}
if (elf_tdata (abfd)->local_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 = (asection **) bfd_zalloc (abfd, amt);
if (n == NULL)
return false;
elf_tdata (abfd)->local_stubs = n;
}
elf_tdata (abfd)->local_stubs[r_symndx] = sec;
}
else
{
struct mips_elf_link_hash_entry *h;
h = ((struct mips_elf_link_hash_entry *)
sym_hashes[r_symndx - extsymoff]);
h->fn_stub = sec;
mips_elf_hash_table (info)->mips16_stubs_seen = true;
}
}
else if (strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
|| strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
{
unsigned long r_symndx;
struct mips_elf_link_hash_entry *h;
asection **loc;
r_symndx = ELF_R_SYM (abfd, relocs->r_info);
if (r_symndx < extsymoff
|| sym_hashes[r_symndx - extsymoff] == NULL)
{
sec->flags |= SEC_EXCLUDE;
return true;
}
h = ((struct mips_elf_link_hash_entry *)
sym_hashes[r_symndx - extsymoff]);
if (strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
loc = &h->call_fp_stub;
else
loc = &h->call_stub;
if (*loc != NULL || h->root.other == STO_MIPS16)
{
sec->flags |= SEC_EXCLUDE;
return true;
}
*loc = sec;
mips_elf_hash_table (info)->mips16_stubs_seen = true;
}
if (dynobj == NULL)
{
sgot = NULL;
g = NULL;
}
else
{
sgot = mips_elf_got_section (dynobj);
if (sgot == NULL)
g = NULL;
else
{
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
}
}
sreloc = NULL;
bed = get_elf_backend_data (abfd);
rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
for (rel = relocs; rel < rel_end; ++rel)
{
unsigned long r_symndx;
unsigned int r_type;
struct elf_link_hash_entry *h;
r_symndx = ELF_R_SYM (abfd, rel->r_info);
r_type = ELF_R_TYPE (abfd, rel->r_info);
if (r_symndx < extsymoff)
h = NULL;
else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
{
(*_bfd_error_handler)
(_("%s: Malformed reloc detected for section %s"),
bfd_archive_filename (abfd), name);
bfd_set_error (bfd_error_bad_value);
return false;
}
else
{
h = sym_hashes[r_symndx - extsymoff];
if (h != NULL)
{
while (h->root.type == bfd_link_hash_indirect)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
}
if (dynobj == NULL || sgot == NULL)
{
switch (r_type)
{
case R_MIPS_GOT16:
case R_MIPS_CALL16:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT_OFST:
case R_MIPS_GOT_DISP:
if (dynobj == NULL)
elf_hash_table (info)->dynobj = dynobj = abfd;
if (! mips_elf_create_got_section (dynobj, info))
return false;
g = mips_elf_got_info (dynobj, &sgot);
break;
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if (dynobj == NULL
&& (info->shared || h != NULL)
&& (sec->flags & SEC_ALLOC) != 0)
elf_hash_table (info)->dynobj = dynobj = abfd;
break;
default:
break;
}
}
if (!h && (r_type == R_MIPS_CALL_LO16
|| r_type == R_MIPS_GOT_LO16
|| r_type == R_MIPS_GOT_DISP))
{
g->local_gotno++;
sgot->_raw_size += MIPS_ELF_GOT_SIZE (dynobj);
}
switch (r_type)
{
case R_MIPS_CALL16:
if (h == NULL)
{
(*_bfd_error_handler)
(_("%s: CALL16 reloc at 0x%lx not against global symbol"),
bfd_archive_filename (abfd), (unsigned long) rel->r_offset);
bfd_set_error (bfd_error_bad_value);
return false;
}
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
if (h != NULL)
{
if (! mips_elf_record_global_got_symbol (h, info, g))
return false;
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
h->type = STT_FUNC;
}
break;
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_DISP:
if (h && ! mips_elf_record_global_got_symbol (h, info, g))
return false;
break;
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if ((info->shared || h != NULL)
&& (sec->flags & SEC_ALLOC) != 0)
{
if (sreloc == NULL)
{
const char *dname = ".rel.dyn";
sreloc = bfd_get_section_by_name (dynobj, dname);
if (sreloc == NULL)
{
sreloc = bfd_make_section (dynobj, dname);
if (sreloc == NULL
|| ! bfd_set_section_flags (dynobj, sreloc,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED
| SEC_READONLY))
|| ! bfd_set_section_alignment (dynobj, sreloc,
4))
return false;
}
}
#define MIPS_READONLY_SECTION (SEC_ALLOC | SEC_LOAD | SEC_READONLY)
if (info->shared)
{
mips_elf_allocate_dynamic_relocations (dynobj, 1);
if ((sec->flags & MIPS_READONLY_SECTION)
== MIPS_READONLY_SECTION)
info->flags |= DF_TEXTREL;
}
else
{
struct mips_elf_link_hash_entry *hmips;
hmips = (struct mips_elf_link_hash_entry *) h;
++hmips->possibly_dynamic_relocs;
if ((sec->flags & MIPS_READONLY_SECTION)
== MIPS_READONLY_SECTION)
hmips->readonly_reloc = true;
}
if (h != NULL
&& ! mips_elf_record_global_got_symbol (h, info, g))
return false;
}
if (SGI_COMPAT (abfd))
mips_elf_hash_table (info)->compact_rel_size +=
sizeof (Elf32_External_crinfo);
break;
case R_MIPS_26:
case R_MIPS_GPREL16:
case R_MIPS_LITERAL:
case R_MIPS_GPREL32:
if (SGI_COMPAT (abfd))
mips_elf_hash_table (info)->compact_rel_size +=
sizeof (Elf32_External_crinfo);
break;
case R_MIPS_GNU_VTINHERIT:
if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return false;
break;
case R_MIPS_GNU_VTENTRY:
if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_offset))
return false;
break;
default:
break;
}
switch (r_type)
{
default:
if (h != NULL)
{
struct mips_elf_link_hash_entry *mh;
mh = (struct mips_elf_link_hash_entry *) h;
mh->no_fn_stub = true;
}
break;
case R_MIPS_CALL16:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
break;
}
if (h != NULL
&& r_type != R_MIPS16_26
&& strncmp (bfd_get_section_name (abfd, sec), FN_STUB,
sizeof FN_STUB - 1) != 0
&& strncmp (bfd_get_section_name (abfd, sec), CALL_STUB,
sizeof CALL_STUB - 1) != 0
&& strncmp (bfd_get_section_name (abfd, sec), CALL_FP_STUB,
sizeof CALL_FP_STUB - 1) != 0)
{
struct mips_elf_link_hash_entry *mh;
mh = (struct mips_elf_link_hash_entry *) h;
mh->need_fn_stub = true;
}
}
return true;
}
boolean
_bfd_mips_elf_adjust_dynamic_symbol (info, h)
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
{
bfd *dynobj;
struct mips_elf_link_hash_entry *hmips;
asection *s;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
|| h->weakdef != NULL
|| ((h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)));
hmips = (struct mips_elf_link_hash_entry *) h;
if (! info->relocateable
&& hmips->possibly_dynamic_relocs != 0
&& (h->root.type == bfd_link_hash_defweak
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
{
mips_elf_allocate_dynamic_relocations (dynobj,
hmips->possibly_dynamic_relocs);
if (hmips->readonly_reloc)
info->flags |= DF_TEXTREL;
}
if (! hmips->no_fn_stub
&& (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
{
if (! elf_hash_table (info)->dynamic_sections_created)
return true;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
{
s = bfd_get_section_by_name (dynobj,
MIPS_ELF_STUB_SECTION_NAME (dynobj));
BFD_ASSERT (s != NULL);
h->root.u.def.section = s;
h->root.u.def.value = s->_raw_size;
h->plt.offset = s->_raw_size;
s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
return true;
}
}
else if ((h->type == STT_FUNC)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0)
{
h->root.u.def.value = 0;
return true;
}
if (h->weakdef != NULL)
{
BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
|| h->weakdef->root.type == bfd_link_hash_defweak);
h->root.u.def.section = h->weakdef->root.u.def.section;
h->root.u.def.value = h->weakdef->root.u.def.value;
return true;
}
return true;
}
boolean
_bfd_mips_elf_always_size_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
asection *ri;
ri = bfd_get_section_by_name (output_bfd, ".reginfo");
if (ri != NULL)
bfd_set_section_size (output_bfd, ri,
(bfd_size_type) sizeof (Elf32_External_RegInfo));
if (info->relocateable
|| ! mips_elf_hash_table (info)->mips16_stubs_seen)
return true;
mips_elf_link_hash_traverse (mips_elf_hash_table (info),
mips_elf_check_mips16_stubs,
(PTR) NULL);
return true;
}
boolean
_bfd_mips_elf_size_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *s;
boolean reltext;
struct mips_got_info *g = NULL;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
if (elf_hash_table (info)->dynamic_sections_created)
{
if (! info->shared)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->_raw_size
= strlen (ELF_DYNAMIC_INTERPRETER (output_bfd)) + 1;
s->contents
= (bfd_byte *) ELF_DYNAMIC_INTERPRETER (output_bfd);
}
}
reltext = false;
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
boolean strip;
name = bfd_get_section_name (dynobj, s);
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
strip = false;
if (strncmp (name, ".rel", 4) == 0)
{
if (s->_raw_size == 0)
{
if (s->output_section != NULL
&& strcmp (name,
bfd_get_section_name (s->output_section->owner,
s->output_section)) == 0)
strip = true;
}
else
{
const char *outname;
asection *target;
outname = bfd_get_section_name (output_bfd,
s->output_section);
target = bfd_get_section_by_name (output_bfd, outname + 4);
if ((target != NULL
&& (target->flags & SEC_READONLY) != 0
&& (target->flags & SEC_ALLOC) != 0)
|| strcmp (outname, ".rel.dyn") == 0)
reltext = true;
if (strcmp (name, ".rel.dyn") != 0)
s->reloc_count = 0;
}
}
else if (strncmp (name, ".got", 4) == 0)
{
int i;
bfd_size_type loadable_size = 0;
bfd_size_type local_gotno;
bfd *sub;
BFD_ASSERT (elf_section_data (s) != NULL);
g = (struct mips_got_info *) elf_section_data (s)->tdata;
BFD_ASSERT (g != NULL);
for (sub = info->input_bfds; sub; sub = sub->link_next)
{
asection *subsection;
for (subsection = sub->sections;
subsection;
subsection = subsection->next)
{
if ((subsection->flags & SEC_ALLOC) == 0)
continue;
loadable_size += ((subsection->_raw_size + 0xf)
&~ (bfd_size_type) 0xf);
}
}
loadable_size += MIPS_FUNCTION_STUB_SIZE;
local_gotno = (loadable_size >> 16) + 5;
if (NEWABI_P (output_bfd))
local_gotno *= 2;
g->local_gotno += local_gotno;
s->_raw_size += local_gotno * MIPS_ELF_GOT_SIZE (dynobj);
if (! mips_elf_sort_hash_table (info, 1))
return false;
if (g->global_gotsym != NULL)
i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
else
i = 0;
g->global_gotno = i;
s->_raw_size += i * MIPS_ELF_GOT_SIZE (dynobj);
}
else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
{
s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
}
else if (! info->shared
&& ! mips_elf_hash_table (info)->use_rld_obj_head
&& strncmp (name, ".rld_map", 8) == 0)
{
s->_raw_size += 4;
}
else if (SGI_COMPAT (output_bfd)
&& strncmp (name, ".compact_rel", 12) == 0)
s->_raw_size += mips_elf_hash_table (info)->compact_rel_size;
else if (strcmp (name, ".msym") == 0)
s->_raw_size = (sizeof (Elf32_External_Msym)
* (elf_hash_table (info)->dynsymcount
+ bfd_count_sections (output_bfd)));
else if (strncmp (name, ".init", 5) != 0)
{
continue;
}
if (strip)
{
_bfd_strip_section_from_output (info, s);
continue;
}
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
if (s->contents == NULL && s->_raw_size != 0)
{
bfd_set_error (bfd_error_no_memory);
return false;
}
}
if (elf_hash_table (info)->dynamic_sections_created)
{
if (! info->shared)
{
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
{
if (!SGI_COMPAT (output_bfd))
{
if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
return false;
}
}
if (reltext && SGI_COMPAT (output_bfd))
info->flags |= DF_TEXTREL;
if ((info->flags & DF_TEXTREL) != 0)
{
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_TEXTREL, 0))
return false;
}
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0))
return false;
if (bfd_get_section_by_name (dynobj, ".rel.dyn"))
{
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELSZ, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0))
return false;
}
if (SGI_COMPAT (output_bfd))
{
if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_CONFLICTNO, 0))
return false;
}
if (SGI_COMPAT (output_bfd))
{
if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LIBLISTNO, 0))
return false;
}
if (bfd_get_section_by_name (dynobj, ".conflict") != NULL)
{
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_CONFLICT, 0))
return false;
s = bfd_get_section_by_name (dynobj, ".liblist");
BFD_ASSERT (s != NULL);
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LIBLIST, 0))
return false;
}
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_VERSION, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0))
return false;
#if 0
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_TIME_STAMP, 0))
return false;
#endif
#if 0
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_ICHECKSUM, 0))
return false;
#endif
#if 0
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_IVERSION, 0))
return false;
#endif
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LOCAL_GOTNO, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_SYMTABNO, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_UNREFEXTNO, 0))
return false;
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
return false;
if (IRIX_COMPAT (dynobj) == ict_irix5
&& ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0))
return false;
if (IRIX_COMPAT (dynobj) == ict_irix6
&& (bfd_get_section_by_name
(dynobj, MIPS_ELF_OPTIONS_SECTION_NAME (dynobj)))
&& !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_OPTIONS, 0))
return false;
if (bfd_get_section_by_name (dynobj, ".msym")
&& !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_MSYM, 0))
return false;
}
return true;
}
boolean
_bfd_mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, local_syms, local_sections)
bfd *output_bfd;
struct bfd_link_info *info;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
Elf_Internal_Rela *relocs;
Elf_Internal_Sym *local_syms;
asection **local_sections;
{
Elf_Internal_Rela *rel;
const Elf_Internal_Rela *relend;
bfd_vma addend = 0;
boolean use_saved_addend_p = false;
struct elf_backend_data *bed;
bed = get_elf_backend_data (output_bfd);
relend = relocs + input_section->reloc_count * bed->s->int_rels_per_ext_rel;
for (rel = relocs; rel < relend; ++rel)
{
const char *name;
bfd_vma value;
reloc_howto_type *howto;
boolean require_jalx;
boolean rela_relocation_p = true;
unsigned int r_type = ELF_R_TYPE (output_bfd, rel->r_info);
const char * msg = (const char *) NULL;
if (r_type == R_MIPS_64 && ! NEWABI_P (input_bfd))
{
howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, R_MIPS_32, false);
if (bfd_big_endian (input_bfd))
rel->r_offset += 4;
}
else
howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type,
NEWABI_P (input_bfd));
if (!use_saved_addend_p)
{
Elf_Internal_Shdr *rel_hdr;
rel_hdr = &elf_section_data (input_section)->rel_hdr;
if ((size_t) (rel - relocs)
>= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
rel_hdr = elf_section_data (input_section)->rel_hdr2;
if (rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (input_bfd))
{
rela_relocation_p = false;
addend = mips_elf_obtain_contents (howto, rel, input_bfd,
contents);
addend &= howto->src_mask;
addend <<= howto->rightshift;
if (r_type == R_MIPS_HI16
|| r_type == R_MIPS_GNU_REL_HI16
|| (r_type == R_MIPS_GOT16
&& 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;
unsigned int lo;
if (r_type == R_MIPS_GNU_REL_HI16)
lo = R_MIPS_GNU_REL_LO16;
else
lo = R_MIPS_LO16;
lo16_relocation = mips_elf_next_relocation (input_bfd, lo,
rel, relend);
if (lo16_relocation == NULL)
return false;
lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, lo, false);
l = mips_elf_obtain_contents (lo16_howto, lo16_relocation,
input_bfd, contents);
l &= lo16_howto->src_mask;
l <<= lo16_howto->rightshift;
l = mips_elf_sign_extend (l, 16);
addend <<= 16;
addend += l;
if (r_type == R_MIPS_GNU_REL_HI16)
addend -= (lo16_relocation->r_offset - rel->r_offset);
}
else if (r_type == R_MIPS16_GPREL)
{
addend = (((addend & 0x1f0000) >> 5)
| ((addend & 0x7e00000) >> 16)
| (addend & 0x1f));
}
}
else
addend = rel->r_addend;
}
if (info->relocateable)
{
Elf_Internal_Sym *sym;
unsigned long r_symndx;
if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd)
&& bfd_big_endian (input_bfd))
rel->r_offset -= 4;
if (! mips_elf_local_relocation_p (input_bfd, rel, local_sections,
false))
continue;
if (r_type == R_MIPS16_GPREL
|| r_type == R_MIPS_GPREL16
|| r_type == R_MIPS_GPREL32
|| r_type == R_MIPS_LITERAL)
addend -= (_bfd_get_gp_value (output_bfd)
- _bfd_get_gp_value (input_bfd));
r_symndx = ELF_R_SYM (output_bfd, rel->r_info);
sym = local_syms + r_symndx;
if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
addend += local_sections[r_symndx]->output_offset;
if (howto->partial_inplace)
{
if (r_type == R_MIPS_HI16 || r_type == R_MIPS_GOT16
|| r_type == R_MIPS_GNU_REL_HI16)
addend = mips_elf_high (addend);
else if (r_type == R_MIPS_HIGHER)
addend = mips_elf_higher (addend);
else if (r_type == R_MIPS_HIGHEST)
addend = mips_elf_highest (addend);
}
if (rela_relocation_p)
rel->r_addend = addend;
else
{
addend >>= howto->rightshift;
addend &= howto->src_mask;
if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd))
{
bfd_vma sign_bits;
bfd_vma low_bits;
bfd_vma high_bits;
if (addend & ((bfd_vma) 1 << 31))
#ifdef BFD64
sign_bits = ((bfd_vma) 1 << 32) - 1;
#else
sign_bits = -1;
#endif
else
sign_bits = 0;
if (bfd_big_endian (input_bfd))
{
low_bits = sign_bits;
high_bits = addend;
}
else
{
low_bits = addend;
high_bits = sign_bits;
}
bfd_put_32 (input_bfd, low_bits,
contents + rel->r_offset);
bfd_put_32 (input_bfd, high_bits,
contents + rel->r_offset + 4);
continue;
}
if (! mips_elf_perform_relocation (info, howto, rel, addend,
input_bfd, input_section,
contents, false))
return false;
}
continue;
}
if (rel + 1 < relend
&& rel->r_offset == rel[1].r_offset
&& ELF_R_TYPE (input_bfd, rel[1].r_info) != R_MIPS_NONE)
use_saved_addend_p = true;
else
use_saved_addend_p = false;
addend >>= howto->rightshift;
switch (mips_elf_calculate_relocation (output_bfd, input_bfd,
input_section, info, rel,
addend, howto, local_syms,
local_sections, &value,
&name, &require_jalx,
use_saved_addend_p))
{
case bfd_reloc_continue:
continue;
case bfd_reloc_undefined:
continue;
case bfd_reloc_notsupported:
msg = _("internal error: unsupported relocation error");
info->callbacks->warning
(info, msg, name, input_bfd, input_section, rel->r_offset);
return false;
case bfd_reloc_overflow:
if (use_saved_addend_p)
;
else
{
BFD_ASSERT (name != NULL);
if (! ((*info->callbacks->reloc_overflow)
(info, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
return false;
}
break;
case bfd_reloc_ok:
break;
default:
abort ();
break;
}
if (use_saved_addend_p)
{
addend = value;
continue;
}
if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd))
{
bfd_vma sign_bits;
bfd_vma low_bits;
bfd_vma high_bits;
if (value & ((bfd_vma) 1 << 31))
#ifdef BFD64
sign_bits = ((bfd_vma) 1 << 32) - 1;
#else
sign_bits = -1;
#endif
else
sign_bits = 0;
if (bfd_big_endian (input_bfd))
{
rel->r_offset -= 4;
low_bits = sign_bits;
high_bits = value;
}
else
{
low_bits = value;
high_bits = sign_bits;
}
bfd_put_32 (input_bfd, low_bits,
contents + rel->r_offset);
bfd_put_32 (input_bfd, high_bits,
contents + rel->r_offset + 4);
continue;
}
if (! mips_elf_perform_relocation (info, howto, rel, value,
input_bfd, input_section,
contents, require_jalx))
return false;
}
return true;
}
static void
mips_elf_irix6_finish_dynamic_symbol (abfd, name, sym)
bfd *abfd ATTRIBUTE_UNUSED;
const char *name;
Elf_Internal_Sym *sym;
{
static const char* const text_section_symbols[] = {
"_ftext",
"_etext",
"__dso_displacement",
"__elf_header",
"__program_header_table",
NULL
};
static const char* const data_section_symbols[] = {
"_fdata",
"_edata",
"_end",
"_fbss",
NULL
};
const char* const *p;
int i;
for (i = 0; i < 2; ++i)
for (p = (i == 0) ? text_section_symbols : data_section_symbols;
*p;
++p)
if (strcmp (*p, name) == 0)
{
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
if (i == 0)
sym->st_shndx = SHN_MIPS_TEXT;
else
sym->st_shndx = SHN_MIPS_DATA;
break;
}
}
boolean
_bfd_mips_elf_finish_dynamic_symbol (output_bfd, info, h, sym)
bfd *output_bfd;
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
bfd *dynobj;
bfd_vma gval;
asection *sgot;
asection *smsym;
struct mips_got_info *g;
const char *name;
struct mips_elf_link_hash_entry *mh;
dynobj = elf_hash_table (info)->dynobj;
gval = sym->st_value;
mh = (struct mips_elf_link_hash_entry *) h;
if (h->plt.offset != (bfd_vma) -1)
{
asection *s;
bfd_byte stub[MIPS_FUNCTION_STUB_SIZE];
BFD_ASSERT (h->dynindx != -1);
s = bfd_get_section_by_name (dynobj,
MIPS_ELF_STUB_SECTION_NAME (dynobj));
BFD_ASSERT (s != NULL);
if (h->dynindx & 0xffff0000)
return false;
bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub);
bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + 4);
bfd_put_32 (output_bfd, STUB_JALR, stub + 8);
bfd_put_32 (output_bfd, STUB_LI16 (output_bfd) + h->dynindx, stub + 12);
BFD_ASSERT (h->plt.offset <= s->_raw_size);
memcpy (s->contents + h->plt.offset, stub, MIPS_FUNCTION_STUB_SIZE);
sym->st_shndx = SHN_UNDEF;
gval = s->output_section->vma + s->output_offset + h->plt.offset;
sym->st_value = gval;
}
BFD_ASSERT (h->dynindx != -1
|| (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0);
sgot = mips_elf_got_section (dynobj);
BFD_ASSERT (sgot != NULL);
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
if (g->global_gotsym != NULL
&& h->dynindx >= g->global_gotsym->dynindx)
{
bfd_vma offset;
bfd_vma value;
if (sym->st_value)
value = sym->st_value;
else
{
if (info->shared && h->root.type == bfd_link_hash_undefined)
value = 0;
else
value = h->root.u.def.value;
}
offset = mips_elf_global_got_index (dynobj, h);
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
smsym = bfd_get_section_by_name (dynobj, ".msym");
if (smsym)
{
Elf32_Internal_Msym msym;
msym.ms_hash_value = bfd_elf_hash (h->root.root.string);
msym.ms_info = ELF32_MS_INFO (mh->min_dyn_reloc_index, 1);
bfd_mips_elf_swap_msym_out
(dynobj, &msym,
((Elf32_External_Msym *) smsym->contents) + h->dynindx);
}
name = h->root.root.string;
if (strcmp (name, "_DYNAMIC") == 0
|| strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
sym->st_shndx = SHN_ABS;
else if (strcmp (name, "_DYNAMIC_LINK") == 0
|| strcmp (name, "_DYNAMIC_LINKING") == 0)
{
sym->st_shndx = SHN_ABS;
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
sym->st_value = 1;
}
else if (strcmp (name, "_gp_disp") == 0 && ! NEWABI_P (output_bfd))
{
sym->st_shndx = SHN_ABS;
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
sym->st_value = elf_gp (output_bfd);
}
else if (SGI_COMPAT (output_bfd))
{
if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0
|| strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0)
{
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
sym->st_other = STO_PROTECTED;
sym->st_value = 0;
sym->st_shndx = SHN_MIPS_DATA;
}
else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0)
{
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
sym->st_other = STO_PROTECTED;
sym->st_value = mips_elf_hash_table (info)->procedure_count;
sym->st_shndx = SHN_ABS;
}
else if (sym->st_shndx != SHN_UNDEF && sym->st_shndx != SHN_ABS)
{
if (h->type == STT_FUNC)
sym->st_shndx = SHN_MIPS_TEXT;
else if (h->type == STT_OBJECT)
sym->st_shndx = SHN_MIPS_DATA;
}
}
if (IRIX_COMPAT (output_bfd) == ict_irix6)
mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
if (! info->shared)
{
if (! mips_elf_hash_table (info)->use_rld_obj_head
&& (strcmp (name, "__rld_map") == 0
|| strcmp (name, "__RLD_MAP") == 0))
{
asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
BFD_ASSERT (s != NULL);
sym->st_value = s->output_section->vma + s->output_offset;
bfd_put_32 (output_bfd, (bfd_vma) 0, s->contents);
if (mips_elf_hash_table (info)->rld_value == 0)
mips_elf_hash_table (info)->rld_value = sym->st_value;
}
else if (mips_elf_hash_table (info)->use_rld_obj_head
&& strcmp (name, "__rld_obj_head") == 0)
{
if (IRIX_COMPAT (output_bfd) == ict_irix5
|| IRIX_COMPAT (output_bfd) == ict_none)
BFD_ASSERT (bfd_get_section_by_name (dynobj, ".rld_map")
!= NULL);
mips_elf_hash_table (info)->rld_value = sym->st_value;
}
}
if (sym->st_other == STO_MIPS16
&& (sym->st_value & 1) != 0)
--sym->st_value;
return true;
}
boolean
_bfd_mips_elf_finish_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *sdyn;
asection *sgot;
struct mips_got_info *g;
dynobj = elf_hash_table (info)->dynobj;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
sgot = bfd_get_section_by_name (dynobj, ".got");
if (sgot == NULL)
g = NULL;
else
{
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
}
if (elf_hash_table (info)->dynamic_sections_created)
{
bfd_byte *b;
BFD_ASSERT (sdyn != NULL);
BFD_ASSERT (g != NULL);
for (b = sdyn->contents;
b < sdyn->contents + sdyn->_raw_size;
b += MIPS_ELF_DYN_SIZE (dynobj))
{
Elf_Internal_Dyn dyn;
const char *name;
size_t elemsize;
asection *s;
boolean swap_out_p;
(*get_elf_backend_data (dynobj)->s->swap_dyn_in) (dynobj, b, &dyn);
swap_out_p = true;
switch (dyn.d_tag)
{
case DT_RELENT:
s = (bfd_get_section_by_name (dynobj, ".rel.dyn"));
BFD_ASSERT (s != NULL);
dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj);
break;
case DT_STRSZ:
dyn.d_un.d_val =
_bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
break;
case DT_PLTGOT:
name = ".got";
goto get_vma;
case DT_MIPS_CONFLICT:
name = ".conflict";
goto get_vma;
case DT_MIPS_LIBLIST:
name = ".liblist";
get_vma:
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma;
break;
case DT_MIPS_RLD_VERSION:
dyn.d_un.d_val = 1;
break;
case DT_MIPS_FLAGS:
dyn.d_un.d_val = RHF_NOTPOT;
break;
case DT_MIPS_CONFLICTNO:
name = ".conflict";
elemsize = sizeof (Elf32_Conflict);
goto set_elemno;
case DT_MIPS_LIBLISTNO:
name = ".liblist";
elemsize = sizeof (Elf32_Lib);
set_elemno:
s = bfd_get_section_by_name (output_bfd, name);
if (s != NULL)
{
if (s->_cooked_size != 0)
dyn.d_un.d_val = s->_cooked_size / elemsize;
else
dyn.d_un.d_val = s->_raw_size / elemsize;
}
else
dyn.d_un.d_val = 0;
break;
case DT_MIPS_TIME_STAMP:
time ((time_t *) &dyn.d_un.d_val);
break;
case DT_MIPS_ICHECKSUM:
swap_out_p = false;
break;
case DT_MIPS_IVERSION:
swap_out_p = false;
break;
case DT_MIPS_BASE_ADDRESS:
s = output_bfd->sections;
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma & ~(bfd_vma) 0xffff;
break;
case DT_MIPS_LOCAL_GOTNO:
dyn.d_un.d_val = g->local_gotno;
break;
case DT_MIPS_UNREFEXTNO:
dyn.d_un.d_val = bfd_count_sections (output_bfd) + 1;
break;
case DT_MIPS_GOTSYM:
if (g->global_gotsym)
{
dyn.d_un.d_val = g->global_gotsym->dynindx;
break;
}
case DT_MIPS_SYMTABNO:
name = ".dynsym";
elemsize = MIPS_ELF_SYM_SIZE (output_bfd);
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
if (s->_cooked_size != 0)
dyn.d_un.d_val = s->_cooked_size / elemsize;
else
dyn.d_un.d_val = s->_raw_size / elemsize;
break;
case DT_MIPS_HIPAGENO:
dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO;
break;
case DT_MIPS_RLD_MAP:
dyn.d_un.d_ptr = mips_elf_hash_table (info)->rld_value;
break;
case DT_MIPS_OPTIONS:
s = (bfd_get_section_by_name
(output_bfd, MIPS_ELF_OPTIONS_SECTION_NAME (output_bfd)));
dyn.d_un.d_ptr = s->vma;
break;
case DT_MIPS_MSYM:
s = (bfd_get_section_by_name (output_bfd, ".msym"));
dyn.d_un.d_ptr = s->vma;
break;
default:
swap_out_p = false;
break;
}
if (swap_out_p)
(*get_elf_backend_data (dynobj)->s->swap_dyn_out)
(dynobj, &dyn, b);
}
}
if (sgot != NULL && sgot->_raw_size > 0)
{
MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0, sgot->contents);
MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0x80000000,
sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd));
}
if (sgot != NULL)
elf_section_data (sgot->output_section)->this_hdr.sh_entsize
= MIPS_ELF_GOT_SIZE (output_bfd);
{
asection *smsym;
asection *s;
Elf32_compact_rel cpt;
smsym = bfd_get_section_by_name (dynobj, ".msym");
if (smsym != NULL)
{
Elf32_Internal_Msym msym;
msym.ms_hash_value = 0;
msym.ms_info = ELF32_MS_INFO (0, 1);
for (s = output_bfd->sections; s != NULL; s = s->next)
{
long dynindx = elf_section_data (s)->dynindx;
bfd_mips_elf_swap_msym_out
(output_bfd, &msym,
(((Elf32_External_Msym *) smsym->contents)
+ dynindx));
}
}
if (SGI_COMPAT (output_bfd))
{
s = bfd_get_section_by_name (dynobj, ".compact_rel");
if (s != NULL)
{
cpt.id1 = 1;
cpt.num = s->reloc_count;
cpt.id2 = 2;
cpt.offset = (s->output_section->filepos
+ sizeof (Elf32_External_compact_rel));
cpt.reserved0 = 0;
cpt.reserved1 = 0;
bfd_elf32_swap_compact_rel_out (output_bfd, &cpt,
((Elf32_External_compact_rel *)
s->contents));
s = bfd_get_section_by_name (dynobj,
MIPS_ELF_STUB_SECTION_NAME (dynobj));
if (s != NULL)
{
file_ptr dummy_offset;
BFD_ASSERT (s->_raw_size >= MIPS_FUNCTION_STUB_SIZE);
dummy_offset = s->_raw_size - MIPS_FUNCTION_STUB_SIZE;
memset (s->contents + dummy_offset, 0,
MIPS_FUNCTION_STUB_SIZE);
}
}
}
if (!ABI_64_P (output_bfd))
{
asection *reldyn;
reldyn = bfd_get_section_by_name (dynobj, ".rel.dyn");
if (reldyn != NULL && reldyn->reloc_count > 2)
{
reldyn_sorting_bfd = output_bfd;
qsort ((Elf32_External_Rel *) reldyn->contents + 1,
(size_t) reldyn->reloc_count - 1,
sizeof (Elf32_External_Rel), sort_dynamic_relocs);
}
}
s = bfd_get_section_by_name (dynobj, ".rel.dyn");
if (s != NULL && s->_raw_size > 0)
memset (s->contents, 0, MIPS_ELF_REL_SIZE (dynobj));
}
return true;
}
void
_bfd_mips_elf_final_write_processing (abfd, linker)
bfd *abfd;
boolean linker ATTRIBUTE_UNUSED;
{
unsigned long val;
unsigned int i;
Elf_Internal_Shdr **hdrpp;
const char *name;
asection *sec;
switch (bfd_get_mach (abfd))
{
default:
case bfd_mach_mips3000:
val = E_MIPS_ARCH_1;
break;
case bfd_mach_mips3900:
val = E_MIPS_ARCH_1 | E_MIPS_MACH_3900;
break;
case bfd_mach_mips6000:
val = E_MIPS_ARCH_2;
break;
case bfd_mach_mips4000:
case bfd_mach_mips4300:
case bfd_mach_mips4400:
case bfd_mach_mips4600:
val = E_MIPS_ARCH_3;
break;
case bfd_mach_mips4010:
val = E_MIPS_ARCH_3 | E_MIPS_MACH_4010;
break;
case bfd_mach_mips4100:
val = E_MIPS_ARCH_3 | E_MIPS_MACH_4100;
break;
case bfd_mach_mips4111:
val = E_MIPS_ARCH_3 | E_MIPS_MACH_4111;
break;
case bfd_mach_mips4120:
val = E_MIPS_ARCH_3 | E_MIPS_MACH_4120;
break;
case bfd_mach_mips4650:
val = E_MIPS_ARCH_3 | E_MIPS_MACH_4650;
break;
case bfd_mach_mips5400:
val = E_MIPS_ARCH_4 | E_MIPS_MACH_5400;
break;
case bfd_mach_mips5500:
val = E_MIPS_ARCH_4 | E_MIPS_MACH_5500;
break;
case bfd_mach_mips5000:
case bfd_mach_mips8000:
case bfd_mach_mips10000:
case bfd_mach_mips12000:
val = E_MIPS_ARCH_4;
break;
case bfd_mach_mips5:
val = E_MIPS_ARCH_5;
break;
case bfd_mach_mips_sb1:
val = E_MIPS_ARCH_64 | E_MIPS_MACH_SB1;
break;
case bfd_mach_mipsisa32:
val = E_MIPS_ARCH_32;
break;
case bfd_mach_mipsisa64:
val = E_MIPS_ARCH_64;
}
elf_elfheader (abfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
elf_elfheader (abfd)->e_flags |= val;
for (i = 1, hdrpp = elf_elfsections (abfd) + 1;
i < elf_numsections (abfd);
i++, hdrpp++)
{
switch ((*hdrpp)->sh_type)
{
case SHT_MIPS_MSYM:
case SHT_MIPS_LIBLIST:
sec = bfd_get_section_by_name (abfd, ".dynstr");
if (sec != NULL)
(*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
break;
case SHT_MIPS_GPTAB:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL
&& strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0);
sec = bfd_get_section_by_name (abfd, name + sizeof ".gptab" - 1);
BFD_ASSERT (sec != NULL);
(*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
break;
case SHT_MIPS_CONTENT:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL
&& strncmp (name, ".MIPS.content",
sizeof ".MIPS.content" - 1) == 0);
sec = bfd_get_section_by_name (abfd,
name + sizeof ".MIPS.content" - 1);
BFD_ASSERT (sec != NULL);
(*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
break;
case SHT_MIPS_SYMBOL_LIB:
sec = bfd_get_section_by_name (abfd, ".dynsym");
if (sec != NULL)
(*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
sec = bfd_get_section_by_name (abfd, ".liblist");
if (sec != NULL)
(*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
break;
case SHT_MIPS_EVENTS:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL);
if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0)
sec = bfd_get_section_by_name (abfd,
name + sizeof ".MIPS.events" - 1);
else
{
BFD_ASSERT (strncmp (name, ".MIPS.post_rel",
sizeof ".MIPS.post_rel" - 1) == 0);
sec = bfd_get_section_by_name (abfd,
(name
+ sizeof ".MIPS.post_rel" - 1));
}
BFD_ASSERT (sec != NULL);
(*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
break;
}
}
}
int
_bfd_mips_elf_additional_program_headers (abfd)
bfd *abfd;
{
asection *s;
int ret = 0;
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s && (s->flags & SEC_LOAD))
++ret;
if (IRIX_COMPAT (abfd) == ict_irix6
&& bfd_get_section_by_name (abfd,
MIPS_ELF_OPTIONS_SECTION_NAME (abfd)))
++ret;
if (IRIX_COMPAT (abfd) == ict_irix5
&& bfd_get_section_by_name (abfd, ".dynamic")
&& bfd_get_section_by_name (abfd, ".mdebug"))
++ret;
return ret;
}
boolean
_bfd_mips_elf_modify_segment_map (abfd)
bfd *abfd;
{
asection *s;
struct elf_segment_map *m, **pm;
bfd_size_type amt;
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s != NULL && (s->flags & SEC_LOAD) != 0)
{
for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
if (m->p_type == PT_MIPS_REGINFO)
break;
if (m == NULL)
{
amt = sizeof *m;
m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
if (m == NULL)
return false;
m->p_type = PT_MIPS_REGINFO;
m->count = 1;
m->sections[0] = s;
pm = &elf_tdata (abfd)->segment_map;
while (*pm != NULL
&& ((*pm)->p_type == PT_PHDR
|| (*pm)->p_type == PT_INTERP))
pm = &(*pm)->next;
m->next = *pm;
*pm = m;
}
}
if (NEWABI_P (abfd))
{
for (s = abfd->sections; s; s = s->next)
if (elf_section_data (s)->this_hdr.sh_type == SHT_MIPS_OPTIONS)
break;
if (s)
{
struct elf_segment_map *options_segment;
for (pm = &elf_tdata (abfd)->segment_map;
*pm != NULL;
pm = &(*pm)->next)
if ((*pm)->p_type == PT_PHDR)
break;
amt = sizeof (struct elf_segment_map);
options_segment = bfd_zalloc (abfd, amt);
options_segment->next = *pm;
options_segment->p_type = PT_MIPS_OPTIONS;
options_segment->p_flags = PF_R;
options_segment->p_flags_valid = true;
options_segment->count = 1;
options_segment->sections[0] = s;
*pm = options_segment;
}
}
else
{
if (IRIX_COMPAT (abfd) == ict_irix5)
{
if (bfd_get_section_by_name (abfd, ".interp") == NULL
&& bfd_get_section_by_name (abfd, ".dynamic") != NULL
&& bfd_get_section_by_name (abfd, ".mdebug") != NULL)
{
for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
if (m->p_type == PT_MIPS_RTPROC)
break;
if (m == NULL)
{
amt = sizeof *m;
m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
if (m == NULL)
return false;
m->p_type = PT_MIPS_RTPROC;
s = bfd_get_section_by_name (abfd, ".rtproc");
if (s == NULL)
{
m->count = 0;
m->p_flags = 0;
m->p_flags_valid = 1;
}
else
{
m->count = 1;
m->sections[0] = s;
}
pm = &elf_tdata (abfd)->segment_map;
while (*pm != NULL && (*pm)->p_type != PT_DYNAMIC)
pm = &(*pm)->next;
if (*pm != NULL)
pm = &(*pm)->next;
m->next = *pm;
*pm = m;
}
}
}
for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL;
pm = &(*pm)->next)
if ((*pm)->p_type == PT_DYNAMIC)
break;
m = *pm;
if (m != NULL && IRIX_COMPAT (abfd) == ict_none)
{
if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
{
m->p_flags = PF_R | PF_W | PF_X;
m->p_flags_valid = 1;
}
}
if (m != NULL
&& m->count == 1 && strcmp (m->sections[0]->name, ".dynamic") == 0)
{
static const char *sec_names[] =
{
".dynamic", ".dynstr", ".dynsym", ".hash"
};
bfd_vma low, high;
unsigned int i, c;
struct elf_segment_map *n;
low = 0xffffffff;
high = 0;
for (i = 0; i < sizeof sec_names / sizeof sec_names[0]; i++)
{
s = bfd_get_section_by_name (abfd, sec_names[i]);
if (s != NULL && (s->flags & SEC_LOAD) != 0)
{
bfd_size_type sz;
if (low > s->vma)
low = s->vma;
sz = s->_cooked_size;
if (sz == 0)
sz = s->_raw_size;
if (high < s->vma + sz)
high = s->vma + sz;
}
}
c = 0;
for (s = abfd->sections; s != NULL; s = s->next)
if ((s->flags & SEC_LOAD) != 0
&& s->vma >= low
&& ((s->vma
+ (s->_cooked_size !=
0 ? s->_cooked_size : s->_raw_size)) <= high))
++c;
amt = sizeof *n + (bfd_size_type) (c - 1) * sizeof (asection *);
n = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
if (n == NULL)
return false;
*n = *m;
n->count = c;
i = 0;
for (s = abfd->sections; s != NULL; s = s->next)
{
if ((s->flags & SEC_LOAD) != 0
&& s->vma >= low
&& ((s->vma
+ (s->_cooked_size != 0 ?
s->_cooked_size : s->_raw_size)) <= high))
{
n->sections[i] = s;
++i;
}
}
*pm = n;
}
}
return true;
}
asection *
_bfd_mips_elf_gc_mark_hook (sec, info, rel, h, sym)
asection *sec;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
Elf_Internal_Rela *rel;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
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);
return NULL;
}
boolean
_bfd_mips_elf_gc_sweep_hook (abfd, info, sec, relocs)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
{
#if 0
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
switch (ELF_R_TYPE (abfd, rel->r_info))
{
case R_MIPS_GOT16:
case R_MIPS_CALL16:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_DISP:
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT_OFST:
break;
default:
break;
}
#endif
return true;
}
void
_bfd_mips_elf_copy_indirect_symbol (bed, dir, ind)
struct elf_backend_data *bed;
struct elf_link_hash_entry *dir, *ind;
{
struct mips_elf_link_hash_entry *dirmips, *indmips;
_bfd_elf_link_hash_copy_indirect (bed, dir, ind);
if (ind->root.type != bfd_link_hash_indirect)
return;
dirmips = (struct mips_elf_link_hash_entry *) dir;
indmips = (struct mips_elf_link_hash_entry *) ind;
dirmips->possibly_dynamic_relocs += indmips->possibly_dynamic_relocs;
if (indmips->readonly_reloc)
dirmips->readonly_reloc = true;
if (dirmips->min_dyn_reloc_index == 0
|| (indmips->min_dyn_reloc_index != 0
&& indmips->min_dyn_reloc_index < dirmips->min_dyn_reloc_index))
dirmips->min_dyn_reloc_index = indmips->min_dyn_reloc_index;
if (indmips->no_fn_stub)
dirmips->no_fn_stub = true;
}
void
_bfd_mips_elf_hide_symbol (info, entry, force_local)
struct bfd_link_info *info;
struct elf_link_hash_entry *entry;
boolean force_local;
{
bfd *dynobj;
asection *got;
struct mips_got_info *g;
struct mips_elf_link_hash_entry *h;
h = (struct mips_elf_link_hash_entry *) entry;
if (h->forced_local)
return;
h->forced_local = true;
dynobj = elf_hash_table (info)->dynobj;
got = bfd_get_section_by_name (dynobj, ".got");
g = (struct mips_got_info *) elf_section_data (got)->tdata;
_bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
g->local_gotno++;
got->_raw_size += MIPS_ELF_GOT_SIZE (dynobj);
}
#define PDR_SIZE 32
boolean
_bfd_mips_elf_discard_info (abfd, cookie, info)
bfd *abfd;
struct elf_reloc_cookie *cookie;
struct bfd_link_info *info;
{
asection *o;
boolean ret = false;
unsigned char *tdata;
size_t i, skip;
o = bfd_get_section_by_name (abfd, ".pdr");
if (! o)
return false;
if (o->_raw_size == 0)
return false;
if (o->_raw_size % PDR_SIZE != 0)
return false;
if (o->output_section != NULL
&& bfd_is_abs_section (o->output_section))
return false;
tdata = bfd_zmalloc (o->_raw_size / PDR_SIZE);
if (! tdata)
return false;
cookie->rels = _bfd_elf32_link_read_relocs (abfd, o, (PTR) NULL,
(Elf_Internal_Rela *) NULL,
info->keep_memory);
if (!cookie->rels)
{
free (tdata);
return false;
}
cookie->rel = cookie->rels;
cookie->relend = cookie->rels + o->reloc_count;
for (i = 0, skip = 0; i < o->_raw_size; i ++)
{
if (_bfd_elf32_reloc_symbol_deleted_p (i * PDR_SIZE, cookie))
{
tdata[i] = 1;
skip ++;
}
}
if (skip != 0)
{
elf_section_data (o)->tdata = tdata;
o->_cooked_size = o->_raw_size - skip * PDR_SIZE;
ret = true;
}
else
free (tdata);
if (! info->keep_memory)
free (cookie->rels);
return ret;
}
boolean
_bfd_mips_elf_ignore_discarded_relocs (sec)
asection *sec;
{
if (strcmp (sec->name, ".pdr") == 0)
return true;
return false;
}
boolean
_bfd_mips_elf_write_section (output_bfd, sec, contents)
bfd *output_bfd;
asection *sec;
bfd_byte *contents;
{
bfd_byte *to, *from, *end;
int i;
if (strcmp (sec->name, ".pdr") != 0)
return false;
if (elf_section_data (sec)->tdata == NULL)
return false;
to = contents;
end = contents + sec->_raw_size;
for (from = contents, i = 0;
from < end;
from += PDR_SIZE, i++)
{
if (((unsigned char *) elf_section_data (sec)->tdata)[i] == 1)
continue;
if (to != from)
memcpy (to, from, PDR_SIZE);
to += PDR_SIZE;
}
bfd_set_section_contents (output_bfd, sec->output_section, contents,
(file_ptr) sec->output_offset,
sec->_cooked_size);
return true;
}
struct mips_elf_find_line
{
struct ecoff_debug_info d;
struct ecoff_find_line i;
};
boolean
_bfd_mips_elf_find_nearest_line (abfd, section, symbols, offset, filename_ptr,
functionname_ptr, line_ptr)
bfd *abfd;
asection *section;
asymbol **symbols;
bfd_vma offset;
const char **filename_ptr;
const char **functionname_ptr;
unsigned int *line_ptr;
{
asection *msec;
if (_bfd_dwarf1_find_nearest_line (abfd, section, symbols, offset,
filename_ptr, functionname_ptr,
line_ptr))
return true;
if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset,
filename_ptr, functionname_ptr,
line_ptr,
(unsigned) (ABI_64_P (abfd) ? 8 : 0),
&elf_tdata (abfd)->dwarf2_find_line_info))
return true;
msec = bfd_get_section_by_name (abfd, ".mdebug");
if (msec != NULL)
{
flagword origflags;
struct mips_elf_find_line *fi;
const struct ecoff_debug_swap * const swap =
get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
origflags = msec->flags;
if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
msec->flags |= SEC_HAS_CONTENTS;
fi = elf_tdata (abfd)->find_line_info;
if (fi == NULL)
{
bfd_size_type external_fdr_size;
char *fraw_src;
char *fraw_end;
struct fdr *fdr_ptr;
bfd_size_type amt = sizeof (struct mips_elf_find_line);
fi = (struct mips_elf_find_line *) bfd_zalloc (abfd, amt);
if (fi == NULL)
{
msec->flags = origflags;
return false;
}
if (! _bfd_mips_elf_read_ecoff_info (abfd, msec, &fi->d))
{
msec->flags = origflags;
return false;
}
amt = fi->d.symbolic_header.ifdMax * sizeof (struct fdr);
fi->d.fdr = (struct fdr *) bfd_alloc (abfd, amt);
if (fi->d.fdr == NULL)
{
msec->flags = origflags;
return false;
}
external_fdr_size = swap->external_fdr_size;
fdr_ptr = fi->d.fdr;
fraw_src = (char *) fi->d.external_fdr;
fraw_end = (fraw_src
+ fi->d.symbolic_header.ifdMax * external_fdr_size);
for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
(*swap->swap_fdr_in) (abfd, (PTR) fraw_src, fdr_ptr);
elf_tdata (abfd)->find_line_info = fi;
}
if (_bfd_ecoff_locate_line (abfd, section, offset, &fi->d, swap,
&fi->i, filename_ptr, functionname_ptr,
line_ptr))
{
msec->flags = origflags;
return true;
}
msec->flags = origflags;
}
return _bfd_elf_find_nearest_line (abfd, section, symbols, offset,
filename_ptr, functionname_ptr,
line_ptr);
}
boolean
_bfd_mips_elf_set_section_contents (abfd, section, location, offset, count)
bfd *abfd;
sec_ptr section;
PTR location;
file_ptr offset;
bfd_size_type count;
{
if (strcmp (section->name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
{
bfd_byte *c;
if (elf_section_data (section) == NULL)
{
bfd_size_type amt = sizeof (struct bfd_elf_section_data);
section->used_by_bfd = (PTR) bfd_zalloc (abfd, amt);
if (elf_section_data (section) == NULL)
return false;
}
c = (bfd_byte *) elf_section_data (section)->tdata;
if (c == NULL)
{
bfd_size_type size;
if (section->_cooked_size != 0)
size = section->_cooked_size;
else
size = section->_raw_size;
c = (bfd_byte *) bfd_zalloc (abfd, size);
if (c == NULL)
return false;
elf_section_data (section)->tdata = (PTR) c;
}
memcpy (c + offset, location, (size_t) count);
}
return _bfd_elf_set_section_contents (abfd, section, location, offset,
count);
}
bfd_byte *
_bfd_elf_mips_get_relocated_section_contents (abfd, link_info, link_order,
data, relocateable, symbols)
bfd *abfd;
struct bfd_link_info *link_info;
struct bfd_link_order *link_order;
bfd_byte *data;
boolean relocateable;
asymbol **symbols;
{
bfd *input_bfd = link_order->u.indirect.section->owner;
asection *input_section = link_order->u.indirect.section;
long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
arelent **reloc_vector = NULL;
long reloc_count;
if (reloc_size < 0)
goto error_return;
reloc_vector = (arelent **) bfd_malloc ((bfd_size_type) reloc_size);
if (reloc_vector == NULL && reloc_size != 0)
goto error_return;
if (!bfd_get_section_contents (input_bfd,
input_section,
(PTR) data,
(file_ptr) 0,
input_section->_raw_size))
goto error_return;
input_section->_cooked_size = input_section->_raw_size;
input_section->reloc_done = true;
reloc_count = bfd_canonicalize_reloc (input_bfd,
input_section,
reloc_vector,
symbols);
if (reloc_count < 0)
goto error_return;
if (reloc_count > 0)
{
arelent **parent;
int gp_found;
bfd_vma gp = 0x12345678;
{
struct bfd_hash_entry *h;
struct bfd_link_hash_entry *lh;
if (abfd && input_bfd
&& abfd->xvec == input_bfd->xvec)
lh = 0;
else
{
h = bfd_hash_lookup (&link_info->hash->table, "_gp", false, false);
lh = (struct bfd_link_hash_entry *) h;
}
lookup:
if (lh)
{
switch (lh->type)
{
case bfd_link_hash_undefined:
case bfd_link_hash_undefweak:
case bfd_link_hash_common:
gp_found = 0;
break;
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
gp_found = 1;
gp = lh->u.def.value;
break;
case bfd_link_hash_indirect:
case bfd_link_hash_warning:
lh = lh->u.i.link;
goto lookup;
case bfd_link_hash_new:
default:
abort ();
}
}
else
gp_found = 0;
}
for (parent = reloc_vector; *parent != (arelent *) NULL;
parent++)
{
char *error_message = (char *) NULL;
bfd_reloc_status_type r;
asymbol *sym = *(*parent)->sym_ptr_ptr;
if (bfd_is_abs_section (sym->section) && abfd)
{
}
else if (!gp_found)
{
}
else if ((*parent)->howto->special_function
== _bfd_mips_elf32_gprel16_reloc)
{
r = _bfd_mips_elf_gprel16_with_gp (input_bfd, sym, *parent,
input_section, relocateable,
(PTR) data, gp);
goto skip_bfd_perform_relocation;
}
r = bfd_perform_relocation (input_bfd,
*parent,
(PTR) data,
input_section,
relocateable ? abfd : (bfd *) NULL,
&error_message);
skip_bfd_perform_relocation:
if (relocateable)
{
asection *os = input_section->output_section;
os->orelocation[os->reloc_count] = *parent;
os->reloc_count++;
}
if (r != bfd_reloc_ok)
{
switch (r)
{
case bfd_reloc_undefined:
if (!((*link_info->callbacks->undefined_symbol)
(link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
input_bfd, input_section, (*parent)->address,
true)))
goto error_return;
break;
case bfd_reloc_dangerous:
BFD_ASSERT (error_message != (char *) NULL);
if (!((*link_info->callbacks->reloc_dangerous)
(link_info, error_message, input_bfd, input_section,
(*parent)->address)))
goto error_return;
break;
case bfd_reloc_overflow:
if (!((*link_info->callbacks->reloc_overflow)
(link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
(*parent)->howto->name, (*parent)->addend,
input_bfd, input_section, (*parent)->address)))
goto error_return;
break;
case bfd_reloc_outofrange:
default:
abort ();
break;
}
}
}
}
if (reloc_vector != NULL)
free (reloc_vector);
return data;
error_return:
if (reloc_vector != NULL)
free (reloc_vector);
return NULL;
}
struct bfd_link_hash_table *
_bfd_mips_elf_link_hash_table_create (abfd)
bfd *abfd;
{
struct mips_elf_link_hash_table *ret;
bfd_size_type amt = sizeof (struct mips_elf_link_hash_table);
ret = (struct mips_elf_link_hash_table *) bfd_malloc (amt);
if (ret == (struct mips_elf_link_hash_table *) NULL)
return NULL;
if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
mips_elf_link_hash_newfunc))
{
free (ret);
return NULL;
}
#if 0
for (i = 0; i < SIZEOF_MIPS_DYNSYM_SECNAMES; i++)
ret->dynsym_sec_strindex[i] = (bfd_size_type) -1;
#endif
ret->procedure_count = 0;
ret->compact_rel_size = 0;
ret->use_rld_obj_head = false;
ret->rld_value = 0;
ret->mips16_stubs_seen = false;
return &ret->root.root;
}
boolean
_bfd_mips_elf_final_link (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
asection **secpp;
asection *o;
struct bfd_link_order *p;
asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec;
asection *rtproc_sec;
Elf32_RegInfo reginfo;
struct ecoff_debug_info debug;
const struct ecoff_debug_swap *swap
= get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
HDRR *symhdr = &debug.symbolic_header;
PTR mdebug_handle = NULL;
asection *s;
EXTR esym;
unsigned int i;
bfd_size_type amt;
static const char * const secname[] =
{
".text", ".init", ".fini", ".data",
".rodata", ".sdata", ".sbss", ".bss"
};
static const int sc[] =
{
scText, scInit, scFini, scData,
scRData, scSData, scSBss, scBss
};
if (!info->shared
&& !info->relocateable
&& elf_elfheader (abfd)->e_flags & EF_MIPS_PIC)
{
elf_elfheader (abfd)->e_flags &= ~EF_MIPS_PIC;
elf_elfheader (abfd)->e_flags |= EF_MIPS_CPIC;
}
if (elf_hash_table (info)->dynamic_sections_created)
{
bfd *dynobj;
asection *got;
struct mips_got_info *g;
if (! mips_elf_sort_hash_table (info, (info->shared
? bfd_count_sections (abfd) + 1
: 1)))
return false;
dynobj = elf_hash_table (info)->dynobj;
got = bfd_get_section_by_name (dynobj, ".got");
g = (struct mips_got_info *) elf_section_data (got)->tdata;
if (g->global_gotsym != NULL)
BFD_ASSERT ((elf_hash_table (info)->dynsymcount
- g->global_gotsym->dynindx)
<= g->global_gotno);
}
#if 0
if (IRIX_COMPAT (abfd) == ict_irix5 || IRIX_COMPAT (abfd) == ict_none)
for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
{
if (strcmp ((*secpp)->name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
{
for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
if (p->type == bfd_indirect_link_order)
p->u.indirect.section->flags &= ~SEC_HAS_CONTENTS;
(*secpp)->link_order_head = NULL;
bfd_section_list_remove (abfd, secpp);
--abfd->section_count;
break;
}
}
for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
{
if (strcmp ((*secpp)->name, ".MIPS.options") == 0)
{
for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
if (p->type == bfd_indirect_link_order)
p->u.indirect.section->flags &=~ SEC_HAS_CONTENTS;
(*secpp)->link_order_head = NULL;
bfd_section_list_remove (abfd, secpp);
--abfd->section_count;
break;
}
}
#endif
if (elf_gp (abfd) == 0)
{
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
if (h != (struct bfd_link_hash_entry *) NULL
&& h->type == bfd_link_hash_defined)
elf_gp (abfd) = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
else if (info->relocateable)
{
bfd_vma lo = MINUS_ONE;
for (o = abfd->sections; o != (asection *) NULL; o = o->next)
if (o->vma < lo
&& (elf_section_data (o)->this_hdr.sh_flags & SHF_MIPS_GPREL))
lo = o->vma;
elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (abfd);
}
else
{
}
}
reginfo_sec = NULL;
mdebug_sec = NULL;
gptab_data_sec = NULL;
gptab_bss_sec = NULL;
for (o = abfd->sections; o != (asection *) NULL; o = o->next)
{
if (strcmp (o->name, ".reginfo") == 0)
{
memset (®info, 0, sizeof reginfo);
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
bfd *input_bfd;
Elf32_External_RegInfo ext;
Elf32_RegInfo sub;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_data_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_bfd = input_section->owner;
if (input_section->_raw_size == 0)
input_section->_raw_size = sizeof (Elf32_External_RegInfo);
if (! bfd_get_section_contents (input_bfd, input_section,
(PTR) &ext,
(file_ptr) 0,
(bfd_size_type) sizeof ext))
return false;
bfd_mips_elf32_swap_reginfo_in (input_bfd, &ext, &sub);
reginfo.ri_gprmask |= sub.ri_gprmask;
reginfo.ri_cprmask[0] |= sub.ri_cprmask[0];
reginfo.ri_cprmask[1] |= sub.ri_cprmask[1];
reginfo.ri_cprmask[2] |= sub.ri_cprmask[2];
reginfo.ri_cprmask[3] |= sub.ri_cprmask[3];
input_section->flags &= ~SEC_HAS_CONTENTS;
}
BFD_ASSERT(o->_raw_size == sizeof (Elf32_External_RegInfo));
o->link_order_head = (struct bfd_link_order *) NULL;
reginfo_sec = o;
}
if (strcmp (o->name, ".mdebug") == 0)
{
struct extsym_info einfo;
bfd_vma last;
symhdr->magic = swap->sym_magic;
symhdr->vstamp = 0;
symhdr->ilineMax = 0;
symhdr->cbLine = 0;
symhdr->idnMax = 0;
symhdr->ipdMax = 0;
symhdr->isymMax = 0;
symhdr->ioptMax = 0;
symhdr->iauxMax = 0;
symhdr->issMax = 0;
symhdr->issExtMax = 0;
symhdr->ifdMax = 0;
symhdr->crfd = 0;
symhdr->iextMax = 0;
debug.line = NULL;
debug.external_dnr = NULL;
debug.external_pdr = NULL;
debug.external_sym = NULL;
debug.external_opt = NULL;
debug.external_aux = NULL;
debug.ss = NULL;
debug.ssext = debug.ssext_end = NULL;
debug.external_fdr = NULL;
debug.external_rfd = NULL;
debug.external_ext = debug.external_ext_end = NULL;
mdebug_handle = bfd_ecoff_debug_init (abfd, &debug, swap, info);
if (mdebug_handle == (PTR) NULL)
return false;
esym.jmptbl = 0;
esym.cobol_main = 0;
esym.weakext = 0;
esym.reserved = 0;
esym.ifd = ifdNil;
esym.asym.iss = issNil;
esym.asym.st = stLocal;
esym.asym.reserved = 0;
esym.asym.index = indexNil;
last = 0;
for (i = 0; i < sizeof (secname) / sizeof (secname[0]); i++)
{
esym.asym.sc = sc[i];
s = bfd_get_section_by_name (abfd, secname[i]);
if (s != NULL)
{
esym.asym.value = s->vma;
last = s->vma + s->_raw_size;
}
else
esym.asym.value = last;
if (!bfd_ecoff_debug_one_external (abfd, &debug, swap,
secname[i], &esym))
return false;
}
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
bfd *input_bfd;
const struct ecoff_debug_swap *input_swap;
struct ecoff_debug_info input_debug;
char *eraw_src;
char *eraw_end;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_data_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_bfd = input_section->owner;
if (bfd_get_flavour (input_bfd) != bfd_target_elf_flavour
|| (get_elf_backend_data (input_bfd)
->elf_backend_ecoff_debug_swap) == NULL)
{
continue;
}
input_swap = (get_elf_backend_data (input_bfd)
->elf_backend_ecoff_debug_swap);
BFD_ASSERT (p->size == input_section->_raw_size);
if (! _bfd_mips_elf_read_ecoff_info (input_bfd, input_section,
&input_debug))
return false;
if (! (bfd_ecoff_debug_accumulate
(mdebug_handle, abfd, &debug, swap, input_bfd,
&input_debug, input_swap, info)))
return false;
eraw_src = input_debug.external_ext;
eraw_end = (eraw_src
+ (input_debug.symbolic_header.iextMax
* input_swap->external_ext_size));
for (;
eraw_src < eraw_end;
eraw_src += input_swap->external_ext_size)
{
EXTR ext;
const char *name;
struct mips_elf_link_hash_entry *h;
(*input_swap->swap_ext_in) (input_bfd, (PTR) eraw_src, &ext);
if (ext.asym.sc == scNil
|| ext.asym.sc == scUndefined
|| ext.asym.sc == scSUndefined)
continue;
name = input_debug.ssext + ext.asym.iss;
h = mips_elf_link_hash_lookup (mips_elf_hash_table (info),
name, false, false, true);
if (h == NULL || h->esym.ifd != -2)
continue;
if (ext.ifd != -1)
{
BFD_ASSERT (ext.ifd
< input_debug.symbolic_header.ifdMax);
ext.ifd = input_debug.ifdmap[ext.ifd];
}
h->esym = ext;
}
free (input_debug.line);
free (input_debug.external_dnr);
free (input_debug.external_pdr);
free (input_debug.external_sym);
free (input_debug.external_opt);
free (input_debug.external_aux);
free (input_debug.ss);
free (input_debug.ssext);
free (input_debug.external_fdr);
free (input_debug.external_rfd);
free (input_debug.external_ext);
input_section->flags &= ~SEC_HAS_CONTENTS;
}
if (SGI_COMPAT (abfd) && info->shared)
{
rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc");
if (rtproc_sec == NULL)
{
flagword flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED | SEC_READONLY);
rtproc_sec = bfd_make_section (abfd, ".rtproc");
if (rtproc_sec == NULL
|| ! bfd_set_section_flags (abfd, rtproc_sec, flags)
|| ! bfd_set_section_alignment (abfd, rtproc_sec, 4))
return false;
}
if (! mips_elf_create_procedure_table (mdebug_handle, abfd,
info, rtproc_sec,
&debug))
return false;
}
einfo.abfd = abfd;
einfo.info = info;
einfo.debug = &debug;
einfo.swap = swap;
einfo.failed = false;
mips_elf_link_hash_traverse (mips_elf_hash_table (info),
mips_elf_output_extsym,
(PTR) &einfo);
if (einfo.failed)
return false;
o->_raw_size = bfd_ecoff_debug_size (abfd, &debug, swap);
o->link_order_head = (struct bfd_link_order *) NULL;
mdebug_sec = o;
}
if (strncmp (o->name, ".gptab.", sizeof ".gptab." - 1) == 0)
{
const char *subname;
unsigned int c;
Elf32_gptab *tab;
Elf32_External_gptab *ext_tab;
unsigned int j;
if (! info->relocateable)
{
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_data_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_section->flags &= ~SEC_HAS_CONTENTS;
}
o->link_order_head = (struct bfd_link_order *) NULL;
for (secpp = &abfd->sections;
*secpp != o;
secpp = &(*secpp)->next)
;
bfd_section_list_remove (abfd, secpp);
--abfd->section_count;
continue;
}
if (strcmp (o->name, ".gptab.sdata") == 0)
gptab_data_sec = o;
else if (strcmp (o->name, ".gptab.sbss") == 0)
gptab_bss_sec = o;
else
{
(*_bfd_error_handler)
(_("%s: illegal section name `%s'"),
bfd_get_filename (abfd), o->name);
bfd_set_error (bfd_error_nonrepresentable_section);
return false;
}
subname = o->name + sizeof ".gptab" - 1;
if (bfd_get_section_by_name (abfd, subname) == NULL)
{
if (o == gptab_data_sec)
o->name = ".gptab.data";
else
o->name = ".gptab.bss";
subname = o->name + sizeof ".gptab" - 1;
BFD_ASSERT (bfd_get_section_by_name (abfd, subname) != NULL);
}
c = 1;
amt = c * sizeof (Elf32_gptab);
tab = (Elf32_gptab *) bfd_malloc (amt);
if (tab == NULL)
return false;
tab[0].gt_header.gt_current_g_value = elf_gp_size (abfd);
tab[0].gt_header.gt_unused = 0;
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
bfd *input_bfd;
bfd_size_type size;
unsigned long last;
bfd_size_type gpentry;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_data_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_bfd = input_section->owner;
size = bfd_section_size (input_bfd, input_section);
last = 0;
for (gpentry = sizeof (Elf32_External_gptab);
gpentry < size;
gpentry += sizeof (Elf32_External_gptab))
{
Elf32_External_gptab ext_gptab;
Elf32_gptab int_gptab;
unsigned long val;
unsigned long add;
boolean exact;
unsigned int look;
if (! (bfd_get_section_contents
(input_bfd, input_section, (PTR) &ext_gptab,
(file_ptr) gpentry,
(bfd_size_type) sizeof (Elf32_External_gptab))))
{
free (tab);
return false;
}
bfd_mips_elf32_swap_gptab_in (input_bfd, &ext_gptab,
&int_gptab);
val = int_gptab.gt_entry.gt_g_value;
add = int_gptab.gt_entry.gt_bytes - last;
exact = false;
for (look = 1; look < c; look++)
{
if (tab[look].gt_entry.gt_g_value >= val)
tab[look].gt_entry.gt_bytes += add;
if (tab[look].gt_entry.gt_g_value == val)
exact = true;
}
if (! exact)
{
Elf32_gptab *new_tab;
unsigned int max;
amt = (bfd_size_type) (c + 1) * sizeof (Elf32_gptab);
new_tab = (Elf32_gptab *) bfd_realloc ((PTR) tab, amt);
if (new_tab == NULL)
{
free (tab);
return false;
}
tab = new_tab;
tab[c].gt_entry.gt_g_value = val;
tab[c].gt_entry.gt_bytes = add;
max = 0;
for (look = 1; look < c; look++)
{
if (tab[look].gt_entry.gt_g_value < val
&& (max == 0
|| (tab[look].gt_entry.gt_g_value
> tab[max].gt_entry.gt_g_value)))
max = look;
}
if (max != 0)
tab[c].gt_entry.gt_bytes +=
tab[max].gt_entry.gt_bytes;
++c;
}
last = int_gptab.gt_entry.gt_bytes;
}
input_section->flags &= ~SEC_HAS_CONTENTS;
}
if (c > 2)
qsort (tab + 1, c - 1, sizeof (tab[0]), gptab_compare);
amt = (bfd_size_type) c * sizeof (Elf32_External_gptab);
ext_tab = (Elf32_External_gptab *) bfd_alloc (abfd, amt);
if (ext_tab == NULL)
{
free (tab);
return false;
}
for (j = 0; j < c; j++)
bfd_mips_elf32_swap_gptab_out (abfd, tab + j, ext_tab + j);
free (tab);
o->_raw_size = c * sizeof (Elf32_External_gptab);
o->contents = (bfd_byte *) ext_tab;
o->link_order_head = (struct bfd_link_order *) NULL;
}
}
if (ABI_64_P (abfd))
{
#ifdef BFD64
if (!bfd_elf64_bfd_final_link (abfd, info))
return false;
#else
abort ();
return false;
#endif
}
else if (!bfd_elf32_bfd_final_link (abfd, info))
return false;
if (reginfo_sec != (asection *) NULL)
{
Elf32_External_RegInfo ext;
bfd_mips_elf32_swap_reginfo_out (abfd, ®info, &ext);
if (! bfd_set_section_contents (abfd, reginfo_sec, (PTR) &ext,
(file_ptr) 0,
(bfd_size_type) sizeof ext))
return false;
}
if (mdebug_sec != (asection *) NULL)
{
BFD_ASSERT (abfd->output_has_begun);
if (! bfd_ecoff_write_accumulated_debug (mdebug_handle, abfd, &debug,
swap, info,
mdebug_sec->filepos))
return false;
bfd_ecoff_debug_free (mdebug_handle, abfd, &debug, swap, info);
}
if (gptab_data_sec != (asection *) NULL)
{
if (! bfd_set_section_contents (abfd, gptab_data_sec,
gptab_data_sec->contents,
(file_ptr) 0,
gptab_data_sec->_raw_size))
return false;
}
if (gptab_bss_sec != (asection *) NULL)
{
if (! bfd_set_section_contents (abfd, gptab_bss_sec,
gptab_bss_sec->contents,
(file_ptr) 0,
gptab_bss_sec->_raw_size))
return false;
}
if (SGI_COMPAT (abfd))
{
rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc");
if (rtproc_sec != NULL)
{
if (! bfd_set_section_contents (abfd, rtproc_sec,
rtproc_sec->contents,
(file_ptr) 0,
rtproc_sec->_raw_size))
return false;
}
}
return true;
}
static boolean
_bfd_mips_elf_mach_extends_p (base, extension)
flagword base, extension;
{
return (base == 0
|| (base == E_MIPS_MACH_5400 && extension == E_MIPS_MACH_5500)
|| (base == E_MIPS_MACH_4100 && extension == E_MIPS_MACH_4111)
|| (base == E_MIPS_MACH_4100 && extension == E_MIPS_MACH_4120));
}
boolean
_bfd_mips_elf_merge_private_bfd_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
flagword old_flags;
flagword new_flags;
boolean ok;
boolean null_input_bfd = true;
asection *sec;
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
return false;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
new_flags = elf_elfheader (ibfd)->e_flags;
elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
old_flags = elf_elfheader (obfd)->e_flags;
if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = true;
elf_elfheader (obfd)->e_flags = new_flags;
elf_elfheader (obfd)->e_ident[EI_CLASS]
= elf_elfheader (ibfd)->e_ident[EI_CLASS];
if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
&& bfd_get_arch_info (obfd)->the_default)
{
if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
bfd_get_mach (ibfd)))
return false;
}
return true;
}
new_flags &= ~EF_MIPS_NOREORDER;
old_flags &= ~EF_MIPS_NOREORDER;
if (new_flags == old_flags)
return true;
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
{
if (strcmp (sec->name, ".reginfo")
&& strcmp (sec->name, ".mdebug")
&& ((!strcmp (sec->name, ".text")
|| !strcmp (sec->name, ".data")
|| !strcmp (sec->name, ".bss"))
&& sec->_raw_size != 0))
{
null_input_bfd = false;
break;
}
}
if (null_input_bfd)
return true;
ok = true;
if ((new_flags & EF_MIPS_PIC) != (old_flags & EF_MIPS_PIC))
{
new_flags &= ~EF_MIPS_PIC;
old_flags &= ~EF_MIPS_PIC;
(*_bfd_error_handler)
(_("%s: linking PIC files with non-PIC files"),
bfd_archive_filename (ibfd));
ok = false;
}
if ((new_flags & EF_MIPS_CPIC) != (old_flags & EF_MIPS_CPIC))
{
new_flags &= ~EF_MIPS_CPIC;
old_flags &= ~EF_MIPS_CPIC;
(*_bfd_error_handler)
(_("%s: linking abicalls files with non-abicalls files"),
bfd_archive_filename (ibfd));
ok = false;
}
if ((new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH))
!= (old_flags & (EF_MIPS_ARCH | EF_MIPS_MACH)))
{
int new_mach = new_flags & EF_MIPS_MACH;
int old_mach = old_flags & EF_MIPS_MACH;
int new_isa = elf_mips_isa (new_flags);
int old_isa = elf_mips_isa (old_flags);
if (new_mach == old_mach
|| _bfd_mips_elf_mach_extends_p (new_mach, old_mach)
|| _bfd_mips_elf_mach_extends_p (old_mach, new_mach))
{
if (( (new_isa == 1 || new_isa == 2 || new_isa == 32)
^ (old_isa == 1 || old_isa == 2 || old_isa == 32)) != 0)
{
(*_bfd_error_handler)
(_("%s: ISA mismatch (-mips%d) with previous modules (-mips%d)"),
bfd_archive_filename (ibfd), new_isa, old_isa);
ok = false;
}
else
{
if (_bfd_mips_elf_mach_extends_p (old_mach, new_mach))
{
elf_elfheader (obfd)->e_flags &= ~EF_MIPS_MACH;
elf_elfheader (obfd)->e_flags |= new_mach;
}
if (new_isa > old_isa)
{
elf_elfheader (obfd)->e_flags &= ~EF_MIPS_ARCH;
elf_elfheader (obfd)->e_flags
|= new_flags & EF_MIPS_ARCH;
}
}
}
else
{
(*_bfd_error_handler)
(_("%s: ISA mismatch (%d) with previous modules (%d)"),
bfd_archive_filename (ibfd),
_bfd_elf_mips_mach (new_flags),
_bfd_elf_mips_mach (old_flags));
ok = false;
}
new_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
old_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
}
if ((new_flags & EF_MIPS_ABI) != (old_flags & EF_MIPS_ABI)
|| (elf_elfheader (ibfd)->e_ident[EI_CLASS]
!= elf_elfheader (obfd)->e_ident[EI_CLASS]))
{
if (((new_flags & EF_MIPS_ABI) && (old_flags & EF_MIPS_ABI))
|| (elf_elfheader (ibfd)->e_ident[EI_CLASS]
!= elf_elfheader (obfd)->e_ident[EI_CLASS]))
{
(*_bfd_error_handler)
(_("%s: ABI mismatch: linking %s module with previous %s modules"),
bfd_archive_filename (ibfd),
elf_mips_abi_name (ibfd),
elf_mips_abi_name (obfd));
ok = false;
}
new_flags &= ~EF_MIPS_ABI;
old_flags &= ~EF_MIPS_ABI;
}
if ((new_flags & EF_MIPS_ARCH_ASE) != (old_flags & EF_MIPS_ARCH_ASE))
{
elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_ARCH_ASE;
new_flags &= ~ EF_MIPS_ARCH_ASE;
old_flags &= ~ EF_MIPS_ARCH_ASE;
}
if (new_flags != old_flags)
{
(*_bfd_error_handler)
(_("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
bfd_archive_filename (ibfd), (unsigned long) new_flags,
(unsigned long) old_flags);
ok = false;
}
if (! ok)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
return true;
}
boolean
_bfd_mips_elf_set_private_flags (abfd, flags)
bfd *abfd;
flagword flags;
{
BFD_ASSERT (!elf_flags_init (abfd)
|| elf_elfheader (abfd)->e_flags == flags);
elf_elfheader (abfd)->e_flags = flags;
elf_flags_init (abfd) = true;
return true;
}
boolean
_bfd_mips_elf_print_private_bfd_data (abfd, ptr)
bfd *abfd;
PTR ptr;
{
FILE *file = (FILE *) ptr;
BFD_ASSERT (abfd != NULL && ptr != NULL);
_bfd_elf_print_private_bfd_data (abfd, ptr);
fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O32)
fprintf (file, _(" [abi=O32]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O64)
fprintf (file, _(" [abi=O64]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32)
fprintf (file, _(" [abi=EABI32]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI64)
fprintf (file, _(" [abi=EABI64]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI))
fprintf (file, _(" [abi unknown]"));
else if (ABI_N32_P (abfd))
fprintf (file, _(" [abi=N32]"));
else if (ABI_64_P (abfd))
fprintf (file, _(" [abi=64]"));
else
fprintf (file, _(" [no abi set]"));
if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1)
fprintf (file, _(" [mips1]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2)
fprintf (file, _(" [mips2]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_3)
fprintf (file, _(" [mips3]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_4)
fprintf (file, _(" [mips4]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_5)
fprintf (file, _(" [mips5]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32)
fprintf (file, _(" [mips32]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64)
fprintf (file, _(" [mips64]"));
else
fprintf (file, _(" [unknown ISA]"));
if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX)
fprintf (file, _(" [mdmx]"));
if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16)
fprintf (file, _(" [mips16]"));
if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
fprintf (file, _(" [32bitmode]"));
else
fprintf (file, _(" [not 32bitmode]"));
fputc ('\n', file);
return true;
}