diff options
author | Richard Sandiford <rdsandiford@googlemail.com> | 2006-03-22 09:28:15 +0000 |
---|---|---|
committer | Richard Sandiford <rdsandiford@googlemail.com> | 2006-03-22 09:28:15 +0000 |
commit | 0a44bf6950b3b1a1a79e46c36be2487c2499fd58 (patch) | |
tree | fd77387be93bd59c5df8d12c0fca9da2b0c7a124 /bfd/elfxx-mips.c | |
parent | 11010f5a82bae6b40067c04c33184c70c0c99e10 (diff) | |
download | gdb-0a44bf6950b3b1a1a79e46c36be2487c2499fd58.zip gdb-0a44bf6950b3b1a1a79e46c36be2487c2499fd58.tar.gz gdb-0a44bf6950b3b1a1a79e46c36be2487c2499fd58.tar.bz2 |
Richard Sandiford <richard@codesourcery.com>
Daniel Jacobowitz <dan@codesourcery.com>
Phil Edwards <phil@codesourcery.com>
Zack Weinberg <zack@codesourcery.com>
Mark Mitchell <mark@codesourcery.com>
Nathan Sidwell <nathan@codesourcery.com>
bfd/
* bfd-in2.h: Regenerate.
* config.bfd (mips*-*-vxworks*, mips*el-*-vxworks*): New stanzas.
* configure.in (bfd_elf32_bigmips_vxworks_vec): New stanza.
(bfd_elf32_littlemips_vxworks_vec): Likewise.
(bfd_elf32_bigmips_vec): Add elf-vxworks.lo.
(bfd_elf32_littlemips_vec): Likewise.
(bfd_elf32_nbigmips_vec): Likewise.
(bfd_elf32_nlittlemips_vec): Likewise.
(bfd_elf32_ntradbigmips_vec): Likewise.
(bfd_elf32_ntradlittlemips_vec): Likewise.
(bfd_elf32_tradbigmips_vec): Likewise.
(bfd_elf32_tradlittlemips_vec): Likewise.
(bfd_elf64_bigmips_vec): Likewise.
(bfd_elf64_littlemips_vec): Likewise.
(bfd_elf64_tradbigmips_vec): Likewise.
(bfd_elf64_tradlittlemips_vec): Likewise.
* elf32-mips.c: Include elf-vxworks.h.
(mips_info_to_howto_rel): Use elf_backend_mips_rtype_to_howto
instead of calling mips_elf32_rtype_to_howto directly.
(mips_vxworks_copy_howto_rela): New reloc howto.
(mips_vxworks_jump_slot_howto_rela): Likewise.
(mips_vxworks_bfd_reloc_type_lookup): New function.
(mips_vxworks_rtype_to_howto): Likewise.
(mips_vxworks_final_write_processing): Likewise.
(TARGET_LITTLE_SYM, TARGET_LITTLE_NAME): Override for VxWorks.
(TARGET_BIG_SYM, TARGET_BIG_NAME, elf_bed, ELF_MAXPAGESIZE): Likewise.
(elf_backend_want_got_plt): Likewise.
(elf_backend_want_plt_sym): Likewise.
(elf_backend_got_symbol_offset): Likewise.
(elf_backend_want_dynbss): Likewise.
(elf_backend_may_use_rel_p): Likewise.
(elf_backend_may_use_rela_p): Likewise.
(elf_backend_default_use_rela_p): Likewise.
(elf_backend_got_header_size: Likewise.
(elf_backend_plt_readonly): Likewise.
(bfd_elf32_bfd_reloc_type_lookup): Likewise.
(elf_backend_mips_rtype_to_howto): Likewise.
(elf_backend_adjust_dynamic_symbol): Likewise.
(elf_backend_finish_dynamic_symbol): Likewise.
(bfd_elf32_bfd_link_hash_table_create): Likewise.
(elf_backend_add_symbol_hook): Likewise.
(elf_backend_link_output_symbol_hook): Likewise.
(elf_backend_emit_relocs): Likewise.
(elf_backend_final_write_processing: Likewise.
(elf_backend_additional_program_headers): Likewise.
(elf_backend_modify_segment_map): Likewise.
(elf_backend_symbol_processing): Likewise.
* elfxx-mips.c: Include elf-vxworks.h.
(mips_elf_link_hash_entry): Add is_relocation_target and
is_branch_target fields.
(mips_elf_link_hash_table): Add is_vxworks, srelbss, sdynbss, srelplt,
srelplt2, sgotplt, splt, plt_header_size and plt_entry_size fields.
(MIPS_ELF_RELA_SIZE, MIPS_ELF_REL_DYN_NAME): New macros.
(MIPS_RESERVED_GOTNO): Take a mips_elf_link_hash_table argument.
Return 3 for VxWorks.
(ELF_MIPS_GP_OFFSET): Change the argument from a bfd to a
mips_elf_link_hash_table. Return 0 for VxWorks.
(MIPS_ELF_GOT_MAX_SIZE): Change the argument from a bfd to a
mips_elf_link_hash_table. Update the call to ELF_MIPS_GP_OFFSET.
(mips_vxworks_exec_plt0_entry): New variable.
(mips_vxworks_exec_plt_entry): Likewise.
(mips_vxworks_shared_plt0_entry): Likewise.
(mips_vxworks_shared_plt_entry): Likewise.
(mips_elf_link_hash_newfunc): Initialize the new hash_entry fields.
(mips_elf_rel_dyn_section): Change the bfd argument to a
mips_elf_link_hash_table. Use MIPS_ELF_REL_DYN_NAME to get
the name of the section.
(mips_elf_initialize_tls_slots): Update the call to
mips_elf_rel_dyn_section.
(mips_elf_gotplt_index): New function.
(mips_elf_local_got_index): Add an input_section argument.
Update the call to mips_elf_create_local_got_entry.
(mips_elf_got_page): Likewise.
(mips_elf_got16_entry): Likewise.
(mips_elf_create_local_got_entry): Add bfd_link_info and input_section
arguments. Create dynamic relocations for each entry on VxWorks.
(mips_elf_merge_gots): Update the use of MIPS_ELF_GOT_MAX_SIZE.
(mips_elf_multi_got): Update the uses of MIPS_ELF_GOT_MAX_SIZE
and MIPS_RESERVED_GOTNO.
(mips_elf_create_got_section): Update the uses of
MIPS_ELF_GOT_MAX_SIZE. Create .got.plt on VxWorks.
(is_gott_symbol): New function.
(mips_elf_calculate_relocation): Use a dynobj local variable.
Update the calls to mips_elf_local_got_index, mips_elf_got16_entry and
mips_elf_got_page_entry. Set G to the .got.plt entry when calculating
VxWorks R_MIPS_CALL* relocations. Calculate and use G for all GOT
relocations on VxWorks. Add dynamic relocations for references
to the VxWorks __GOTT_BASE__ and __GOTT_INDEX__ symbols. Don't
create dynamic relocations for R_MIPS_32, R_MIPS_REL32 or R_MIPS_64
in VxWorks executables.
(mips_elf_allocate_dynamic_relocations): Add a bfd_link_info argument.
Use MIPS_ELF_RELA_SIZE to calculate the size of a VxWorks entry.
Don't allocate a null entry on VxWorks.
(mips_elf_create_dynamic_relocation): Update the call to
mips_elf_rel_dyn_section. Use absolute rather than relative
relocations for VxWorks, and make them RELA rather than REL.
(_bfd_mips_elf_create_dynamic_sections): Don't make .dynamic
read-only on VxWorks. Update the call to mips_elf_rel_dyn_section.
Create the .plt, .rela.plt, .dynbss and .rela.bss sections on
VxWorks. Likewise create the _PROCEDURE_LINKAGE_TABLE symbol.
Call elf_vxworks_create_dynamic_sections for VxWorks and
initialize the plt_header_size and plt_entry_size fields.
(_bfd_mips_elf_check_relocs): Don't allow GOT relocations to be
used in VxWorks executables. Don't allocate dynamic relocations
for R_MIPS_32, R_MIPS_REL32 or R_MIPS_64 in VxWorks executables.
Set is_relocation_target for each symbol referenced by a relocation.
Allocate .rela.dyn entries for relocations against the special
VxWorks __GOTT_BASE__ and __GOTT_INDEX__ symbols. Create GOT
entries for all VxWorks R_MIPS_GOT16 relocations. Don't allocate
a global GOT entry for symbols mentioned in VxWorks R_MIPS_CALL*,
R_MIPS_32, R_MIPS_REL32 or R_MIPS_64 relocations. Update the calls
to mips_elf_rel_dyn_section and mips_elf_allocate_dynamic_relocations.
Set is_branch_target for symbols mentioned in R_MIPS_PC16 or R_MIPS_26
relocations. Don't set no_fn_stub on VxWorks.
(_bfd_mips_elf_adjust_dynamic_symbol): Update the call to
mips_elf_allocate_dynamic_relocations.
(_bfd_mips_vxworks_adjust_dynamic_symbol): New function.
(_bfd_mips_elf_always_size_sections): Do not allocate GOT page
entries for VxWorks, and do not create multiple GOTs.
(_bfd_mips_elf_size_dynamic_sections): Use MIPS_ELF_REL_DYN_NAME.
Handle .got specially for VxWorks. Update the uses of
MIPS_RESERVED_GOTNO and mips_elf_allocate_dynamic_relocations.
Check for sgotplt and splt. Allocate the .rel(a).dyn contents last,
once its final size is known. Set DF_TEXTREL for VxWorks. Add
DT_RELA, DT_RELASZ, DT_RELAENT, DT_PLTREL, DT_PLTRELSZ and DT_JMPREL
tags on VxWorks. Do not add the MIPS-specific tags for VxWorks.
(_bfd_mips_vxworks_finish_dynamic_symbol): New function.
(mips_vxworks_finish_exec_plt): Likewise.
(mips_vxworks_finish_shared_plt): Likewise.
(_bfd_mips_elf_finish_dynamic_sections): Remove an unncessary call
to mips_elf_rel_dyn_section. Use a VxWorks-specific value of
DT_PLTGOT. Handle DT_RELA, DT_RELASZ, DT_RELAENT, DT_PLTREL,
DT_PLTRELSZ and DT_JMPREL. Update the uses of MIPS_RESERVED_GOTNO
and mips_elf_rel_dyn_section. Use a different GOT header for
VxWorks. Don't sort .rela.dyn on VxWorks. Finish the PLT on VxWorks.
(_bfd_mips_elf_link_hash_table_create): Initialize the new
mips_elf_link_hash_table fields.
(_bfd_mips_vxworks_link_hash_table_create): New function.
(_bfd_mips_elf_final_link): Set the GP value to _GLOBAL_OFFSET_TABLE_
on VxWorks. Update the call to ELF_MIPS_GP_OFFSET.
* elfxx-mips.h (_bfd_mips_vxworks_adjust_dynamic_symbol): Declare.
(_bfd_mips_vxworks_finish_dynamic_symbol): Likewise.
(_bfd_mips_vxworks_link_hash_table_create): Likewise.
* libbfd.h: Regenerate.
* Makefile.am (elfxx-mips.lo): Depend on elf-vxworks.h.
(elf32-mips.lo): Likewise.
* Makefile.in: Regenerate.
* reloc.c (BFD_RELOC_MIPS_COPY, BFD_RELOC_MIPS_JUMP_SLOT): Declare.
* targets.c (bfd_elf32_bigmips_vxworks_vec): Declare.
(bfd_elf32_littlemips_vxworks_vec): Likewise.
(_bfd_target_vector): Add entries for them.
gas/
* config/tc-mips.c (mips_target_format): Handle vxworks targets.
(md_begin): Complain about -G being used for PIC. Don't change
the text, data and bss alignments on VxWorks.
(reloc_needs_lo_p): Don't return true for R_MIPS_GOT16 when
generating VxWorks PIC.
(load_address): Extend SVR4_PIC handling to VXWORKS_PIC.
(macro): Likewise, but do not treat la $25 specially for
VxWorks PIC, and do not handle jal.
(OPTION_MVXWORKS_PIC): New macro.
(md_longopts): Add -mvxworks-pic.
(md_parse_option): Don't complain about using PIC and -G together here.
Handle OPTION_MVXWORKS_PIC.
(md_estimate_size_before_relax): Always use the first relaxation
sequence on VxWorks.
* config/tc-mips.h (VXWORKS_PIC): New.
gas/testsuite/
* gas/mips/vxworks1.s, gas/mips/vxworks1.d,
* gas/mips/vxworks1-xgot.d: New tests.
* gas/mips/mips.exp: Run them. Do not run other tests on VxWorks.
include/elf/
* mips.h (R_MIPS_COPY, R_MIPS_JUMP_SLOT): New relocs.
ld/
* configure.tgt (mips*el-*-vxworks*, mips*-*-vxworks*): Use
separate VxWorks emulations.
* emulparams/elf32ebmipvxworks.sh: New file.
* emulparams/elf32elmipvxworks.sh: New file.
* Makefile.am (ALL_EMULATIONS): Add eelf32ebmipvxworks.o and
eelf32elmipvxworks.o.
(eelf32ebmipvxworks.c, eelf32elmipvxworks.c): New rules.
* Makefile.in: Regenerate.
ld/testsuite/
* ld-mips/vxworks1.dd, ld-mips/vxworks1.ld, ld-mips/vxworks1-lib.dd,
* ld-mips/vxworks1-lib.nd, ld-mips/vxworks1-lib.rd,
* ld-mips/vxworks1-lib.s, ld-mips/vxworks1.rd, ld-mips/vxworks1.s,
* ld-mips/vxworks1-static.d, ld-mips/vxworks2.s, ld-mips/vxworks2.sd,
* ld-mips/vxworks2-static.sd: New tests.
* ld-mips/mips-elf.exp: Run them.
Diffstat (limited to 'bfd/elfxx-mips.c')
-rw-r--r-- | bfd/elfxx-mips.c | 1373 |
1 files changed, 1154 insertions, 219 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index 6f6b4a2..b5af3b1 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -34,6 +34,7 @@ #include "elf-bfd.h" #include "elfxx-mips.h" #include "elf/mips.h" +#include "elf-vxworks.h" /* Get the ECOFF swapping routines. */ #include "coff/sym.h" @@ -246,6 +247,12 @@ struct mips_elf_link_hash_entry the initial global GOT entry to a local GOT entry. */ bfd_boolean forced_local; + /* Are we referenced by some kind of relocation? */ + bfd_boolean is_relocation_target; + + /* Are we referenced by branch relocations? */ + bfd_boolean is_branch_target; + #define GOT_NORMAL 0 #define GOT_TLS_GD 1 #define GOT_TLS_LDM 2 @@ -283,6 +290,20 @@ struct mips_elf_link_hash_table bfd_vma rld_value; /* This is set if we see any mips16 stub sections. */ bfd_boolean mips16_stubs_seen; + /* True if we're generating code for VxWorks. */ + bfd_boolean is_vxworks; + /* Shortcuts to some dynamic sections, or NULL if they are not + being used. */ + asection *srelbss; + asection *sdynbss; + asection *srelplt; + asection *srelplt2; + asection *sgotplt; + asection *splt; + /* The size of the PLT header in bytes (VxWorks only). */ + bfd_vma plt_header_size; + /* The size of a PLT entry in bytes (VxWorks only). */ + bfd_vma plt_entry_size; }; #define TLS_RELOC_P(r_type) \ @@ -433,8 +454,8 @@ typedef struct runtime_pdr { #define rpdNil ((pRPDR) 0) static struct mips_got_entry *mips_elf_create_local_got_entry - (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma, unsigned long, - struct mips_elf_link_hash_entry *, int); + (bfd *, struct bfd_link_info *, bfd *, struct mips_got_info *, asection *, + asection *, bfd_vma, unsigned long, struct mips_elf_link_hash_entry *, int); static bfd_boolean mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *, void *); static bfd_vma mips_elf_high @@ -490,6 +511,10 @@ static bfd *reldyn_sorting_bfd; #define MIPS_ELF_REL_SIZE(abfd) \ (get_elf_backend_data (abfd)->s->sizeof_rel) +/* The size of an external RELA relocation. */ +#define MIPS_ELF_RELA_SIZE(abfd) \ + (get_elf_backend_data (abfd)->s->sizeof_rela) + /* The size of an external dynamic table entry. */ #define MIPS_ELF_DYN_SIZE(abfd) \ (get_elf_backend_data (abfd)->s->sizeof_dyn) @@ -540,20 +565,26 @@ static bfd *reldyn_sorting_bfd; == (ABI_64_P (abfd) ? sizeof (Elf64_External_Rela) \ : sizeof (Elf32_External_Rela)))) +/* The name of the dynamic relocation section. */ +#define MIPS_ELF_REL_DYN_NAME(INFO) \ + (mips_elf_hash_table (INFO)->is_vxworks ? ".rela.dyn" : ".rel.dyn") + /* In case we're on a 32-bit machine, construct a 64-bit "-1" value from smaller values. Start with zero, widen, *then* decrement. */ #define MINUS_ONE (((bfd_vma)0) - 1) #define MINUS_TWO (((bfd_vma)0) - 2) /* The number of local .got entries we reserve. */ -#define MIPS_RESERVED_GOTNO (2) +#define MIPS_RESERVED_GOTNO(INFO) \ + (mips_elf_hash_table (INFO)->is_vxworks ? 3 : 2) /* The offset of $gp from the beginning of the .got section. */ -#define ELF_MIPS_GP_OFFSET(abfd) (0x7ff0) +#define ELF_MIPS_GP_OFFSET(INFO) \ + (mips_elf_hash_table (INFO)->is_vxworks ? 0x0 : 0x7ff0) /* The maximum size of the GOT for it to be addressable using 16-bit offsets from $gp. */ -#define MIPS_ELF_GOT_MAX_SIZE(abfd) (ELF_MIPS_GP_OFFSET(abfd) + 0x7fff) +#define MIPS_ELF_GOT_MAX_SIZE(INFO) (ELF_MIPS_GP_OFFSET (INFO) + 0x7fff) /* Instructions which appear in a stub. */ #define STUB_LW(abfd) \ @@ -636,6 +667,44 @@ static bfd *reldyn_sorting_bfd; #define CALL_STUB ".mips16.call." #define CALL_FP_STUB ".mips16.call.fp." +/* The format of the first PLT entry in a VxWorks executable. */ +static const bfd_vma mips_vxworks_exec_plt0_entry[] = { + 0x3c190000, /* lui t9, %hi(_GLOBAL_OFFSET_TABLE_) */ + 0x27390000, /* addiu t9, t9, %lo(_GLOBAL_OFFSET_TABLE_) */ + 0x8f390008, /* lw t9, 8(t9) */ + 0x00000000, /* nop */ + 0x03200008, /* jr t9 */ + 0x00000000 /* nop */ +}; + +/* The format of subsequent PLT entries. */ +static const bfd_vma mips_vxworks_exec_plt_entry[] = { + 0x10000000, /* b .PLT_resolver */ + 0x24180000, /* li t8, <pltindex> */ + 0x3c190000, /* lui t9, %hi(<.got.plt slot>) */ + 0x27390000, /* addiu t9, t9, %lo(<.got.plt slot>) */ + 0x8f390000, /* lw t9, 0(t9) */ + 0x00000000, /* nop */ + 0x03200008, /* jr t9 */ + 0x00000000 /* nop */ +}; + +/* The format of the first PLT entry in a VxWorks shared object. */ +static const bfd_vma mips_vxworks_shared_plt0_entry[] = { + 0x8f990008, /* lw t9, 8(gp) */ + 0x00000000, /* nop */ + 0x03200008, /* jr t9 */ + 0x00000000, /* nop */ + 0x00000000, /* nop */ + 0x00000000 /* nop */ +}; + +/* The format of subsequent PLT entries. */ +static const bfd_vma mips_vxworks_shared_plt_entry[] = { + 0x10000000, /* b .PLT_resolver */ + 0x24180000 /* li t8, <pltindex> */ +}; + /* Look up an entry in a MIPS ELF linker hash table. */ #define mips_elf_link_hash_lookup(table, string, create, copy, follow) \ @@ -715,6 +784,8 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry, ret->call_stub = NULL; ret->call_fp_stub = NULL; ret->forced_local = FALSE; + ret->is_branch_target = FALSE; + ret->is_relocation_target = FALSE; ret->tls_type = GOT_NORMAL; } @@ -1896,14 +1967,19 @@ mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2) : e1->d.h == e2->d.h); } -/* Returns the dynamic relocation section for DYNOBJ. */ +/* Return the dynamic relocation section. If it doesn't exist, try to + create a new it if CREATE_P, otherwise return NULL. Also return NULL + if creation fails. */ static asection * -mips_elf_rel_dyn_section (bfd *dynobj, bfd_boolean create_p) +mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p) { - static const char dname[] = ".rel.dyn"; + const char *dname; asection *sreloc; + bfd *dynobj; + dname = MIPS_ELF_REL_DYN_NAME (info); + dynobj = elf_hash_table (info)->dynobj; sreloc = bfd_get_section_by_name (dynobj, dname); if (sreloc == NULL && create_p) { @@ -2121,7 +2197,7 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset, || h->root.root.type == bfd_link_hash_undefweak); /* Emit necessary relocations. */ - sreloc = mips_elf_rel_dyn_section (dynobj, FALSE); + sreloc = mips_elf_rel_dyn_section (info, FALSE); /* General Dynamic. */ if (*tls_type_p & GOT_TLS_GD) @@ -2240,14 +2316,46 @@ mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type, return got_index; } -/* Returns the GOT offset at which the indicated address can be found. - If there is not yet a GOT entry for this value, create one. If - R_SYMNDX refers to a TLS symbol, create a TLS GOT entry instead. - Returns -1 if no satisfactory GOT offset can be found. */ +/* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry + for global symbol H. .got.plt comes before the GOT, so the offset + will be negative. */ + +static bfd_vma +mips_elf_gotplt_index (struct bfd_link_info *info, + struct elf_link_hash_entry *h) +{ + bfd_vma plt_index, got_address, got_value; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); + BFD_ASSERT (h->plt.offset != (bfd_vma) -1); + + /* Calculate the index of the symbol's PLT entry. */ + plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size; + + /* Calculate the address of the associated .got.plt entry. */ + got_address = (htab->sgotplt->output_section->vma + + htab->sgotplt->output_offset + + plt_index * 4); + + /* Calculate the value of _GLOBAL_OFFSET_TABLE_. */ + got_value = (htab->root.hgot->root.u.def.section->output_section->vma + + htab->root.hgot->root.u.def.section->output_offset + + htab->root.hgot->root.u.def.value); + + return got_address - got_value; +} + +/* Return the GOT offset for address VALUE, which was derived from + a symbol belonging to INPUT_SECTION. If there is not yet a GOT + entry for this value, create one. If R_SYMNDX refers to a TLS symbol, + create a TLS GOT entry instead. Return -1 if no satisfactory GOT + offset can be found. */ static bfd_vma mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, - bfd_vma value, unsigned long r_symndx, + asection *input_section, bfd_vma value, + unsigned long r_symndx, struct mips_elf_link_hash_entry *h, int r_type) { asection *sgot; @@ -2256,7 +2364,8 @@ mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); - entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value, + entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot, + input_section, value, r_symndx, h, r_type); if (!entry) return MINUS_ONE; @@ -2350,26 +2459,27 @@ mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h, return index; } -/* Find a GOT entry that is within 32KB of the VALUE. These entries - are supposed to be placed at small offsets in the GOT, i.e., - within 32KB of GP. Return the index into the GOT for this page, - and store the offset from this entry to the desired address in - OFFSETP, if it is non-NULL. */ +/* Find a GOT page entry that points to within 32KB of VALUE, which was + calculated from a symbol belonging to INPUT_SECTION. These entries + are supposed to be placed at small offsets in the GOT, i.e., within + 32KB of GP. Return the index of the GOT entry, or -1 if no entry + could be created. If OFFSETP is nonnull, use it to return the + offset of the GOT entry from VALUE. */ static bfd_vma mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, - bfd_vma value, bfd_vma *offsetp) + asection *input_section, bfd_vma value, bfd_vma *offsetp) { asection *sgot; struct mips_got_info *g; - bfd_vma index; + bfd_vma page, index; struct mips_got_entry *entry; g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); - entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, - (value + 0x8000) - & (~(bfd_vma)0xffff), 0, + page = (value + 0x8000) & ~(bfd_vma) 0xffff; + entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot, + input_section, page, 0, NULL, R_MIPS_GOT_PAGE); if (!entry) @@ -2383,30 +2493,32 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, return index; } -/* Find a GOT entry whose higher-order 16 bits are the same as those - for value. Return the index into the GOT for this entry. */ +/* Find a local GOT entry for an R_MIPS_GOT16 relocation against VALUE, + which was calculated from a symbol belonging to INPUT_SECTION. + EXTERNAL is true if the relocation was against a global symbol + that has been forced local. */ static bfd_vma mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, - bfd_vma value, bfd_boolean external) + asection *input_section, bfd_vma value, + bfd_boolean external) { asection *sgot; struct mips_got_info *g; struct mips_got_entry *entry; + /* GOT16 relocations against local symbols are followed by a LO16 + relocation; those against global symbols are not. Thus if the + symbol was originally local, the GOT16 relocation should load the + equivalent of %hi(VALUE), otherwise it should load VALUE itself. */ if (! external) - { - /* Although the ABI says that it is "the high-order 16 bits" that we - want, it is really the %high value. The complete value is - calculated with a `addiu' of a LO16 relocation, just as with a - HI16/LO16 pair. */ - value = mips_elf_high (value) << 16; - } + value = mips_elf_high (value) << 16; g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); - entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value, 0, NULL, - R_MIPS_GOT16); + entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot, + input_section, value, 0, + NULL, R_MIPS_GOT16); if (entry) return entry->gotidx; else @@ -2431,20 +2543,24 @@ mips_elf_got_offset_from_index (bfd *dynobj, bfd *output_bfd, return sgot->output_section->vma + sgot->output_offset + index - gp; } -/* Create a local GOT entry for VALUE. Return the index of the entry, - or -1 if it could not be created. If R_SYMNDX refers to a TLS symbol, - create a TLS entry instead. */ +/* Create and return a local GOT entry for VALUE, which was calculated + from a symbol belonging to INPUT_SECTON. Return NULL if it could not + be created. If R_SYMNDX refers to a TLS symbol, create a TLS entry + instead. */ static struct mips_got_entry * -mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd, - struct mips_got_info *gg, - asection *sgot, bfd_vma value, - unsigned long r_symndx, +mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info, + bfd *ibfd, struct mips_got_info *gg, + asection *sgot, asection *input_section, + bfd_vma value, unsigned long r_symndx, struct mips_elf_link_hash_entry *h, int r_type) { struct mips_got_entry entry, **loc; struct mips_got_info *g; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); entry.abfd = NULL; entry.symndx = -1; @@ -2517,6 +2633,33 @@ mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd, MIPS_ELF_PUT_WORD (abfd, value, (sgot->contents + entry.gotidx)); + /* These GOT entries need a dynamic relocation on VxWorks. Because + the offset between segments is not fixed, the relocation must be + against a symbol in the same segment as the original symbol. + The easiest way to do this is to take INPUT_SECTION's output + section and emit a relocation against its section symbol. */ + if (htab->is_vxworks) + { + Elf_Internal_Rela outrel; + asection *s, *output_section; + bfd_byte *loc; + bfd_vma got_address; + int dynindx; + + s = mips_elf_rel_dyn_section (info, FALSE); + output_section = input_section->output_section; + dynindx = elf_section_data (output_section)->dynindx; + got_address = (sgot->output_section->vma + + sgot->output_offset + + entry.gotidx); + + loc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela)); + outrel.r_offset = got_address; + outrel.r_info = ELF32_R_INFO (dynindx, R_MIPS_32); + outrel.r_addend = value - output_section->vma; + bfd_elf32_swap_reloca_out (abfd, &outrel, loc); + } + return *loc; } @@ -2892,7 +3035,7 @@ mips_elf_merge_gots (void **bfd2got_, void *p) { unsigned int primary_total = lcount + tcount + arg->global_count; if (primary_total * MIPS_ELF_GOT_SIZE (bfd2got->bfd) - >= MIPS_ELF_GOT_MAX_SIZE (bfd2got->bfd)) + >= MIPS_ELF_GOT_MAX_SIZE (arg->info)) too_many_for_tls = TRUE; } @@ -3207,9 +3350,9 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info, /* Taking out PAGES entries is a worst-case estimate. We could compute the maximum number of pages that each separate input bfd uses, but it's probably not worth it. */ - got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd) + got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (info) / MIPS_ELF_GOT_SIZE (abfd)) - - MIPS_RESERVED_GOTNO - pages); + - MIPS_RESERVED_GOTNO (info) - pages); /* The number of globals that will be included in the primary GOT. See the calls to mips_elf_set_global_got_offset below for more information. */ @@ -3344,7 +3487,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info, { struct mips_got_info *gn; - assign += MIPS_RESERVED_GOTNO; + assign += MIPS_RESERVED_GOTNO (info); g->assigned_gotno = assign; g->local_gotno += assign + pages; assign = g->local_gotno + g->global_gotno + g->tls_gotno; @@ -3540,6 +3683,9 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info, struct bfd_link_hash_entry *bh; struct mips_got_info *g; bfd_size_type amt; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); /* This function may be called more than once. */ s = mips_elf_got_section (abfd, TRUE); @@ -3589,8 +3735,8 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info, g->global_gotsym = NULL; g->global_gotno = 0; g->tls_gotno = 0; - g->local_gotno = MIPS_RESERVED_GOTNO; - g->assigned_gotno = MIPS_RESERVED_GOTNO; + g->local_gotno = MIPS_RESERVED_GOTNO (info); + g->assigned_gotno = MIPS_RESERVED_GOTNO (info); g->bfd2got = NULL; g->next = NULL; g->tls_ldm_offset = MINUS_ONE; @@ -3602,9 +3748,33 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info, mips_elf_section_data (s)->elf.this_hdr.sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL; + /* VxWorks also needs a .got.plt section. */ + if (htab->is_vxworks) + { + s = bfd_make_section_with_flags (abfd, ".got.plt", + SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if (s == NULL || !bfd_set_section_alignment (abfd, s, 4)) + return FALSE; + + htab->sgotplt = s; + } return TRUE; } +/* Return true if H refers to the special VxWorks __GOTT_BASE__ or + __GOTT_INDEX__ symbols. These symbols are only special for + shared objects; they are not used in executables. */ + +static bfd_boolean +is_gott_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *h) +{ + return (mips_elf_hash_table (info)->is_vxworks + && info->shared + && (strcmp (h->root.root.string, "__GOTT_BASE__") == 0 + || strcmp (h->root.root.string, "__GOTT_INDEX__") == 0)); +} + /* Calculate the value produced by the RELOCATION (which comes from the INPUT_BFD). The ADDEND is the addend to use for this RELOCATION; RELOCATION->R_ADDEND is ignored. @@ -3667,6 +3837,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, bfd_boolean overflowed_p; /* TRUE if this relocation refers to a MIPS16 function. */ bfd_boolean target_is_16_bit_code_p = FALSE; + struct mips_elf_link_hash_table *htab; + bfd *dynobj; + + dynobj = elf_hash_table (info)->dynobj; + htab = mips_elf_hash_table (info); /* Parse the relocation. */ r_symndx = ELF_R_SYM (input_bfd, relocation->r_info); @@ -3916,52 +4091,61 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, /* Find the index into the GOT where this value is located. */ if (r_type == R_MIPS_TLS_LDM) { - g = mips_elf_local_got_index (abfd, input_bfd, info, 0, 0, NULL, - r_type); + g = mips_elf_local_got_index (abfd, input_bfd, info, + sec, 0, 0, NULL, r_type); if (g == MINUS_ONE) return bfd_reloc_outofrange; } else if (!local_p) { - /* GOT_PAGE may take a non-zero addend, that is ignored in a - GOT_PAGE relocation that decays to GOT_DISP because the - symbol turns out to be global. The addend is then added - as GOT_OFST. */ - BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE); - g = mips_elf_global_got_index (elf_hash_table (info)->dynobj, - input_bfd, - (struct elf_link_hash_entry *) h, - r_type, info); - if (h->tls_type == GOT_NORMAL - && (! elf_hash_table(info)->dynamic_sections_created - || (info->shared - && (info->symbolic || h->root.forced_local) - && h->root.def_regular))) + /* On VxWorks, CALL relocations should refer to the .got.plt + entry, which is initialized to point at the PLT stub. */ + if (htab->is_vxworks + && (r_type == R_MIPS_CALL_HI16 + || r_type == R_MIPS_CALL_LO16 + || r_type == R_MIPS_CALL16)) + { + BFD_ASSERT (addend == 0); + BFD_ASSERT (h->root.needs_plt); + g = mips_elf_gotplt_index (info, &h->root); + } + else { - /* This is a static link or a -Bsymbolic link. The - symbol is defined locally, or was forced to be local. - We must initialize this entry in the GOT. */ - bfd *tmpbfd = elf_hash_table (info)->dynobj; - asection *sgot = mips_elf_got_section (tmpbfd, FALSE); - MIPS_ELF_PUT_WORD (tmpbfd, symbol, sgot->contents + g); + /* GOT_PAGE may take a non-zero addend, that is ignored in a + GOT_PAGE relocation that decays to GOT_DISP because the + symbol turns out to be global. The addend is then added + as GOT_OFST. */ + BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE); + g = mips_elf_global_got_index (dynobj, input_bfd, + &h->root, r_type, info); + if (h->tls_type == GOT_NORMAL + && (! elf_hash_table(info)->dynamic_sections_created + || (info->shared + && (info->symbolic || h->root.forced_local) + && h->root.def_regular))) + { + /* This is a static link or a -Bsymbolic link. The + symbol is defined locally, or was forced to be local. + We must initialize this entry in the GOT. */ + asection *sgot = mips_elf_got_section (dynobj, FALSE); + MIPS_ELF_PUT_WORD (dynobj, symbol, sgot->contents + g); + } } } - else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16) - /* There's no need to create a local GOT entry here; the - calculation for a local GOT16 entry does not involve G. */ + else if (!htab->is_vxworks + && (r_type == R_MIPS_CALL16 || (r_type == R_MIPS_GOT16))) + /* The calculation below does not involve "g". */ break; else { - g = mips_elf_local_got_index (abfd, input_bfd, - info, symbol + addend, r_symndx, h, - r_type); + g = mips_elf_local_got_index (abfd, input_bfd, info, sec, + symbol + addend, r_symndx, h, r_type); if (g == MINUS_ONE) return bfd_reloc_outofrange; } /* Convert GOT indices to actual offsets. */ - g = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj, - abfd, input_bfd, g); + g = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, g); break; case R_MIPS_HI16: @@ -3974,10 +4158,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, case R_MIPS16_GPREL: gp0 = _bfd_get_gp_value (input_bfd); gp = _bfd_get_gp_value (abfd); - if (elf_hash_table (info)->dynobj) - gp += mips_elf_adjust_gp (abfd, - mips_elf_got_info - (elf_hash_table (info)->dynobj, NULL), + if (dynobj) + gp += mips_elf_adjust_gp (abfd, mips_elf_got_info (dynobj, NULL), input_bfd); break; @@ -3988,6 +4170,27 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, if (gnu_local_gp_p) symbol = gp; + /* Relocations against the VxWorks __GOTT_BASE__ and __GOTT_INDEX__ + symbols are resolved by the loader. Add them to .rela.dyn. */ + if (h != NULL && is_gott_symbol (info, &h->root)) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + asection *s; + + s = mips_elf_rel_dyn_section (info, FALSE); + loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela); + + outrel.r_offset = (input_section->output_section->vma + + input_section->output_offset + + relocation->r_offset); + outrel.r_info = ELF32_R_INFO (h->root.dynindx, r_type); + outrel.r_addend = addend; + bfd_elf32_swap_reloca_out (abfd, &outrel, loc); + *valuep = 0; + return bfd_reloc_ok; + } + /* Figure out what kind of relocation is being performed. */ switch (r_type) { @@ -4003,7 +4206,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, case R_MIPS_REL32: case R_MIPS_64: if ((info->shared - || (elf_hash_table (info)->dynamic_sections_created + || (!htab->is_vxworks + && htab->root.dynamic_sections_created && h != NULL && h->root.def_dynamic && !h->root.def_regular)) @@ -4014,7 +4218,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, against a symbol in a shared library, then we can't know where the symbol will end up. So, we create a relocation record in the output, and leave the job up to the dynamic - linker. */ + linker. + + In VxWorks executables, references to external symbols + are handled using copy relocs or PLT stubs, so there's + no need to add a dynamic relocation here. */ value = addend; if (!mips_elf_create_dynamic_relocation (abfd, info, @@ -4166,22 +4374,20 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, case R_MIPS_GOT16: case R_MIPS_CALL16: - if (local_p) + /* VxWorks does not have separate local and global semantics for + R_MIPS_GOT16; every relocation evaluates to "G". */ + if (!htab->is_vxworks && local_p) { bfd_boolean forced; - /* The special case is when the symbol is forced to be local. We - need the full address in the GOT since no R_MIPS_LO16 relocation - follows. */ forced = ! mips_elf_local_relocation_p (input_bfd, relocation, local_sections, FALSE); - value = mips_elf_got16_entry (abfd, input_bfd, info, + value = mips_elf_got16_entry (abfd, input_bfd, info, sec, symbol + addend, forced); if (value == MINUS_ONE) return bfd_reloc_outofrange; value - = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj, - abfd, input_bfd, value); + = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, value); overflowed_p = mips_elf_overflow_p (value, 16); break; } @@ -4231,17 +4437,18 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, 0. */ if (! local_p) goto got_disp; - value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL); + value = mips_elf_got_page (abfd, input_bfd, info, sec, + symbol + addend, NULL); if (value == MINUS_ONE) return bfd_reloc_outofrange; - value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj, - abfd, input_bfd, value); + value = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, value); overflowed_p = mips_elf_overflow_p (value, 16); break; case R_MIPS_GOT_OFST: if (local_p) - mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value); + mips_elf_got_page (abfd, input_bfd, info, sec, + symbol + addend, &value); else value = addend; overflowed_p = mips_elf_overflow_p (value, 16); @@ -4426,23 +4633,31 @@ mips_elf_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section) || strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0); } -/* Add room for N relocations to the .rel.dyn section in ABFD. */ +/* Add room for N relocations to the .rel(a).dyn section in ABFD. */ static void -mips_elf_allocate_dynamic_relocations (bfd *abfd, unsigned int n) +mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info, + unsigned int n) { asection *s; + struct mips_elf_link_hash_table *htab; - s = mips_elf_rel_dyn_section (abfd, FALSE); + htab = mips_elf_hash_table (info); + s = mips_elf_rel_dyn_section (info, FALSE); BFD_ASSERT (s != NULL); - if (s->size == 0) + if (htab->is_vxworks) + s->size += n * MIPS_ELF_RELA_SIZE (abfd); + else { - /* Make room for a null element. */ - s->size += MIPS_ELF_REL_SIZE (abfd); - ++s->reloc_count; + if (s->size == 0) + { + /* Make room for a null element. */ + s->size += MIPS_ELF_REL_SIZE (abfd); + ++s->reloc_count; + } + s->size += n * MIPS_ELF_REL_SIZE (abfd); } - s->size += n * MIPS_ELF_REL_SIZE (abfd); } /* Create a rel.dyn relocation for the dynamic linker to resolve. REL @@ -4464,10 +4679,12 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd, int r_type; long indx; bfd_boolean defined_p; + struct mips_elf_link_hash_table *htab; + htab = mips_elf_hash_table (info); r_type = ELF_R_TYPE (output_bfd, rel->r_info); dynobj = elf_hash_table (info)->dynobj; - sreloc = mips_elf_rel_dyn_section (dynobj, FALSE); + sreloc = mips_elf_rel_dyn_section (info, FALSE); BFD_ASSERT (sreloc != NULL); BFD_ASSERT (sreloc->contents != NULL); BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd) @@ -4552,10 +4769,15 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd, if (defined_p && r_type != R_MIPS_REL32) *addendp += symbol; - /* The relocation is always an REL32 relocation because we don't - know where the shared library will wind up at load-time. */ - outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx, - R_MIPS_REL32); + if (htab->is_vxworks) + /* VxWorks uses non-relative relocations for this. */ + outrel[0].r_info = ELF32_R_INFO (indx, R_MIPS_32); + else + /* The relocation is always an REL32 relocation because we don't + know where the shared library will wind up at load-time. */ + outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx, + R_MIPS_REL32); + /* For strict adherence to the ABI specification, we should generate a R_MIPS_64 relocation record by itself before the _REL32/_64 record as well, such that the addend is read in as @@ -4593,6 +4815,15 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd, (sreloc->contents + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel))); } + else if (htab->is_vxworks) + { + /* VxWorks uses RELA rather than REL dynamic relocations. */ + outrel[0].r_addend = *addendp; + bfd_elf32_swap_reloca_out + (output_bfd, &outrel[0], + (sreloc->contents + + sreloc->reloc_count * sizeof (Elf32_External_Rela))); + } else bfd_elf32_swap_reloc_out (output_bfd, &outrel[0], @@ -5609,23 +5840,29 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) flagword flags; register asection *s; const char * const *namep; + struct mips_elf_link_hash_table *htab; + htab = mips_elf_hash_table (info); flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED | SEC_READONLY); - /* Mips ABI requests the .dynamic section to be read only. */ - s = bfd_get_section_by_name (abfd, ".dynamic"); - if (s != NULL) + /* The psABI requires a read-only .dynamic section, but the VxWorks + EABI doesn't. */ + if (!htab->is_vxworks) { - if (! bfd_set_section_flags (abfd, s, flags)) - return FALSE; + s = bfd_get_section_by_name (abfd, ".dynamic"); + if (s != NULL) + { + if (! bfd_set_section_flags (abfd, s, flags)) + return FALSE; + } } /* We need to create .got section. */ if (! mips_elf_create_got_section (abfd, info, FALSE)) return FALSE; - if (! mips_elf_rel_dyn_section (elf_hash_table (info)->dynobj, TRUE)) + if (! mips_elf_rel_dyn_section (info, TRUE)) return FALSE; /* Create .stub section. */ @@ -5746,6 +5983,45 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) } } + if (htab->is_vxworks) + { + /* Create the .plt, .rela.plt, .dynbss and .rela.bss sections. + Also create the _PROCEDURE_LINKAGE_TABLE symbol. */ + if (!_bfd_elf_create_dynamic_sections (abfd, info)) + return FALSE; + + /* Cache the sections created above. */ + htab->sdynbss = bfd_get_section_by_name (abfd, ".dynbss"); + htab->srelbss = bfd_get_section_by_name (abfd, ".rela.bss"); + htab->srelplt = bfd_get_section_by_name (abfd, ".rela.plt"); + htab->splt = bfd_get_section_by_name (abfd, ".plt"); + if (!htab->sdynbss + || (!htab->srelbss && !info->shared) + || !htab->srelplt + || !htab->splt) + abort (); + + /* Do the usual VxWorks handling. */ + if (!elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2)) + return FALSE; + + /* Work out the PLT sizes. */ + if (info->shared) + { + htab->plt_header_size + = 4 * ARRAY_SIZE (mips_vxworks_shared_plt0_entry); + htab->plt_entry_size + = 4 * ARRAY_SIZE (mips_vxworks_shared_plt_entry); + } + else + { + htab->plt_header_size + = 4 * ARRAY_SIZE (mips_vxworks_exec_plt0_entry); + htab->plt_entry_size + = 4 * ARRAY_SIZE (mips_vxworks_exec_plt_entry); + } + } + return TRUE; } @@ -5767,10 +6043,12 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sgot; asection *sreloc; const struct elf_backend_data *bed; + struct mips_elf_link_hash_table *htab; if (info->relocatable) return TRUE; + htab = mips_elf_hash_table (info); dynobj = elf_hash_table (info)->dynobj; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); @@ -6012,13 +6290,24 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, if (! mips_elf_create_got_section (dynobj, info, FALSE)) return FALSE; g = mips_elf_got_info (dynobj, &sgot); + if (htab->is_vxworks && !info->shared) + { + (*_bfd_error_handler) + (_("%B: GOT reloc at 0x%lx not expected in executables"), + abfd, (unsigned long) rel->r_offset); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } break; case R_MIPS_32: case R_MIPS_REL32: case R_MIPS_64: + /* In VxWorks executables, references to external symbols + are handled using copy relocs or PLT stubs, so there's + no need to add a dynamic relocation here. */ if (dynobj == NULL - && (info->shared || h != NULL) + && (info->shared || (h != NULL && !htab->is_vxworks)) && (sec->flags & SEC_ALLOC) != 0) elf_hash_table (info)->dynobj = dynobj = abfd; break; @@ -6028,15 +6317,35 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } } - if (!h && (r_type == R_MIPS_CALL_LO16 - || r_type == R_MIPS_GOT_LO16 - || r_type == R_MIPS_GOT_DISP)) + if (h) + { + ((struct mips_elf_link_hash_entry *) h)->is_relocation_target = TRUE; + + /* Relocations against the special VxWorks __GOTT_BASE__ and + __GOTT_INDEX__ symbols must be left to the loader. Allocate + room for them in .rela.dyn. */ + if (is_gott_symbol (info, h)) + { + if (sreloc == NULL) + { + sreloc = mips_elf_rel_dyn_section (info, TRUE); + if (sreloc == NULL) + return FALSE; + } + mips_elf_allocate_dynamic_relocations (dynobj, info, 1); + } + } + else if (r_type == R_MIPS_CALL_LO16 + || r_type == R_MIPS_GOT_LO16 + || r_type == R_MIPS_GOT_DISP + || (r_type == R_MIPS_GOT16 && htab->is_vxworks)) { /* We may need a local GOT entry for this relocation. We don't count R_MIPS_GOT_PAGE because we can estimate the maximum number of pages needed by looking at the size of the segment. Similar comments apply to R_MIPS_GOT16 and - R_MIPS_CALL16. We don't count R_MIPS_GOT_HI16, or + R_MIPS_CALL16, except on VxWorks, where GOT relocations + always evaluate to "G". We don't count R_MIPS_GOT_HI16, or R_MIPS_CALL_HI16 because these are always followed by an R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */ if (! mips_elf_record_local_got_symbol (abfd, r_symndx, @@ -6061,8 +6370,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_MIPS_CALL_LO16: if (h != NULL) { - /* This symbol requires a global offset table entry. */ - if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0)) + /* VxWorks call relocations point the function's .got.plt + entry, which will be allocated by adjust_dynamic_symbol. + Otherwise, this symbol requires a global GOT entry. */ + if (!htab->is_vxworks + && !mips_elf_record_global_got_symbol (h, abfd, info, g, 0)) return FALSE; /* We need a stub, not a plt entry for the undefined @@ -6148,12 +6460,15 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_MIPS_32: case R_MIPS_REL32: case R_MIPS_64: - if ((info->shared || h != NULL) + /* In VxWorks executables, references to external symbols + are handled using copy relocs or PLT stubs, so there's + no need to add a .rela.dyn entry for this relocation. */ + if ((info->shared || (h != NULL && !htab->is_vxworks)) && (sec->flags & SEC_ALLOC) != 0) { if (sreloc == NULL) { - sreloc = mips_elf_rel_dyn_section (dynobj, TRUE); + sreloc = mips_elf_rel_dyn_section (info, TRUE); if (sreloc == NULL) return FALSE; } @@ -6162,9 +6477,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, { /* When creating a shared object, we must copy these reloc types into the output file as R_MIPS_REL32 - relocs. We make room for this reloc in the - .rel.dyn reloc section. */ - mips_elf_allocate_dynamic_relocations (dynobj, 1); + relocs. Make room for this reloc in .rel(a).dyn. */ + mips_elf_allocate_dynamic_relocations (dynobj, info, 1); if ((sec->flags & MIPS_READONLY_SECTION) == MIPS_READONLY_SECTION) /* We tell the dynamic linker that there are @@ -6189,8 +6503,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* Even though we don't directly need a GOT entry for this symbol, a symbol must have a dynamic symbol table index greater that DT_MIPS_GOTSYM if there are - dynamic relocations against it. */ - if (h != NULL) + dynamic relocations against it. This does not apply + to VxWorks, which does not have the usual coupling + between global GOT entries and .dynsym entries. */ + if (h != NULL && !htab->is_vxworks) { if (dynobj == NULL) elf_hash_table (info)->dynobj = dynobj = abfd; @@ -6207,7 +6523,16 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, sizeof (Elf32_External_crinfo); break; + case R_MIPS_PC16: + if (h) + ((struct mips_elf_link_hash_entry *) h)->is_branch_target = TRUE; + break; + case R_MIPS_26: + if (h) + ((struct mips_elf_link_hash_entry *) h)->is_branch_target = TRUE; + /* Fall through. */ + case R_MIPS_GPREL16: case R_MIPS_LITERAL: case R_MIPS_GPREL32: @@ -6235,24 +6560,21 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } /* We must not create a stub for a symbol that has relocations - related to taking the function's address. */ - 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: - case R_MIPS_JALR: - break; - } + related to taking the function's address. This doesn't apply to + VxWorks, where CALL relocs refer to a .got.plt entry instead of + a normal .got entry. */ + if (!htab->is_vxworks && h != NULL) + switch (r_type) + { + default: + ((struct mips_elf_link_hash_entry *) h)->no_fn_stub = TRUE; + break; + case R_MIPS_CALL16: + case R_MIPS_CALL_HI16: + case R_MIPS_CALL_LO16: + case R_MIPS_JALR: + break; + } /* If this reloc is not a 16 bit call, and it has a global symbol, then we will need the fn_stub if there is one. @@ -6479,8 +6801,8 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info, && (h->root.type == bfd_link_hash_defweak || !h->def_regular)) { - mips_elf_allocate_dynamic_relocations (dynobj, - hmips->possibly_dynamic_relocs); + mips_elf_allocate_dynamic_relocations + (dynobj, info, hmips->possibly_dynamic_relocs); if (hmips->readonly_reloc) /* We tell the dynamic linker that there are relocations against the text segment. */ @@ -6545,6 +6867,160 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info, return TRUE; } + +/* Likewise, for VxWorks. */ + +bfd_boolean +_bfd_mips_vxworks_adjust_dynamic_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *h) +{ + bfd *dynobj; + struct mips_elf_link_hash_entry *hmips; + struct mips_elf_link_hash_table *htab; + unsigned int power_of_two; + + htab = mips_elf_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + hmips = (struct mips_elf_link_hash_entry *) h; + + /* Make sure we know what is going on here. */ + BFD_ASSERT (dynobj != NULL + && (h->needs_plt + || h->needs_copy + || h->u.weakdef != NULL + || (h->def_dynamic + && h->ref_regular + && !h->def_regular))); + + /* If the symbol is defined by a dynamic object, we need a PLT stub if + either (a) we want to branch to the symbol or (b) we're linking an + executable that needs a canonical function address. In the latter + case, the canonical address will be the address of the executable's + load stub. */ + if ((hmips->is_branch_target + || (!info->shared + && h->type == STT_FUNC + && hmips->is_relocation_target)) + && h->def_dynamic + && h->ref_regular + && !h->def_regular + && !h->forced_local) + h->needs_plt = 1; + + /* Locally-binding symbols do not need a PLT stub; we can refer to + the functions directly. */ + else if (h->needs_plt + && (SYMBOL_CALLS_LOCAL (info, h) + || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT + && h->root.type == bfd_link_hash_undefweak))) + { + h->needs_plt = 0; + return TRUE; + } + + if (h->needs_plt) + { + /* If this is the first symbol to need a PLT entry, allocate room + for the header, and for the header's .rela.plt.unloaded entries. */ + if (htab->splt->size == 0) + { + htab->splt->size += htab->plt_header_size; + if (!info->shared) + htab->srelplt2->size += 2 * sizeof (Elf32_External_Rela); + } + + /* Assign the next .plt entry to this symbol. */ + h->plt.offset = htab->splt->size; + htab->splt->size += htab->plt_entry_size; + + /* If the output file has no definition of the symbol, set the + symbol's value to the address of the stub. For executables, + point at the PLT load stub rather than the lazy resolution stub; + this stub will become the canonical function address. */ + if (!h->def_regular) + { + h->root.u.def.section = htab->splt; + h->root.u.def.value = h->plt.offset; + if (!info->shared) + h->root.u.def.value += 8; + } + + /* Make room for the .got.plt entry and the R_JUMP_SLOT relocation. */ + htab->sgotplt->size += 4; + htab->srelplt->size += sizeof (Elf32_External_Rela); + + /* Make room for the .rela.plt.unloaded relocations. */ + if (!info->shared) + htab->srelplt2->size += 3 * sizeof (Elf32_External_Rela); + + return TRUE; + } + + /* If a function symbol is defined by a dynamic object, and we do not + need a PLT stub for it, the symbol's value should be zero. */ + if (h->type == STT_FUNC + && h->def_dynamic + && h->ref_regular + && !h->def_regular) + { + h->root.u.def.value = 0; + return TRUE; + } + + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->u.weakdef != NULL) + { + BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined + || h->u.weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->u.weakdef->root.u.def.section; + h->root.u.def.value = h->u.weakdef->root.u.def.value; + return TRUE; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + if (info->shared) + return TRUE; + + /* We must allocate the symbol in our .dynbss section, which will + become part of the .bss section of the executable. There will be + an entry for this symbol in the .dynsym section. The dynamic + object will contain position independent code, so all references + from the dynamic object to this symbol will go through the global + offset table. The dynamic linker will use the .dynsym entry to + determine the address it must put in the global offset table, so + both the dynamic object and the regular object will refer to the + same memory location for the variable. */ + + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) + { + htab->srelbss->size += sizeof (Elf32_External_Rela); + h->needs_copy = 1; + } + + /* We need to figure out the alignment required for this symbol. */ + power_of_two = bfd_log2 (h->size); + if (power_of_two > 4) + power_of_two = 4; + + /* Apply the required alignment. */ + htab->sdynbss->size = BFD_ALIGN (htab->sdynbss->size, + (bfd_size_type) 1 << power_of_two); + if (power_of_two > bfd_get_section_alignment (dynobj, htab->sdynbss) + && !bfd_set_section_alignment (dynobj, htab->sdynbss, power_of_two)) + return FALSE; + + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = htab->sdynbss; + h->root.u.def.value = htab->sdynbss->size; + + /* Increment the section size to make room for the symbol. */ + htab->sdynbss->size += h->size; + + return TRUE; +} /* This function is called after all the input files have been read, and the input sections have been assigned to output sections. We @@ -6564,6 +7040,9 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, bfd_size_type local_gotno; bfd *sub; struct mips_elf_count_tls_arg count_tls_arg; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); /* The .reginfo section has a fixed size. */ ri = bfd_get_section_by_name (output_bfd, ".reginfo"); @@ -6622,9 +7101,15 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, rld. */ loadable_size += MIPS_FUNCTION_STUB_SIZE * (i + 1); - /* Assume there are two loadable segments consisting of - contiguous sections. Is 5 enough? */ - local_gotno = (loadable_size >> 16) + 5; + if (htab->is_vxworks) + /* There's no need to allocate page entries for VxWorks; R_MIPS_GOT16 + relocations against local symbols evaluate to "G", and the EABI does + not include R_MIPS_GOT_PAGE. */ + local_gotno = 0; + else + /* Assume there are two loadable segments consisting of contiguous + sections. Is 5 enough? */ + local_gotno = (loadable_size >> 16) + 5; g->local_gotno += local_gotno; s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd); @@ -6645,7 +7130,10 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, mips_elf_resolve_final_got_entries (g); - if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd)) + /* VxWorks does not support multiple GOTs. It initializes $gp to + __GOTT_BASE__[__GOTT_INDEX__], the value of which is set by the + dynamic loader. */ + if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info)) { if (! mips_elf_multi_got (output_bfd, info, g, s, local_gotno)) return FALSE; @@ -6667,9 +7155,11 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { bfd *dynobj; - asection *s; + asection *s, *sreldyn; bfd_boolean reltext; + struct mips_elf_link_hash_table *htab; + htab = mips_elf_hash_table (info); dynobj = elf_hash_table (info)->dynobj; BFD_ASSERT (dynobj != NULL); @@ -6691,6 +7181,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, determined the sizes of the various dynamic sections. Allocate memory for them. */ reltext = FALSE; + sreldyn = NULL; for (s = dynobj->sections; s != NULL; s = s->next) { const char *name; @@ -6711,7 +7202,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, /* If this relocation section applies to a read only section, then we probably need a DT_TEXTREL entry. - If the relocation section is .rel.dyn, we always + If the relocation section is .rel(a).dyn, we always assert a DT_TEXTREL entry rather than testing whether there exists a relocation to a read only section or not. */ @@ -6721,12 +7212,12 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, if ((target != NULL && (target->flags & SEC_READONLY) != 0 && (target->flags & SEC_ALLOC) != 0) - || strcmp (outname, ".rel.dyn") == 0) + || strcmp (outname, MIPS_ELF_REL_DYN_NAME (info)) == 0) reltext = TRUE; /* We use the reloc_count field as a counter if we need to copy relocs into the output file. */ - if (strcmp (name, ".rel.dyn") != 0) + if (strcmp (name, MIPS_ELF_REL_DYN_NAME (info)) != 0) s->reloc_count = 0; /* If combreloc is enabled, elf_link_sort_relocs() will @@ -6738,7 +7229,23 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, info->combreloc = 0; } } - else if (strncmp (name, ".got", 4) == 0) + else if (htab->is_vxworks && strcmp (name, ".got") == 0) + { + /* Executables do not need a GOT. */ + if (info->shared) + { + /* Allocate relocations for all but the reserved entries. */ + struct mips_got_info *g; + unsigned int count; + + g = mips_elf_got_info (dynobj, NULL); + count = (g->global_gotno + + g->local_gotno + - MIPS_RESERVED_GOTNO (info)); + mips_elf_allocate_dynamic_relocations (dynobj, info, count); + } + } + else if (!htab->is_vxworks && strncmp (name, ".got", 4) == 0) { /* _bfd_mips_elf_always_size_sections() has already done most of the work, but some symbols may have been mapped @@ -6783,7 +7290,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, BFD_ASSERT (g->assigned_gotno == g->next->local_gotno + g->next->global_gotno + g->next->tls_gotno - + MIPS_RESERVED_GOTNO); + + MIPS_RESERVED_GOTNO (info)); } } } @@ -6803,7 +7310,8 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, } if (needed_relocs) - mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs); + mips_elf_allocate_dynamic_relocations (dynobj, info, + needed_relocs); } else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0) { @@ -6822,7 +7330,9 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, else if (SGI_COMPAT (output_bfd) && strncmp (name, ".compact_rel", 12) == 0) s->size += mips_elf_hash_table (info)->compact_rel_size; - else if (strncmp (name, ".init", 5) != 0) + else if (strncmp (name, ".init", 5) != 0 + && s != htab->sgotplt + && s != htab->splt) { /* It's not one of our sections, so don't allocate space. */ continue; @@ -6837,6 +7347,14 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, if ((s->flags & SEC_HAS_CONTENTS) == 0) continue; + /* Allocate memory for this section last, since we may increase its + size above. */ + if (strcmp (name, MIPS_ELF_REL_DYN_NAME (info)) == 0) + { + sreldyn = s; + continue; + } + /* Allocate memory for the section contents. */ s->contents = bfd_zalloc (dynobj, s->size); if (s->contents == NULL) @@ -6846,6 +7364,17 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, } } + /* Allocate memory for the .rel(a).dyn section. */ + if (sreldyn != NULL) + { + sreldyn->contents = bfd_zalloc (dynobj, sreldyn->size); + if (sreldyn->contents == NULL) + { + bfd_set_error (bfd_error_no_memory); + return FALSE; + } + } + if (elf_hash_table (info)->dynamic_sections_created) { /* Add some entries to the .dynamic section. We fill in the @@ -6875,7 +7404,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, } } - if (reltext && SGI_COMPAT (output_bfd)) + if (reltext && (SGI_COMPAT (output_bfd) || htab->is_vxworks)) info->flags |= DF_TEXTREL; if ((info->flags & DF_TEXTREL) != 0) @@ -6887,48 +7416,78 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0)) return FALSE; - if (mips_elf_rel_dyn_section (dynobj, FALSE)) + if (htab->is_vxworks) { - if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0)) - return FALSE; + /* VxWorks uses .rela.dyn instead of .rel.dyn. It does not + use any of the DT_MIPS_* tags. */ + if (mips_elf_rel_dyn_section (info, FALSE)) + { + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELA, 0)) + return FALSE; - if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELSZ, 0)) - return FALSE; + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELASZ, 0)) + return FALSE; - if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0)) - return FALSE; + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELAENT, 0)) + return FALSE; + } + if (htab->splt->size > 0) + { + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTREL, 0)) + return FALSE; + + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_JMPREL, 0)) + return FALSE; + + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTRELSZ, 0)) + return FALSE; + } } + else + { + if (mips_elf_rel_dyn_section (info, FALSE)) + { + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 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_RELSZ, 0)) + return FALSE; - if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0)) - return FALSE; + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0)) + return FALSE; + } - if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 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_LOCAL_GOTNO, 0)) - return FALSE; + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 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_BASE_ADDRESS, 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_LOCAL_GOTNO, 0)) + return FALSE; - if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0)) - return FALSE; + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_SYMTABNO, 0)) + return FALSE; - if (IRIX_COMPAT (dynobj) == ict_irix5 - && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0)) - return FALSE; + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_UNREFEXTNO, 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 (! 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; + } } return TRUE; @@ -7584,6 +8143,268 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, return TRUE; } +/* Likewise, for VxWorks. */ + +bfd_boolean +_bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, + struct bfd_link_info *info, + struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) +{ + bfd *dynobj; + asection *sgot; + struct mips_got_info *g; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + + if (h->plt.offset != (bfd_vma) -1) + { + char *loc; + bfd_vma plt_address, plt_index, got_address, got_offset, branch_offset; + Elf_Internal_Rela rel; + static const bfd_vma *plt_entry; + + BFD_ASSERT (h->dynindx != -1); + BFD_ASSERT (htab->splt != NULL); + BFD_ASSERT (h->plt.offset <= htab->splt->size); + + /* Calculate the address of the .plt entry. */ + plt_address = (htab->splt->output_section->vma + + htab->splt->output_offset + + h->plt.offset); + + /* Calculate the index of the entry. */ + plt_index = ((h->plt.offset - htab->plt_header_size) + / htab->plt_entry_size); + + /* Calculate the address of the .got.plt entry. */ + got_address = (htab->sgotplt->output_section->vma + + htab->sgotplt->output_offset + + plt_index * 4); + + /* Calculate the offset of the .got.plt entry from + _GLOBAL_OFFSET_TABLE_. */ + got_offset = mips_elf_gotplt_index (info, h); + + /* Calculate the offset for the branch at the start of the PLT + entry. The branch jumps to the beginning of .plt. */ + branch_offset = -(h->plt.offset / 4 + 1) & 0xffff; + + /* Fill in the initial value of the .got.plt entry. */ + bfd_put_32 (output_bfd, plt_address, + htab->sgotplt->contents + plt_index * 4); + + /* Find out where the .plt entry should go. */ + loc = htab->splt->contents + h->plt.offset; + + if (info->shared) + { + plt_entry = mips_vxworks_shared_plt_entry; + bfd_put_32 (output_bfd, plt_entry[0] | branch_offset, loc); + bfd_put_32 (output_bfd, plt_entry[1] | plt_index, loc + 4); + } + else + { + bfd_vma got_address_high, got_address_low; + + plt_entry = mips_vxworks_exec_plt_entry; + got_address_high = ((got_address + 0x8000) >> 16) & 0xffff; + got_address_low = got_address & 0xffff; + + bfd_put_32 (output_bfd, plt_entry[0] | branch_offset, loc); + bfd_put_32 (output_bfd, plt_entry[1] | plt_index, loc + 4); + bfd_put_32 (output_bfd, plt_entry[2] | got_address_high, loc + 8); + bfd_put_32 (output_bfd, plt_entry[3] | got_address_low, loc + 12); + bfd_put_32 (output_bfd, plt_entry[4], loc + 16); + bfd_put_32 (output_bfd, plt_entry[5], loc + 20); + bfd_put_32 (output_bfd, plt_entry[6], loc + 24); + bfd_put_32 (output_bfd, plt_entry[7], loc + 28); + + loc = (htab->srelplt2->contents + + (plt_index * 3 + 2) * sizeof (Elf32_External_Rela)); + + /* Emit a relocation for the .got.plt entry. */ + rel.r_offset = got_address; + rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_MIPS_32); + rel.r_addend = h->plt.offset; + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + + /* Emit a relocation for the lui of %hi(<.got.plt slot>). */ + loc += sizeof (Elf32_External_Rela); + rel.r_offset = plt_address + 8; + rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_HI16); + rel.r_addend = got_offset; + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + + /* Emit a relocation for the addiu of %lo(<.got.plt slot>). */ + loc += sizeof (Elf32_External_Rela); + rel.r_offset += 4; + rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_LO16); + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + } + + /* Emit an R_MIPS_JUMP_SLOT relocation against the .got.plt entry. */ + loc = htab->srelplt->contents + plt_index * sizeof (Elf32_External_Rela); + rel.r_offset = got_address; + rel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_JUMP_SLOT); + rel.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + + if (!h->def_regular) + sym->st_shndx = SHN_UNDEF; + } + + BFD_ASSERT (h->dynindx != -1 || h->forced_local); + + sgot = mips_elf_got_section (dynobj, FALSE); + BFD_ASSERT (sgot != NULL); + BFD_ASSERT (mips_elf_section_data (sgot) != NULL); + g = mips_elf_section_data (sgot)->u.got_info; + BFD_ASSERT (g != NULL); + + /* See if this symbol has an entry in the GOT. */ + if (g->global_gotsym != NULL + && h->dynindx >= g->global_gotsym->dynindx) + { + bfd_vma offset; + Elf_Internal_Rela outrel; + bfd_byte *loc; + asection *s; + + /* Install the symbol value in the GOT. */ + offset = mips_elf_global_got_index (dynobj, output_bfd, h, + R_MIPS_GOT16, info); + MIPS_ELF_PUT_WORD (output_bfd, sym->st_value, sgot->contents + offset); + + /* Add a dynamic relocation for it. */ + s = mips_elf_rel_dyn_section (info, FALSE); + loc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela)); + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + offset); + outrel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_32); + outrel.r_addend = 0; + bfd_elf32_swap_reloca_out (dynobj, &outrel, loc); + } + + /* Emit a copy reloc, if needed. */ + if (h->needs_copy) + { + Elf_Internal_Rela rel; + + BFD_ASSERT (h->dynindx != -1); + + rel.r_offset = (h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset + + h->root.u.def.value); + rel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_COPY); + rel.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &rel, + htab->srelbss->contents + + (htab->srelbss->reloc_count + * sizeof (Elf32_External_Rela))); + ++htab->srelbss->reloc_count; + } + + /* If this is a mips16 symbol, force the value to be even. */ + if (sym->st_other == STO_MIPS16) + sym->st_value &= ~1; + + return TRUE; +} + +/* Install the PLT header for a VxWorks executable and finalize the + contents of .rela.plt.unloaded. */ + +static void +mips_vxworks_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info) +{ + Elf_Internal_Rela rela; + bfd_byte *loc; + bfd_vma got_value, got_value_high, got_value_low, plt_address; + static const bfd_vma *plt_entry; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); + plt_entry = mips_vxworks_exec_plt0_entry; + + /* Calculate the value of _GLOBAL_OFFSET_TABLE_. */ + got_value = (htab->root.hgot->root.u.def.section->output_section->vma + + htab->root.hgot->root.u.def.section->output_offset + + htab->root.hgot->root.u.def.value); + + got_value_high = ((got_value + 0x8000) >> 16) & 0xffff; + got_value_low = got_value & 0xffff; + + /* Calculate the address of the PLT header. */ + plt_address = htab->splt->output_section->vma + htab->splt->output_offset; + + /* Install the PLT header. */ + loc = htab->splt->contents; + bfd_put_32 (output_bfd, plt_entry[0] | got_value_high, loc); + bfd_put_32 (output_bfd, plt_entry[1] | got_value_low, loc + 4); + bfd_put_32 (output_bfd, plt_entry[2], loc + 8); + bfd_put_32 (output_bfd, plt_entry[3], loc + 12); + bfd_put_32 (output_bfd, plt_entry[4], loc + 16); + bfd_put_32 (output_bfd, plt_entry[5], loc + 20); + + /* Output the relocation for the lui of %hi(_GLOBAL_OFFSET_TABLE_). */ + loc = htab->srelplt2->contents; + rela.r_offset = plt_address; + rela.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_HI16); + rela.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + loc += sizeof (Elf32_External_Rela); + + /* Output the relocation for the following addiu of + %lo(_GLOBAL_OFFSET_TABLE_). */ + rela.r_offset += 4; + rela.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_LO16); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + loc += sizeof (Elf32_External_Rela); + + /* Fix up the remaining relocations. They may have the wrong + symbol index for _G_O_T_ or _P_L_T_ depending on the order + in which symbols were output. */ + while (loc < htab->srelplt2->contents + htab->srelplt2->size) + { + Elf_Internal_Rela rel; + + bfd_elf32_swap_reloca_in (output_bfd, loc, &rel); + rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_MIPS_32); + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + loc += sizeof (Elf32_External_Rela); + + bfd_elf32_swap_reloca_in (output_bfd, loc, &rel); + rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_HI16); + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + loc += sizeof (Elf32_External_Rela); + + bfd_elf32_swap_reloca_in (output_bfd, loc, &rel); + rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_LO16); + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + loc += sizeof (Elf32_External_Rela); + } +} + +/* Install the PLT header for a VxWorks shared library. */ + +static void +mips_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info) +{ + unsigned int i; + struct mips_elf_link_hash_table *htab; + + htab = mips_elf_hash_table (info); + + /* We just need to copy the entry byte-by-byte. */ + for (i = 0; i < ARRAY_SIZE (mips_vxworks_shared_plt0_entry); i++) + bfd_put_32 (output_bfd, mips_vxworks_shared_plt0_entry[i], + htab->splt->contents + i * 4); +} + /* Finish up the dynamic sections. */ bfd_boolean @@ -7594,7 +8415,9 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, asection *sdyn; asection *sgot; struct mips_got_info *gg, *g; + struct mips_elf_link_hash_table *htab; + htab = mips_elf_hash_table (info); dynobj = elf_hash_table (info)->dynobj; sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); @@ -7637,11 +8460,14 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, switch (dyn.d_tag) { case DT_RELENT: - s = mips_elf_rel_dyn_section (dynobj, FALSE); - BFD_ASSERT (s != NULL); dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj); break; + case DT_RELAENT: + BFD_ASSERT (htab->is_vxworks); + dyn.d_un.d_val = MIPS_ELF_RELA_SIZE (dynobj); + break; + case DT_STRSZ: /* Rewrite DT_STRSZ. */ dyn.d_un.d_val = @@ -7650,9 +8476,20 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, case DT_PLTGOT: name = ".got"; - s = bfd_get_section_by_name (output_bfd, name); - BFD_ASSERT (s != NULL); - dyn.d_un.d_ptr = s->vma; + if (htab->is_vxworks) + { + /* _GLOBAL_OFFSET_TABLE_ is defined to be the beginning + of the ".got" section in DYNOBJ. */ + s = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma + s->output_offset; + } + else + { + 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: @@ -7718,7 +8555,7 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, break; case DT_MIPS_HIPAGENO: - dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO; + dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO (info); break; case DT_MIPS_RLD_MAP: @@ -7731,6 +8568,29 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, dyn.d_un.d_ptr = s->vma; break; + case DT_RELASZ: + BFD_ASSERT (htab->is_vxworks); + /* The count does not include the JUMP_SLOT relocations. */ + if (htab->srelplt) + dyn.d_un.d_val -= htab->srelplt->size; + break; + + case DT_PLTREL: + BFD_ASSERT (htab->is_vxworks); + dyn.d_un.d_val = DT_RELA; + break; + + case DT_PLTRELSZ: + BFD_ASSERT (htab->is_vxworks); + dyn.d_un.d_val = htab->srelplt->size; + break; + + case DT_JMPREL: + BFD_ASSERT (htab->is_vxworks); + dyn.d_un.d_val = (htab->srelplt->output_section->vma + + htab->srelplt->output_offset); + break; + default: swap_out_p = FALSE; break; @@ -7742,14 +8602,33 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, } } - /* The first entry of the global offset table will be filled at - runtime. The second entry will be used by some runtime loaders. - This isn't the case of IRIX rld. */ if (sgot != NULL && sgot->size > 0) { - MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents); - MIPS_ELF_PUT_WORD (output_bfd, 0x80000000, - sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd)); + if (htab->is_vxworks) + { + /* The first entry of the global offset table points to the + ".dynamic" section. The second is initialized by the + loader and contains the shared library identifier. + The third is also initialized by the loader and points + to the lazy resolution stub. */ + MIPS_ELF_PUT_WORD (output_bfd, + sdyn->output_offset + sdyn->output_section->vma, + sgot->contents); + MIPS_ELF_PUT_WORD (output_bfd, 0, + sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd)); + MIPS_ELF_PUT_WORD (output_bfd, 0, + sgot->contents + + 2 * MIPS_ELF_GOT_SIZE (output_bfd)); + } + else + { + /* The first entry of the global offset table will be filled at + runtime. The second entry will be used by some runtime loaders. + This isn't the case of IRIX rld. */ + 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) @@ -7823,7 +8702,7 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, decided not to make. This is for the n64 irix rld, which doesn't seem to apply any relocations if there are trailing null entries. */ - s = mips_elf_rel_dyn_section (dynobj, FALSE); + s = mips_elf_rel_dyn_section (info, FALSE); dyn.d_un.d_val = (s->reloc_count * (ABI_64_P (output_bfd) ? sizeof (Elf64_Mips_External_Rel) @@ -7877,24 +8756,37 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, } } - /* We need to sort the entries of the dynamic relocation section. */ - - s = mips_elf_rel_dyn_section (dynobj, FALSE); - - if (s != NULL - && s->size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd)) + /* The psABI says that the dynamic relocations must be sorted in + increasing order of r_symndx. The VxWorks EABI doesn't require + this, and because the code below handles REL rather than RELA + relocations, using it for VxWorks would be outright harmful. */ + if (!htab->is_vxworks) { - reldyn_sorting_bfd = output_bfd; + s = mips_elf_rel_dyn_section (info, FALSE); + if (s != NULL + && s->size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd)) + { + reldyn_sorting_bfd = output_bfd; - if (ABI_64_P (output_bfd)) - qsort ((Elf64_External_Rel *) s->contents + 1, s->reloc_count - 1, - sizeof (Elf64_Mips_External_Rel), sort_dynamic_relocs_64); - else - qsort ((Elf32_External_Rel *) s->contents + 1, s->reloc_count - 1, - sizeof (Elf32_External_Rel), sort_dynamic_relocs); + if (ABI_64_P (output_bfd)) + qsort ((Elf64_External_Rel *) s->contents + 1, + s->reloc_count - 1, sizeof (Elf64_Mips_External_Rel), + sort_dynamic_relocs_64); + else + qsort ((Elf32_External_Rel *) s->contents + 1, + s->reloc_count - 1, sizeof (Elf32_External_Rel), + sort_dynamic_relocs); + } } } + if (htab->is_vxworks && htab->splt->size > 0) + { + if (info->shared) + mips_vxworks_finish_shared_plt (output_bfd, info); + else + mips_vxworks_finish_exec_plt (output_bfd, info); + } return TRUE; } @@ -8962,9 +9854,36 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd) ret->use_rld_obj_head = FALSE; ret->rld_value = 0; ret->mips16_stubs_seen = FALSE; + ret->is_vxworks = FALSE; + ret->srelbss = NULL; + ret->sdynbss = NULL; + ret->srelplt = NULL; + ret->srelplt2 = NULL; + ret->sgotplt = NULL; + ret->splt = NULL; + ret->plt_header_size = 0; + ret->plt_entry_size = 0; return &ret->root.root; } + +/* Likewise, but indicate that the target is VxWorks. */ + +struct bfd_link_hash_table * +_bfd_mips_vxworks_link_hash_table_create (bfd *abfd) +{ + struct bfd_link_hash_table *ret; + + ret = _bfd_mips_elf_link_hash_table_create (abfd); + if (ret) + { + struct mips_elf_link_hash_table *htab; + + htab = (struct mips_elf_link_hash_table *) ret; + htab->is_vxworks = 1; + } + return ret; +} /* We need to use a special link routine to handle the .reginfo and the .mdebug sections. We need to merge all instances of these @@ -8987,6 +9906,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) EXTR esym; unsigned int i; bfd_size_type amt; + struct mips_elf_link_hash_table *htab; static const char * const secname[] = { @@ -9003,6 +9923,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) generic size_dynamic_sections renumbered them out from under us. Rather than trying somehow to prevent the renumbering, just do the sort again. */ + htab = mips_elf_hash_table (info); if (elf_hash_table (info)->dynamic_sections_created) { bfd *dynobj; @@ -9053,6 +9974,14 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) elf_gp (abfd) = (h->u.def.value + h->u.def.section->output_section->vma + h->u.def.section->output_offset); + else if (htab->is_vxworks + && (h = bfd_link_hash_lookup (info->hash, + "_GLOBAL_OFFSET_TABLE_", + FALSE, FALSE, TRUE)) + && h->type == bfd_link_hash_defined) + elf_gp (abfd) = (h->u.def.section->output_section->vma + + h->u.def.section->output_offset + + h->u.def.value); else if (info->relocatable) { bfd_vma lo = MINUS_ONE; @@ -9064,7 +9993,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) lo = o->vma; /* And calculate GP relative to that. */ - elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (abfd); + elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (info); } else { @@ -9796,6 +10725,12 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) new_flags &= ~EF_MIPS_UCODE; old_flags &= ~EF_MIPS_UCODE; + /* Don't care about the PIC flags from dynamic objects; they are + PIC by design. */ + if ((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0 + && (ibfd->flags & DYNAMIC) != 0) + new_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC); + if (new_flags == old_flags) return TRUE; |