diff --git a/bfd/arc-plt.def b/bfd/arc-plt.def new file mode 100644 index 0000000..c7c70e8 --- /dev/null +++ b/bfd/arc-plt.def @@ -0,0 +1,75 @@ +/* Arc V2 Related PLT entries. */ + +PLT_TYPE_START (ELF_ARCV2_PIC) + PLT_ENTRY (0x2730, 0x7f8b, 0x0000, 0x0000) /* ld %r11, [pcl,0] : 0 to be replaced by _DYNAMIC@GOTPC+4 */ + PLT_ENTRY (0x2730, 0x7f8a, 0x0000, 0x0000) /* ld %r10, [pcl,0] : 0 to be replaced by _DYNAMIC@GOTPC+8 */ + PLT_ENTRY (0x2020, 0x0280) /* j [%r10] */ + PLT_ENTRY (0x0,0x0,0x0,0x0,0x0,0x0) /* padding */ + + PLT_ELEM (0x2730, 0x7f8c, 0x0000, 0x0000) /* ld %r12, [%pc,func@got] */ + PLT_ELEM (0x2021, 0x0300) /* j.d [%r12] */ + PLT_ELEM (0x240a, 0x1fc0) /* mov %r12, %pcl */ + + ENTRY_RELOC (4, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 4) + ENTRY_RELOC (12, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 8) + ENTRY_RELOC (20, 32, 0xFFFFFFFF, SGOT, 0) + + ELEM_RELOC (4, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 0) + +PLT_TYPE_END (ELF_ARCV2_PIC) + +PLT_TYPE_START (ELF_ARCV2_ABS) + PLT_ENTRY (0x1600,0x700b,0x0000,0x0000) /* ld %r11, [0] */ + PLT_ENTRY (0x1600,0x700a,0x0000,0x0000) /* ld %r10, [0] */ + PLT_ENTRY (0x2020,0x0280) /* j [%r10] */ + PLT_ENTRY (0x0,0x0,0x0,0x0,0x0,0x0) /* padding */ + + PLT_ELEM (0x2730, 0x7f8c, 0x0000, 0x0000) /* ld %r12, [%pcl,func@gotpc] */ + PLT_ELEM (0x2021,0x0300) /* j.d [%r12] */ + PLT_ELEM (0x240a,0x1fc0) /* mov %r12, %pcl */ + + ENTRY_RELOC (4, 32, 0xFFFFFFFF, SGOT | MIDDLE_ENDIAN, 4) + ENTRY_RELOC (12, 32, 0xFFFFFFFF, SGOT | MIDDLE_ENDIAN, 8) + ENTRY_RELOC (20, 32, 0xFFFFFFFF, SGOT, 0) + + ELEM_RELOC (4, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 0) + +PLT_TYPE_END (ELF_ARCV2_ABS) + + +/* Non Arc V2 Related PLT entries. */ + +PLT_TYPE_START (ELF_ARC_PIC) + PLT_ENTRY (0x2730,0x7f8b,0x0000,0x0000) /* ld %r11, [pcl,0] : 0 to be replaced by _DYNAMIC@GOTPC+4 */ + PLT_ENTRY (0x2730,0x7f8a,0x0000,0x0000) /* ld %r10, [pcl,0] : 0 to be replaced by -DYNAMIC@GOTPC+8 */ + PLT_ENTRY (0x2020,0x0280) /* j [%r10] */ + PLT_ENTRY (0x0,0x0) /* padding */ + + PLT_ELEM (0x2730,0x7f8c,0x0000,0x0000) /* ld %r12, [%pc,func@got] */ + PLT_ELEM (0x7c20) /* j_s.d [%r12] */ + PLT_ELEM (0x74ef) /* mov_s %r12, %pcl */ + + ENTRY_RELOC (4, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 4) + ENTRY_RELOC (12, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 8) + ENTRY_RELOC (20, 32, 0xFFFFFFFF, SGOT, 0) + + ELEM_RELOC (4, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 0) + +PLT_TYPE_END (ELF_ARC_PIC) + +PLT_TYPE_START (ELF_ARC_ABS) + PLT_ENTRY (0x1600,0x700b,0x0000,0x0000) /* ld %r11, [0] */ + PLT_ENTRY (0x1600,0x700a,0x0000,0x0000) /* ld %r10, [0] */ + PLT_ENTRY (0x2020,0x0280) /* j [%r10] */ + PLT_ENTRY (0x0,0x0) /* padding */ + + PLT_ELEM (0x2730,0x7f8c,0x0000,0x0000) /* ld %r12, [%pc,func@gotpc] */ + PLT_ELEM (0x7c20,0x74ef) /* mov_s %r12, %pcl */ + + ENTRY_RELOC (4, 32, 0xFFFFFFFF, SGOT | MIDDLE_ENDIAN, 4) + ENTRY_RELOC (12, 32, 0xFFFFFFFF, SGOT | MIDDLE_ENDIAN, 8) + ENTRY_RELOC (20, 32, 0xFFFFFFFF, SGOT, 0) + + ELEM_RELOC (4, 32, 0xFFFFFFFF, SGOT | RELATIVE_INSN_32 | MIDDLE_ENDIAN, 0) + +PLT_TYPE_END (ELF_ARC_ABS) diff --git a/bfd/arc-plt.h b/bfd/arc-plt.h new file mode 100644 index 0000000..fc6654b --- /dev/null +++ b/bfd/arc-plt.h @@ -0,0 +1,161 @@ +#ifndef ARC_PLT_H +#define ARC_PLT_H + +#include + + +/* Instructions appear in memory as a sequence of half-words (16 bit); + individual half-words are represented on the target in target byte order. + We use 'unsigned short' on the host to represent the PLT templates, + and translate to target byte order as we copy to the target. */ +typedef uint16_t insn_hword; + +enum plt_reloc_symbol { + LAST_RELOC = 0, + + SGOT = 1, + + RELATIVE = (1 << 8), + RELATIVE_INSN_32 = (1 << 9), + RELATIVE_INSN_24 = (1 << 10), + + MIDDLE_ENDIAN = (1 << 11) +}; + +#define IS_RELATIVE(S) ((S & (RELATIVE | RELATIVE_INSN_24 | RELATIVE_INSN_32)) != 0) +#define IS_INSN_32(S) ((S & RELATIVE_INSN_32) != 0) +#define IS_INSN_24(S) ((S & RELATIVE_INSN_24) != 0) +#define IS_MIDDLE_ENDIAN(S) ((S & MIDDLE_ENDIAN) != 0) +#define SYM_ONLY(S) (S & 0xFF) + +struct plt_reloc +{ + bfd_vma offset; + bfd_vma size; + bfd_vma mask; + enum plt_reloc_symbol symbol; + bfd_vma addend; +}; + + +#define PLT_TYPE_START(NAME) NAME, +#define PLT_TYPE_END(NAME) +#define PLT_ENTRY(...) +#define PLT_ELEM(...) +#define ENTRY_RELOC(...) +#define ELEM_RELOC(...) +enum plt_types_enum { + PLT_START = -1, +#include "arc-plt.def" + PLT_MAX +}; +#undef PLT_TYPE_START +#undef PLT_TYPE_END +#undef PLT_ENTRY +#undef PLT_ELEM +#undef ENTRY_RELOC +#undef ELEM_RELOC + +typedef insn_hword insn_hword_array[]; + +struct plt_version_t { + const insn_hword_array *entry; + const bfd_vma entry_size; + const insn_hword_array *elem; + const bfd_vma elem_size; + + const struct plt_reloc *entry_relocs; + const struct plt_reloc *elem_relocs; +}; + +#define PLT_TYPE_START(NAME) \ + const insn_hword NAME##_plt_entry[] = { +#define PLT_TYPE_END(NAME) }; +#define PLT_ENTRY(...) __VA_ARGS__, +#define PLT_ELEM(...) +#define ENTRY_RELOC(...) +#define ELEM_RELOC(...) + #include "arc-plt.def" +#undef PLT_TYPE_START +#undef PLT_TYPE_END +#undef PLT_ENTRY +#undef PLT_ELEM +#undef ENTRY_RELOC +#undef ELEM_RELOC + +#define PLT_TYPE_START(NAME) \ + const struct plt_reloc NAME##_plt_entry_relocs[] = { +#define PLT_TYPE_END(NAME) \ + {0, 0, 0, LAST_RELOC, 0} \ + }; +#define PLT_ENTRY(...) +#define PLT_ELEM(...) +#define ENTRY_RELOC(...) { __VA_ARGS__ }, +#define ELEM_RELOC(...) + #include "arc-plt.def" +#undef PLT_TYPE_START +#undef PLT_TYPE_END +#undef PLT_ENTRY +#undef PLT_ELEM +#undef ENTRY_RELOC +#undef ELEM_RELOC + + +#define PLT_TYPE_START(NAME) \ + const insn_hword NAME##_plt_elem[] = { +#define PLT_TYPE_END(NAME) }; +#define PLT_ENTRY(...) +#define PLT_ELEM(...) __VA_ARGS__, +#define ENTRY_RELOC(...) +#define ELEM_RELOC(...) + #include "arc-plt.def" +#undef PLT_TYPE_START +#undef PLT_TYPE_END +#undef PLT_ENTRY +#undef PLT_ELEM +#undef ENTRY_RELOC +#undef ELEM_RELOC + +#define PLT_TYPE_START(NAME) \ + const struct plt_reloc NAME##_plt_elem_relocs[] = { +#define PLT_TYPE_END(NAME) \ + {0, 0, 0, LAST_RELOC, 0} \ + }; +#define PLT_ENTRY(...) +#define PLT_ELEM(...) +#define ENTRY_RELOC(...) +#define ELEM_RELOC(...) { __VA_ARGS__ }, + #include "arc-plt.def" +#undef PLT_TYPE_START +#undef PLT_TYPE_END +#undef PLT_ENTRY +#undef PLT_ELEM +#undef ENTRY_RELOC +#undef ELEM_RELOC + + +#define PLT_TYPE_START(NAME) \ + { \ + .entry = &NAME##_plt_entry, \ + .entry_size = sizeof (NAME##_plt_entry), \ + .elem = &NAME##_plt_elem, \ + .elem_size = sizeof (NAME##_plt_elem), \ + .entry_relocs = NAME##_plt_entry_relocs, \ + .elem_relocs = NAME##_plt_elem_relocs +#define PLT_TYPE_END(NAME) }, +#define PLT_ENTRY(...) +#define PLT_ELEM(...) +#define ENTRY_RELOC(...) +#define ELEM_RELOC(...) +struct plt_version_t plt_versions[PLT_MAX] = { + #include "arc-plt.def" +}; +#undef PLT_TYPE_START +#undef PLT_TYPE_END +#undef PLT_ENTRY +#undef PLT_ELEM +#undef ENTRY_RELOC +#undef ELEM_RELOC + + +#endif /* ARC_PLT_H */ diff --git a/bfd/elf32-arc.c b/bfd/elf32-arc.c index deadc6a..87a22ce 100644 --- a/bfd/elf32-arc.c +++ b/bfd/elf32-arc.c @@ -26,10 +26,47 @@ #include "elf/arc.h" #include "libiberty.h" #include "opcode/arc-func.h" +#include "arc-plt.h" +#ifdef DEBUG +# define PR_DEBUG(fmt, args...) fprintf (stderr, fmt, ##args) +#else +# define PR_DEBUG(fmt, args...) +#endif + +/* #define ARC_ENABLE_DEBUG 1 */ +#ifndef ARC_ENABLE_DEBUG #define ARC_DEBUG(...) -#define DEBUG(...) printf (__ARGV__) -#define DEBUG_ARC_RELOC(A) +#else +static char * +name_for_global_symbol (struct elf_link_hash_entry *h) +{ + static char *local_str = "(local)"; + if (h == NULL) + return local_str; + else + return h->root.root.string; +} +#define ARC_DEBUG(args...) fprintf (stderr, ##args) +#endif + + +#define ADD_RELA(BFD, SECTION, OFFSET, SYM_IDX, TYPE, ADDEND) \ + { \ + struct elf_link_hash_table *_htab = elf_hash_table (info); \ + bfd_vma loc = (bfd_vma) _htab->srel##SECTION->contents \ + + ( \ + (_htab->srel##SECTION->reloc_count) \ + * sizeof (Elf32_External_Rela) \ + ); \ + _htab->srel##SECTION->reloc_count++; \ + Elf_Internal_Rela _rel; \ + _rel.r_addend = ADDEND; \ + _rel.r_offset = (_htab->s##SECTION)->output_section->vma \ + + (_htab->s##SECTION)->output_offset + OFFSET; \ + _rel.r_info = ELF32_R_INFO (SYM_IDX, TYPE); \ + bfd_elf32_swap_reloca_out (BFD, &_rel, (bfd_byte *) loc); \ + } struct arc_local_data { @@ -51,6 +88,7 @@ struct dynamic_sections asection * sgot; asection * srelgot; asection * sgotplt; + asection * srelgotplt; asection * sdyn; asection * splt; asection * srelplt; @@ -80,6 +118,74 @@ const char * dyn_section_names[DYN_SECTION_TYPES_END] = ".rela.plt" }; +enum tls_type_e { + GOT_UNKNOWN = 0, + GOT_NORMAL, + GOT_TLS_GD, + GOT_TLS_IE, + GOT_TLS_LE +}; + +enum tls_got_entries { + NONE = 0, + MOD, + OFF, + MOD_AND_OFF +}; + +struct got_entry +{ + struct got_entry *next; + enum tls_type_e type; + bfd_vma offset; + bfd_boolean processed; + bfd_boolean created_dyn_relocation; + enum tls_got_entries existing_entries; +}; + +static void +new_got_entry_to_list (struct got_entry **list, + enum tls_type_e type, + bfd_vma offset, + enum tls_got_entries existing_entries) +{ + /* Find list end. Avoid having multiple entries of the same + type. */ + struct got_entry **p = list; + while (*p != NULL) + { + if ((*p)->type == type) + return; + p = &((*p)->next); + } + + struct got_entry *entry = + (struct got_entry *) malloc (sizeof(struct got_entry)); + + entry->type = type; + entry->offset = offset; + entry->next = NULL; + entry->processed = FALSE; + entry->created_dyn_relocation = FALSE; + entry->existing_entries = existing_entries; + + /* Add the entry to the end of the list. */ + *p = entry; +} + +static bfd_boolean +symbol_has_entry_of_type (struct got_entry *list, enum tls_type_e type) +{ + while (list != NULL) + { + if (list->type == type) + return TRUE; + list = list->next; + } + + return FALSE; +} + /* The default symbols representing the init and fini dyn values. TODO: Check what is the relation of those strings with arclinux.em and DT_INIT. */ @@ -129,6 +235,8 @@ is_reloc_SDA_relative (reloc_howto_type *howto) static bfd_boolean is_reloc_for_GOT (reloc_howto_type * howto) { + if (strstr (howto->name, "TLS") != NULL) + return FALSE; return (strstr (howto->name, "GOT") != NULL) ? TRUE : FALSE; } @@ -138,6 +246,12 @@ is_reloc_for_PLT (reloc_howto_type * howto) return (strstr (howto->name, "PLT") != NULL) ? TRUE : FALSE; } +static bfd_boolean +is_reloc_for_TLS (reloc_howto_type *howto) +{ + return (strstr (howto->name, "TLS") != NULL) ? TRUE : FALSE; +} + #define arc_bfd_get_8(A,B,C) bfd_get_8(A,B) #define arc_bfd_get_16(A,B,C) bfd_get_16(A,B) #define arc_bfd_put_8(A,B,C,D) bfd_put_8(A,B,C) @@ -208,7 +322,8 @@ enum howto_list static struct reloc_howto_struct elf_arc_howto_table[] = { #include "elf/arc-reloc.def" -/* Example of what is generated by the preprocessor. Currently kept as an example. +/* Example of what is generated by the preprocessor. Currently kept as an + * example. HOWTO (R_ARC_NONE, // Type. 0, // Rightshift. 2, // Size (0 = byte, 1 = short, 2 = long). @@ -276,7 +391,7 @@ static const struct arc_reloc_map arc_reloc_map[] = #undef ARC_RELOC_HOWTO static reloc_howto_type * -bfd_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, +arc_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) { unsigned int i; @@ -290,8 +405,82 @@ bfd_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, return NULL; } +/* Function to set the ELF flag bits. */ +static bfd_boolean +arc_elf_set_private_flags (bfd *abfd, flagword flags) +{ + elf_elfheader (abfd)->e_flags = flags; + elf_flags_init (abfd) = TRUE; + return TRUE; +} + +/* Print private flags. */ +static bfd_boolean +arc_elf_print_private_bfd_data (bfd *abfd, void * ptr) +{ + FILE *file = (FILE *) ptr; + flagword flags; + + BFD_ASSERT (abfd != NULL && ptr != NULL); + + /* Print normal ELF private data. */ + _bfd_elf_print_private_bfd_data (abfd, ptr); + + flags = elf_elfheader (abfd)->e_flags; + fprintf (file, _("private flags = 0x%lx:"), (unsigned long) flags); + + switch (flags & EF_ARC_MACH_MSK) + { + case EF_ARC_CPU_GENERIC : fprintf (file, " -mcpu=generic"); break; + case EF_ARC_CPU_ARCV2HS : fprintf (file, " -mcpu=ARCv2HS"); break; + case EF_ARC_CPU_ARCV2EM : fprintf (file, " -mcpu=ARCv2EM"); break; + case E_ARC_MACH_ARC600 : fprintf (file, " -mcpu=ARC600"); break; + case E_ARC_MACH_ARC601 : fprintf (file, " -mcpu=ARC601"); break; + case E_ARC_MACH_ARC700 : fprintf (file, " -mcpu=ARC700"); break; + default: + fprintf (file, "-mcpu=unknown"); + break; + } + + switch (flags & EF_ARC_OSABI_MSK) + { + case E_ARC_OSABI_ORIG : fprintf (file, " (ABI:legacy)"); break; + case E_ARC_OSABI_V2 : fprintf (file, " (ABI:v2)"); break; + case E_ARC_OSABI_V3 : fprintf (file, " (ABI:v3)"); break; + default: + fprintf (file, "(ABI:unknown)"); + break; + } + + fputc ('\n', file); + return TRUE; +} + +/* Copy backend specific data from one object module to another. */ + +static bfd_boolean +arc_elf_copy_private_bfd_data (bfd *ibfd, bfd *obfd) +{ + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return TRUE; + + BFD_ASSERT (!elf_flags_init (obfd) + || elf_elfheader (obfd)->e_flags == elf_elfheader (ibfd)->e_flags); + + elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; + elf_flags_init (obfd) = TRUE; + + /* Copy object attributes. */ + _bfd_elf_copy_obj_attributes (ibfd, obfd); + + return TRUE; +} + + static reloc_howto_type * -bfd_elf32_bfd_reloc_name_lookup (bfd * abfd ATTRIBUTE_UNUSED, const char *r_name) +bfd_elf32_bfd_reloc_name_lookup (bfd * abfd ATTRIBUTE_UNUSED, + const char *r_name) { unsigned int i; @@ -317,6 +506,104 @@ arc_info_to_howto_rel (bfd * abfd ATTRIBUTE_UNUSED, cache_ptr->howto = arc_elf_howto (r_type); } +/* Merge backend specific data from an object file to the output + object file when linking. */ +static bfd_boolean +arc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) +{ + unsigned short mach_ibfd; + static unsigned short mach_obfd = EM_NONE; + flagword out_flags; + flagword in_flags; + asection *sec; + + /* Check if we have the same endianess. */ + if (! _bfd_generic_verify_endian_match (ibfd, obfd)) + { + _bfd_error_handler (_("ERROR: Endian Match failed. Attempting to link " + "%B with binary %s of opposite endian-ness"), + ibfd, bfd_get_filename (obfd)); + return FALSE; + } + + /* Collect ELF flags. */ + in_flags = elf_elfheader (ibfd)->e_flags & EF_ARC_MACH_MSK; + out_flags = elf_elfheader (obfd)->e_flags & EF_ARC_MACH_MSK; + + if (!elf_flags_init (obfd)) /* First call, no flags set. */ + { + elf_flags_init (obfd) = TRUE; + out_flags = in_flags; + } + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return TRUE; + + /* Check to see if the input BFD actually contains any sections. Do + not short-circuit dynamic objects; their section list may be + emptied by elf_link_add_object_symbols. */ + if (!(ibfd->flags & DYNAMIC)) + { + bfd_boolean null_input_bfd = TRUE; + bfd_boolean only_data_sections = TRUE; + + for (sec = ibfd->sections; sec != NULL; sec = sec->next) + { + if ((bfd_get_section_flags (ibfd, sec) + & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) + == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) + only_data_sections = FALSE; + + null_input_bfd = FALSE; + } + + if (null_input_bfd || only_data_sections) + return TRUE; + } + + + /* Complain about various flag/architecture mismatches. */ + mach_ibfd = elf_elfheader (ibfd)->e_machine; + if (mach_obfd == EM_NONE) + { + mach_obfd = mach_ibfd; + } + else + { + if (mach_ibfd != mach_obfd) + { + _bfd_error_handler (_("ERROR: Attempting to link %B " + "with a binary %s of different architecture"), + ibfd, bfd_get_filename (obfd)); + return FALSE; + } + else if (in_flags != out_flags) + { + /* Warn if different flags. */ + (*_bfd_error_handler) + (_("%s: uses different e_flags (0x%lx) fields than " + "previous modules (0x%lx)"), + bfd_get_filename (ibfd), (long)in_flags, (long)out_flags); + if (in_flags && out_flags) + return FALSE; + /* MWDT doesnt set the eflags hence make sure we choose the + eflags set by gcc. */ + in_flags = in_flags > out_flags ? in_flags : out_flags; + } + } + + /* Update the flags. */ + elf_elfheader (obfd)->e_flags = in_flags; + + if (bfd_get_mach (obfd) < bfd_get_mach (ibfd)) + { + return bfd_set_arch_mach (obfd, bfd_arch_arc, bfd_get_mach (ibfd)); + } + + return TRUE; +} + /* Set the right machine number for an ARC ELF file. */ static bfd_boolean arc_elf_object_p (bfd * abfd) @@ -374,7 +661,8 @@ arc_elf_object_p (bfd * abfd) This gets the ARC architecture right based on the machine number. */ static void -arc_elf_final_write_processing (bfd * abfd, bfd_boolean linker ATTRIBUTE_UNUSED) +arc_elf_final_write_processing (bfd * abfd, + bfd_boolean linker ATTRIBUTE_UNUSED) { unsigned long val; unsigned long emf; @@ -404,6 +692,7 @@ arc_elf_final_write_processing (bfd * abfd, bfd_boolean linker ATTRIBUTE_UNUSED) } elf_elfheader (abfd)->e_flags &= ~EF_ARC_MACH; elf_elfheader (abfd)->e_flags |= val; + elf_elfheader (abfd)->e_machine = emf; /* Record whatever is the current syscall ABI version. */ @@ -435,102 +724,180 @@ struct arc_relocation_data static void debug_arc_reloc (struct arc_relocation_data reloc_data) { - fprintf (stderr, "Reloc type=%s, should_relocate = %s\n", + PR_DEBUG ("Reloc type=%s, should_relocate = %s\n", reloc_data.howto->name, reloc_data.should_relocate ? "true" : "false"); - fprintf (stderr, " offset = 0x%x, addend = 0x%x\n", + PR_DEBUG (" offset = 0x%x, addend = 0x%x\n", (unsigned int) reloc_data.reloc_offset, (unsigned int) reloc_data.reloc_addend); - fprintf (stderr, " Symbol:\n"); - fprintf (stderr, " value = 0x%08x\n", + PR_DEBUG (" Symbol:\n"); + PR_DEBUG (" value = 0x%08x\n", (unsigned int) reloc_data.sym_value); if (reloc_data.sym_section != NULL) { - fprintf (stderr, "IN IF\n"); - fprintf (stderr, - " section name = %s, output_offset 0x%08x, output_section->vma = 0x%08x\n", + PR_DEBUG ("IN IF\n"); + PR_DEBUG ( + " section name = %s, output_offset 0x%08x", reloc_data.sym_section->name, - (unsigned int) reloc_data.sym_section->output_offset, - (unsigned int) reloc_data.sym_section->output_section->vma); + (unsigned int) reloc_data.sym_section->output_offset); + if (reloc_data.sym_section->output_section != NULL) + { + PR_DEBUG ( + ", output_section->vma = 0x%08x", + ((unsigned int) reloc_data.sym_section->output_section->vma)); + } + + PR_DEBUG ( "\n"); } else - fprintf (stderr, " symbol section is NULL\n"); + { + PR_DEBUG ( " symbol section is NULL\n"); + } - fprintf (stderr, " Input_section:\n"); + PR_DEBUG ( " Input_section:\n"); if (reloc_data.input_section != NULL) { - fprintf (stderr, + PR_DEBUG ( " section name = %s, output_offset 0x%08x, output_section->vma = 0x%08x\n", reloc_data.input_section->name, (unsigned int) reloc_data.input_section->output_offset, (unsigned int) reloc_data.input_section->output_section->vma); - fprintf (stderr, " changed_address = 0x%08x\n", + PR_DEBUG ( " changed_address = 0x%08x\n", (unsigned int) (reloc_data.input_section->output_section->vma + reloc_data.input_section->output_offset + reloc_data.reloc_offset)); } else - fprintf (stderr, " input section is NULL\n"); + { + PR_DEBUG ( " input section is NULL\n"); + } } static ATTRIBUTE_UNUSED bfd_vma get_middle_endian_relocation (bfd_vma reloc) { - bfd_vma ret = - ((reloc & 0xffff0000) >> 16) | - ((reloc & 0xffff) << 16); + bfd_vma ret = ((reloc & 0xffff0000) >> 16) | + ((reloc & 0xffff) << 16); return ret; } -#define ME(RELOC) (get_middle_endian_reloction(RELOC)) - -#define S (reloc_data.sym_value \ - + reloc_data.sym_section->output_offset \ - + reloc_data.sym_section->output_section->vma) +#define S (reloc_data.sym_value \ + + (reloc_data.sym_section->output_section != NULL ? \ + (reloc_data.sym_section->output_offset \ + + reloc_data.sym_section->output_section->vma) : 0) \ + ) +#define L (reloc_data.sym_value \ + + (reloc_data.sym_section->output_section != NULL ? \ + (reloc_data.sym_section->output_offset \ + + reloc_data.sym_section->output_section->vma) : 0) \ + ) #define A (reloc_data.reloc_addend) #define B (0) #define G (reloc_data.got_offset_value) -#define GOT (reloc_data.got_symbol_vma + 12) -#define L (reloc_data.sym_value \ - + reloc_data.sym_section->output_section->vma \ - + reloc_data.sym_section->output_offset) +#define GOT (reloc_data.got_symbol_vma) +#define GOT_BEGIN (htab->sgot->output_section->vma) + #define MES (0) - /* P: relative offset to PCL The offset should be to the current location - aligned to 32 bits. */ -#define P ( \ - (reloc_data.input_section->output_section->vma \ - + reloc_data.input_section->output_offset \ - + (reloc_data.reloc_offset - (bitsize >= 32 ? 4 : 0)) \ + /* P: relative offset to PCL The offset should be to the + current location aligned to 32 bits. */ +#define P ( \ + ( \ + (reloc_data.input_section->output_section != NULL ? \ + reloc_data.input_section->output_section->vma : 0) \ + + reloc_data.input_section->output_offset \ + + (reloc_data.reloc_offset - (bitsize >= 32 ? 4 : 0)) \ ) & ~0x3) #define PDATA ( \ (reloc_data.input_section->output_section->vma \ + reloc_data.input_section->output_offset \ + (reloc_data.reloc_offset) \ - ) & ~0x3) + )) #define SECTSTAR (reloc_data.input_section->output_offset) #define SECTSTART (reloc_data.input_section->output_offset) #define _SDA_BASE_ (reloc_data.sdata_begin_symbol_vma) +#define TLS_REL ((elf_hash_table (info))->tls_sec->output_section->vma) +#define _SDA_BASE_ (reloc_data.sdata_begin_symbol_vma) +#define TLS_TBSS (8) +#define TCB_SIZE (8) + +#define NON_ME(VALUE) (reverse_me (reloc_data, VALUE)) #define none (0) +#define PRINT_DEBUG_RELOC_INFO_BEFORE(FORMULA) \ + {\ + asection *sym_section = reloc_data.sym_section; \ + asection *input_section = reloc_data.input_section; \ + ARC_DEBUG ("FORMULA = " #FORMULA "\n"); \ + ARC_DEBUG ("S = 0x%x\n", S); \ + ARC_DEBUG ("A = 0x%x\n", A); \ + ARC_DEBUG ("L = 0x%x\n", L); \ + if (sym_section->output_section != NULL) \ + { \ + ARC_DEBUG ("symbol_section->vma = 0x%x\n", \ + sym_section->output_section->vma + sym_section->output_offset); \ + } \ + else \ + { \ + ARC_DEBUG ("symbol_section->vma = NULL\n"); \ + } \ + if (input_section->output_section != NULL) \ + { \ + ARC_DEBUG ("symbol_section->vma = 0x%x\n", \ + input_section->output_section->vma + input_section->output_offset); \ + } \ + else \ + { \ + ARC_DEBUG ("symbol_section->vma = NULL\n"); \ + } \ + ARC_DEBUG ("PCL = 0x%x\n", P); \ + ARC_DEBUG ("P = 0x%x\n", P); \ + ARC_DEBUG ("G = 0x%x\n", G); \ + ARC_DEBUG ("SDA_OFFSET = 0x%x\n", _SDA_BASE_); \ + ARC_DEBUG ("SDA_SET = %d\n", reloc_data.sdata_begin_symbol_vma_set); \ + ARC_DEBUG ("GOT_OFFSET = 0x%x\n", GOT); \ + ARC_DEBUG ("relocation = 0x%08x\n", relocation); \ + ARC_DEBUG ("before = 0x%08x\n", (unsigned int) insn); \ + ARC_DEBUG ("data = 0x%08x (%u) (%d)\n", (unsigned int) relocation, (unsigned int) relocation, (int) relocation); \ + } + +#define PRINT_DEBUG_RELOC_INFO_AFTER \ + { \ + ARC_DEBUG ("after = 0x%08x\n", (unsigned int) insn); \ + } + #define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ case R_##TYPE: \ { \ bfd_vma bitsize ATTRIBUTE_UNUSED = BITSIZE; \ relocation = FORMULA ; \ + PRINT_DEBUG_RELOC_INFO_BEFORE(FORMULA) \ insn = RELOC_FUNCTION (insn, relocation); \ + PRINT_DEBUG_RELOC_INFO_AFTER \ } \ break; +static bfd_vma +reverse_me (struct arc_relocation_data reloc_data, bfd_vma reloc) +{ + if (reloc_data.input_section && reloc_data.input_section->flags & SEC_CODE) + return ((0x0000fffff & reloc) << 16) | ((0xffff0000 & reloc) >> 16); + else + return reloc; +} + static bfd_reloc_status_type -arc_do_relocation (bfd_byte * contents, struct arc_relocation_data reloc_data) +arc_do_relocation (bfd_byte * contents, + struct arc_relocation_data reloc_data, + struct bfd_link_info *info) { bfd_vma relocation = 0; bfd_vma insn; bfd_vma orig_insn ATTRIBUTE_UNUSED; + struct elf_link_hash_table *htab ATTRIBUTE_UNUSED = elf_hash_table (info); if (reloc_data.should_relocate == FALSE) - return bfd_reloc_notsupported; + return bfd_reloc_ok; switch (reloc_data.howto->size) { @@ -576,12 +943,13 @@ arc_do_relocation (bfd_byte * contents, struct arc_relocation_data reloc_data) #define DEBUG_ARC_RELOC(A) debug_arc_reloc (A) if (flag != bfd_reloc_ok) { - fprintf (stderr, "Relocation overflows !!!!\n"); + PR_DEBUG ( "Relocation overflows !!!!\n"); DEBUG_ARC_RELOC (reloc_data); - fprintf (stderr, - "Relocation value = signed -> %d, unsigned -> %u, hex -> (0x%08x)\n", + PR_DEBUG ( + "Relocation value = signed -> %d, unsigned -> %u" + ", hex -> (0x%08x)\n", (int) relocation, (unsigned int) relocation, (unsigned int) relocation); @@ -627,59 +995,59 @@ arc_do_relocation (bfd_byte * contents, struct arc_relocation_data reloc_data) #undef ARC_RELOC_HOWTO -static bfd_vma * -arc_get_local_got_offsets (bfd * abfd) +static struct got_entry ** +arc_get_local_got_ents (bfd * abfd) { - static bfd_vma *local_got_offsets = NULL; + static struct got_entry **local_got_ents = NULL; - if (local_got_offsets == NULL) + if (local_got_ents == NULL) { size_t size; - unsigned int i; Elf_Internal_Shdr *symtab_hdr = &((elf_tdata (abfd))->symtab_hdr); size = symtab_hdr->sh_info * sizeof (bfd_vma); - local_got_offsets = (bfd_vma *) bfd_alloc (abfd, size); - if (local_got_offsets == NULL) + local_got_ents = (struct got_entry **) + bfd_alloc (abfd, sizeof(struct got_entry *) * size); + if (local_got_ents == NULL) return FALSE; - elf_local_got_offsets (abfd) = local_got_offsets; - for (i = 0; i < symtab_hdr->sh_info; i++) - local_got_offsets[i] = (bfd_vma) - 1; + + memset (local_got_ents, 0, sizeof(struct got_entry *) * size); + elf_local_got_ents (abfd) = local_got_ents; } - return local_got_offsets; + return local_got_ents; } - /* Relocate an arc ELF section. Function : elf_arc_relocate_section Brief : Relocate an arc section, by handling all the relocations - appearing in that section. + appearing in that section. Args : output_bfd : The bfd being written to. - info : Link information. - input_bfd : The input bfd. - input_section : The section being relocated. - contents : contents of the section being relocated. - relocs : List of relocations in the section. - local_syms : is a pointer to the swapped in local symbols. - local_section : is an array giving the section in the input file - corresponding to the st_shndx field of each - local symbol. */ + info : Link information. + input_bfd : The input bfd. + input_section : The section being relocated. + contents : contents of the section being relocated. + relocs : List of relocations in the section. + local_syms : is a pointer to the swapped in local symbols. + local_section : is an array giving the section in the input file + corresponding to the st_shndx field of each + local symbol. */ static bfd_boolean -elf_arc_relocate_section (bfd * output_bfd, +elf_arc_relocate_section (bfd * output_bfd, struct bfd_link_info * info, - bfd * input_bfd, - asection * input_section, - bfd_byte * contents, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, Elf_Internal_Rela * relocs, Elf_Internal_Sym * local_syms, - asection ** local_sections) + asection ** local_sections) { - Elf_Internal_Shdr * symtab_hdr; + Elf_Internal_Shdr * symtab_hdr; struct elf_link_hash_entry ** sym_hashes; - bfd_vma * local_got_offsets; - Elf_Internal_Rela * rel; - Elf_Internal_Rela * relend; + struct got_entry ** local_got_ents; + Elf_Internal_Rela * rel; + Elf_Internal_Rela * relend; + struct elf_link_hash_table *htab = elf_hash_table (info); symtab_hdr = &((elf_tdata (input_bfd))->symtab_hdr); sym_hashes = elf_sym_hashes (input_bfd); @@ -689,11 +1057,12 @@ elf_arc_relocate_section (bfd * output_bfd, for (; rel < relend; rel++) { enum elf_arc_reloc_type r_type; - reloc_howto_type * howto; - unsigned long r_symndx; + reloc_howto_type * howto; + unsigned long r_symndx; struct elf_link_hash_entry * h; - Elf_Internal_Sym * sym; - asection * sec; + Elf_Internal_Sym * sym; + asection * sec; + struct elf_link_hash_entry *h2; struct arc_relocation_data reloc_data = { @@ -704,19 +1073,66 @@ elf_arc_relocate_section (bfd * output_bfd, .sym_section = NULL, /* asection *sym_section; */ .howto = NULL, /* reloc_howto_type *howto; */ .input_section = NULL, /* asection *input_section; */ - .sdata_begin_symbol_vma = 0, /* bfd_vma sdata_begin_symbol_vma; */ - .sdata_begin_symbol_vma_set = FALSE, /* bfd_vma sdata_begin_symbol_vma_set; */ + .sdata_begin_symbol_vma = 0, /* bfd_vma sdata_begin_symbol_vma; */ + .sdata_begin_symbol_vma_set = FALSE, /* bfd_vma + sdata_begin_symbol_vma_set; */ .got_symbol_vma = 0, /* bfd_vma got_symbol_vma; */ - .should_relocate = FALSE /* bfd_boolean should_relocate; */ + .should_relocate = FALSE /* bfd_boolean should_relocate; */ }; - struct elf_link_hash_entry *h2; + r_type = ELF32_R_TYPE (rel->r_info); + + if (r_type >= (int) R_ARC_max) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + howto = &elf_arc_howto_table[r_type]; + + r_symndx = ELF32_R_SYM (rel->r_info); + + /* If we are generating another .o file and the symbol in not + local, skip this relocation. */ + if (bfd_link_relocatable (info)) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + + /* Checks if this is a local symbol and thus the reloc + might (will??) be against a section symbol. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sec = local_sections[r_symndx]; + + /* for RELA relocs.Just adjust the addend + value in the relocation entry. */ + rel->r_addend += sec->output_offset + sym->st_value; + + BFD_DEBUG_PIC ( + PR_DEBUG ("local symbols reloc " + "(section=%d %s) seen in %s\n", + r_symndx, + local_sections[r_symndx]->name, + __PRETTY_FUNCTION__) + ); + } + } + + continue; + } h2 = elf_link_hash_lookup (elf_hash_table (info), "__SDATA_BEGIN__", FALSE, FALSE, TRUE); if (reloc_data.sdata_begin_symbol_vma_set == FALSE - && h2 != NULL && h2->root.type != bfd_link_hash_undefined) + && h2 != NULL && h2->root.type != bfd_link_hash_undefined + && h2->root.u.def.section->output_section != NULL) + /* TODO: Verify this condition. */ { reloc_data.sdata_begin_symbol_vma = (h2->root.u.def.value + @@ -724,32 +1140,11 @@ elf_arc_relocate_section (bfd * output_bfd, reloc_data.sdata_begin_symbol_vma_set = TRUE; } - h2 = elf_link_hash_lookup (elf_hash_table (info), - "_GLOBAL_OFFSET_TABLE_", FALSE, FALSE, - TRUE); - if (h2 != NULL && h2->root.type != bfd_link_hash_undefined) - { - reloc_data.got_symbol_vma = - (h2->root.u.def.value + - h2->root.u.def.section->output_section->vma); - } - - r_type = ELF32_R_TYPE (rel->r_info); - - if (r_type >= (int) R_ARC_max) - { - bfd_set_error (bfd_error_bad_value); - return FALSE; - } - howto = arc_elf_howto (r_type); - reloc_data.input_section = input_section; reloc_data.howto = howto; reloc_data.reloc_offset = rel->r_offset; reloc_data.reloc_addend = rel->r_addend; - r_symndx = ELF32_R_SYM (rel->r_info); - /* This is a final link. */ h = NULL; sym = NULL; @@ -757,17 +1152,101 @@ elf_arc_relocate_section (bfd * output_bfd, if (r_symndx < symtab_hdr->sh_info) /* A local symbol. */ { + local_got_ents = arc_get_local_got_ents (output_bfd); + struct got_entry *entry = local_got_ents[r_symndx]; + sym = local_syms + r_symndx; sec = local_sections[r_symndx]; reloc_data.sym_value = sym->st_value; reloc_data.sym_section = sec; - if (is_reloc_for_GOT (reloc_data.howto)) - { - local_got_offsets = arc_get_local_got_offsets (output_bfd); - reloc_data.got_offset_value = local_got_offsets[r_symndx]; - } + if ((is_reloc_for_GOT (howto) + || is_reloc_for_TLS (howto)) && entry != NULL) + { + if (is_reloc_for_TLS (howto)) + while (entry->type == GOT_NORMAL && entry->next != NULL) + entry = entry->next; + + if (is_reloc_for_GOT (howto)) + while (entry->type != GOT_NORMAL && entry->next != NULL) + entry = entry->next; + + if (entry->type == GOT_TLS_GD && entry->processed == FALSE) + { + bfd_vma sym_vma = sym->st_value + + sec->output_section->vma + + sec->output_offset; + + /* Create dynamic relocation for local sym. */ + ADD_RELA (output_bfd, got, entry->offset, 0, + R_ARC_TLS_DTPMOD, 0); + ADD_RELA (output_bfd, got, entry->offset+4, 0, + R_ARC_TLS_DTPOFF, 0); + + bfd_vma sec_vma = sec->output_section->vma + + sec->output_offset; + bfd_put_32 (output_bfd, sym_vma - sec_vma, + htab->sgot->contents + entry->offset + 4); + + ARC_DEBUG ("arc_info: FIXED -> GOT_TLS_GD value " + "= 0x%x @ 0x%x, for symbol %s\n", + sym_vma - sec_vma, + htab->sgot->contents + entry->offset + 4, + "(local)"); + + entry->processed = TRUE; + } + if (entry->type == GOT_TLS_IE && entry->processed == FALSE) + { + bfd_vma sym_vma = sym->st_value + + sec->output_section->vma + + sec->output_offset; + bfd_vma sec_vma = htab->tls_sec->output_section->vma; + bfd_put_32 (output_bfd, sym_vma - sec_vma, + htab->sgot->contents + entry->offset); + /* TODO: Check if this type of relocs is the cause + for all the ARC_NONE dynamic relocs. */ + + ARC_DEBUG ("arc_info: FIXED -> GOT_TLS_IE value = " + "0x%x @ 0x%x, for symbol %s\n", + sym_vma - sec_vma, + htab->sgot->contents + entry->offset, + "(local)"); + + entry->processed = TRUE; + } + if (entry->type == GOT_NORMAL && entry->processed == FALSE) + { + bfd_vma sec_vma = reloc_data.sym_section->output_section->vma + + reloc_data.sym_section->output_offset; + + bfd_put_32 (output_bfd, reloc_data.sym_value + sec_vma, + htab->sgot->contents + entry->offset); + + ARC_DEBUG ("arc_info: PATCHED: 0x%08x @ 0x%08x for " + "sym %s in got offset 0x%x\n", + reloc_data.sym_value + sec_vma, + htab->sgot->output_section->vma + + htab->sgot->output_offset + entry->offset, + "(local)", + entry->offset); + entry->processed = TRUE; + } + + reloc_data.got_offset_value = entry->offset; + ARC_DEBUG ("arc_info: GOT_ENTRY = %d, offset = 0x%x, " + "vma = 0x%x for symbol %s\n", + entry->type, entry->offset, + htab->sgot->output_section->vma + + htab->sgot->output_offset + entry->offset, + "(local)"); + } + + BFD_ASSERT (htab->sgot != NULL || !is_reloc_for_GOT (howto)); + if (htab->sgot != NULL) + reloc_data.got_symbol_vma = htab->sgot->output_section->vma + + htab->sgot->output_offset; reloc_data.should_relocate = TRUE; } @@ -790,46 +1269,62 @@ elf_arc_relocate_section (bfd * output_bfd, reloc_data.should_relocate = TRUE; - if (is_reloc_for_GOT (howto)) + if (is_reloc_for_GOT (howto) && !bfd_link_pic (info)) { - struct dynamic_sections ds = - arc_create_dynamic_sections (output_bfd, info); - - /* TODO: Change it to use arc_do_relocation with ARC_32 - reloc. */ + /* TODO: Change it to use arc_do_relocation with + ARC_32 reloc. Try to use ADD_RELA macro. */ bfd_vma relocation = reloc_data.sym_value + reloc_data.reloc_addend - + reloc_data.sym_section->output_offset - + reloc_data.sym_section->output_section->vma; - - bfd_put_32 (output_bfd, relocation, ds.sgot->contents + h->got.offset); - + + (reloc_data.sym_section->output_section != NULL ? + (reloc_data.sym_section->output_offset + + reloc_data.sym_section->output_section->vma) + : 0); + + BFD_ASSERT (h->got.glist); + bfd_vma got_offset = h->got.glist->offset; + bfd_put_32 (output_bfd, relocation, + htab->sgot->contents + got_offset); + } + if (is_reloc_for_PLT (howto) && h->plt.offset != (bfd_vma) -1) + { + /* TODO: This is repeated up here. */ + reloc_data.sym_value = h->plt.offset; + reloc_data.sym_section = htab->splt; } } else if (h->root.type == bfd_link_hash_undefweak) { /* Is weak symbol and has no definition. */ - continue; + if (is_reloc_for_GOT (howto)) + { + reloc_data.sym_value = h->root.u.def.value; + reloc_data.sym_section = htab->sgot; + reloc_data.should_relocate = TRUE; + } + else if (is_reloc_for_PLT (howto) + && h->plt.offset != (bfd_vma) -1) + { + /* TODO: This is repeated up here. */ + reloc_data.sym_value = h->plt.offset; + reloc_data.sym_section = htab->splt; + reloc_data.should_relocate = TRUE; + } + else + continue; } else { if (is_reloc_for_GOT (howto)) { - struct dynamic_sections ds = - arc_create_dynamic_sections (output_bfd, info); - reloc_data.sym_value = h->root.u.def.value; - reloc_data.sym_section = ds.sgot; + reloc_data.sym_section = htab->sgot; reloc_data.should_relocate = TRUE; } else if (is_reloc_for_PLT (howto)) { - struct dynamic_sections ds = - arc_create_dynamic_sections (output_bfd, info); - reloc_data.sym_value = h->plt.offset; - reloc_data.sym_section = ds.splt; + reloc_data.sym_section = htab->splt; reloc_data.should_relocate = TRUE; } @@ -841,10 +1336,205 @@ elf_arc_relocate_section (bfd * output_bfd, } } - reloc_data.got_offset_value = h->got.offset; + if (h->got.glist != NULL) + { + struct got_entry *entry = h->got.glist; + if (is_reloc_for_GOT (howto) || is_reloc_for_TLS (howto)) + { + if (! elf_hash_table (info)->dynamic_sections_created + || (bfd_link_pic (info) + && SYMBOL_REFERENCES_LOCAL (info, h))) + { + reloc_data.sym_value = h->root.u.def.value; + reloc_data.sym_section = h->root.u.def.section; + + if (is_reloc_for_TLS (howto)) + while (entry->type == GOT_NORMAL && entry->next != NULL) + entry = entry->next; + + if (entry->processed == FALSE + && (entry->type == GOT_TLS_GD + || entry->type == GOT_TLS_IE)) + { + bfd_vma sym_value = h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset; + + bfd_vma sec_vma = + elf_hash_table (info)->tls_sec->output_section->vma; + + bfd_put_32 (output_bfd, + sym_value - sec_vma, + htab->sgot->contents + entry->offset + + (entry->existing_entries == MOD_AND_OFF ? 4 : 0)); + + + ARC_DEBUG ("arc_info: FIXED -> %s value = 0x%x " + "@ 0x%x, for symbol %s\n", + (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" : + "GOT_TLS_IE"), + sym_value - sec_vma, + htab->sgot->contents + entry->offset + + (entry->existing_entries == MOD_AND_OFF ? 4 : 0), + h->root.root.string); + + entry->processed = TRUE; + } + if (entry->type == GOT_TLS_IE && entry->processed == FALSE) + { + bfd_vma sec_vma = htab->tls_sec->output_section->vma; + bfd_put_32 (output_bfd, + reloc_data.sym_value - sec_vma, + htab->sgot->contents + entry->offset); + } + if (entry->type == GOT_NORMAL && entry->processed == FALSE) + { + bfd_vma sec_vma = + reloc_data.sym_section->output_section->vma + + reloc_data.sym_section->output_offset; + + if (h->root.type != bfd_link_hash_undefweak) + { + bfd_put_32 (output_bfd, + reloc_data.sym_value + sec_vma, + htab->sgot->contents + entry->offset); + + ARC_DEBUG ("arc_info: PATCHED: 0x%08x " + "@ 0x%08x for sym %s in got offset 0x%x\n", + reloc_data.sym_value + sec_vma, + htab->sgot->output_section->vma + + htab->sgot->output_offset + entry->offset, + h->root.root.string, + entry->offset); + } + else + { + ARC_DEBUG ("arc_info: PATCHED: NOT_PATCHED " + "@ 0x%08x for sym %s in got offset 0x%x " + "(is undefweak)\n", + htab->sgot->output_section->vma + + htab->sgot->output_offset + entry->offset, + h->root.root.string, + entry->offset); + } + + entry->processed = TRUE; + } + } + } + reloc_data.got_offset_value = entry->offset; + ARC_DEBUG ("arc_info: GOT_ENTRY = %d, offset = 0x%x, " + "vma = 0x%x for symbol %s\n", + entry->type, entry->offset, + htab->sgot->output_section->vma + + htab->sgot->output_offset + entry->offset, + h->root.root.string); + } + BFD_ASSERT (htab->sgot != NULL || !is_reloc_for_GOT (howto)); + if (htab->sgot != NULL) + reloc_data.got_symbol_vma = htab->sgot->output_section->vma + + htab->sgot->output_offset; + } - if (is_reloc_SDA_relative (howto) && reloc_data.sdata_begin_symbol_vma_set == FALSE) + switch (r_type) + { + case R_ARC_32: + case R_ARC_32_ME: + case R_ARC_PC32: + case R_ARC_32_PCREL: + if (bfd_link_pic (info) + && ((r_type != R_ARC_PC32 && r_type != R_ARC_32_PCREL) + || (h != NULL + && h->dynindx != -1 + && (!info->symbolic || !h->def_regular)))) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + bfd_boolean skip = FALSE; + bfd_boolean relocate = FALSE; + asection *sreloc = _bfd_elf_get_dynamic_reloc_section + (input_bfd, input_section, + /*RELA*/ TRUE); + + BFD_ASSERT (sreloc != NULL); + + outrel.r_offset = _bfd_elf_section_offset (output_bfd, + info, + input_section, + rel->r_offset); + if (outrel.r_offset == (bfd_vma) -1) + skip = TRUE; + + outrel.r_addend = rel->r_addend; + outrel.r_offset += (input_section->output_section->vma + + input_section->output_offset); + + if (skip) + { + memset (&outrel, 0, sizeof outrel); + relocate = FALSE; + } + else if (r_type == R_ARC_PC32 + || r_type == R_ARC_32_PCREL) + { + BFD_ASSERT (h != NULL && h->dynindx != -1); + if ((input_section->flags & SEC_ALLOC) != 0) + relocate = FALSE; + else + relocate = TRUE; + outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); + } + else + { + /* Handle local symbols, they either do not have a + global hash table entry (h == NULL), or are + forced local due to a version script + (h->forced_local), or the third condition is + legacy, it appears to say something like, for + links where we are pre-binding the symbols, or + there's not an entry for this symbol in the + dynamic symbol table, and it's a regular symbol + not defined in a shared object, then treat the + symbol as local, resolve it now. */ + if (h == NULL + || ((info->symbolic || h->dynindx == -1) + && h->def_regular) + || h->forced_local) + { + relocate = TRUE; + /* outrel.r_addend = 0; */ + outrel.r_info = ELF32_R_INFO (0, R_ARC_RELATIVE); + } + else + { + BFD_ASSERT (h->dynindx != -1); + if ((input_section->flags & SEC_ALLOC) != 0) + relocate = FALSE; + else + relocate = TRUE; + outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_32); + } + } + + BFD_ASSERT (sreloc->contents != 0); + + loc = sreloc->contents; + loc += sreloc->reloc_count * sizeof (Elf32_External_Rela); + sreloc->reloc_count += 1; + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + + if (relocate == FALSE) + continue; + } + break; + default: + break; + } + + if (is_reloc_SDA_relative (howto) + && (reloc_data.sdata_begin_symbol_vma_set == FALSE)) { (*_bfd_error_handler) ("Error: Linker symbol __SDATA_BEGIN__ not found"); @@ -853,7 +1543,8 @@ elf_arc_relocate_section (bfd * output_bfd, } DEBUG_ARC_RELOC (reloc_data); - if (arc_do_relocation (contents, reloc_data) != bfd_reloc_ok) + + if (arc_do_relocation (contents, reloc_data, info) != bfd_reloc_ok) return FALSE; } @@ -863,85 +1554,95 @@ elf_arc_relocate_section (bfd * output_bfd, static struct dynamic_sections arc_create_dynamic_sections (bfd * abfd, struct bfd_link_info *info) { - static bfd * dynobj = NULL; + struct elf_link_hash_table *htab; + bfd *dynobj; struct dynamic_sections ds = - { - .initialized = FALSE, - .sgot = NULL, - .srelgot = NULL, - .sgotplt = NULL, - .sdyn = NULL, - .splt = NULL, - .srelplt = NULL - }; - - if (dynobj == NULL) { - elf_hash_table (info)->dynobj = dynobj = abfd; - if (!_bfd_elf_create_got_section (dynobj, info)) - return ds; + .initialized = FALSE, + .sgot = NULL, + .srelgot = NULL, + .sgotplt = NULL, + .srelgotplt = NULL, + .sdyn = NULL, + .splt = NULL, + .srelplt = NULL + }; + + htab = elf_hash_table (info); + BFD_ASSERT (htab); + + /* Create dynamic sections for relocatable executables so that we + can copy relocations. */ + if (! htab->dynamic_sections_created && bfd_link_pic (info)) + { + if (! _bfd_elf_link_create_dynamic_sections (abfd, info)) + BFD_ASSERT (0); } - else - dynobj = (elf_hash_table (info))->dynobj; - ds.sgot = bfd_get_section_by_name (dynobj, ".got"); + dynobj = (elf_hash_table (info))->dynobj; - ds.srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - if (ds.srelgot == NULL) + if (dynobj) { - ds.srelgot = bfd_make_section_with_flags (dynobj, ".rela.got", - SEC_ALLOC - | SEC_LOAD - | SEC_HAS_CONTENTS - | SEC_IN_MEMORY - | SEC_LINKER_CREATED - | SEC_READONLY); - if (ds.srelgot == NULL - || !bfd_set_section_alignment (dynobj, ds.srelgot, 2)) - return ds; - } + ds.sgot = htab->sgot; + ds.srelgot = htab->srelgot; - ds.sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); + ds.sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); + ds.srelgotplt = ds.srelplt; - ds.sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); - ds.splt = bfd_get_section_by_name (dynobj, ".plt"); - ds.srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + ds.splt = bfd_get_section_by_name (dynobj, ".plt"); + ds.srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); + } + + if (htab->dynamic_sections_created) + { + ds.sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + } ds.initialized = TRUE; return ds; } -#define ADD_SYMBOL_REF_SEC_AND_RELOC(SECNAME, COND_FOR_RELOC, H) \ - ds.s##SECNAME->size; \ - { \ - if (COND_FOR_RELOC) ds.srel##SECNAME->size += sizeof (Elf32_External_Rela); \ - if (H) \ - if (h->dynindx == -1 && !h->forced_local) \ - if (! bfd_elf_link_record_dynamic_symbol (info, H)) \ - return FALSE; \ - ds.s##SECNAME->size += 4; \ - } +#define ADD_SYMBOL_REF_SEC_AND_RELOC(SECNAME, COND_FOR_RELOC, H) \ + htab->s##SECNAME->size; \ + { \ + if (COND_FOR_RELOC) \ + { \ + htab->srel##SECNAME->size += sizeof (Elf32_External_Rela); \ + ARC_DEBUG ("arc_info: Added reloc space in " \ + #SECNAME " section at " __FILE__ \ + ":%d for symbol\n", \ + __LINE__, name_for_global_symbol (H)); \ + } \ + if (H) \ + if (h->dynindx == -1 && !h->forced_local) \ + if (! bfd_elf_link_record_dynamic_symbol (info, H)) \ + return FALSE; \ + htab->s##SECNAME->size += 4; \ + } static bfd_boolean -elf_arc_check_relocs (bfd * abfd, +elf_arc_check_relocs (bfd * abfd, struct bfd_link_info * info, - asection * sec, + asection * sec, const Elf_Internal_Rela * relocs) { - Elf_Internal_Shdr * symtab_hdr; - struct elf_link_hash_entry ** sym_hashes; - bfd_vma * local_got_offsets; - const Elf_Internal_Rela * rel; - const Elf_Internal_Rela * rel_end; - bfd * dynobj ATTRIBUTE_UNUSED; + Elf_Internal_Shdr * symtab_hdr; + struct elf_link_hash_entry ** sym_hashes; + struct got_entry ** local_got_ents; + const Elf_Internal_Rela * rel; + const Elf_Internal_Rela * rel_end; + bfd * dynobj; + asection * sreloc = NULL; + struct elf_link_hash_table * htab = elf_hash_table (info); + + if (bfd_link_relocatable (info)) + return TRUE; dynobj = (elf_hash_table (info))->dynobj; symtab_hdr = &((elf_tdata (abfd))->symtab_hdr); sym_hashes = elf_sym_hashes (abfd); - local_got_offsets = arc_get_local_got_offsets (abfd); - - struct dynamic_sections ds = arc_create_dynamic_sections (abfd, info); + local_got_ents = arc_get_local_got_ents (abfd); rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) @@ -958,7 +1659,16 @@ elf_arc_check_relocs (bfd * abfd, bfd_set_error (bfd_error_bad_value); return FALSE; } - howto = arc_elf_howto (r_type); + howto = &elf_arc_howto_table[r_type]; + + if (dynobj == NULL + && (is_reloc_for_GOT (howto) == TRUE + || is_reloc_for_TLS (howto) == TRUE)) + { + dynobj = elf_hash_table (info)->dynobj = abfd; + if (! _bfd_elf_create_got_section (abfd, info)) + return FALSE; + } /* Load symbol information. */ r_symndx = ELF32_R_SYM (rel->r_info); @@ -967,6 +1677,67 @@ elf_arc_check_relocs (bfd * abfd, else /* Global one. */ h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + switch (r_type) + { + case R_ARC_32: + case R_ARC_32_ME: + /* During shared library creation, these relocs should not + appear in a shared library (as memory will be read only + and the dynamic linker can not resolve these. However + the error should not occur for e.g. debugging or + non-readonly sections. */ + if (bfd_link_dll (info) && !bfd_link_pie (info) + && (sec->flags & SEC_ALLOC) != 0 + && (sec->flags & SEC_READONLY) != 0) + { + const char *name; + if (h) + name = h->root.root.string; + else + /* bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL); */ + name = "UNKNOWN"; + (*_bfd_error_handler) + (_("%B: relocation %s against `%s' can not be used \ +when making a shared object; recompile with -fPIC"), + abfd, + arc_elf_howto (r_type)->name, + name); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + /* In some cases we are not setting the 'non_got_ref' + flag, even though the relocations don't require a GOT + access. We should extend the testing in this area to + ensure that no significant cases are being missed. */ + if (h) + h->non_got_ref = 1; + /* FALLTHROUGH */ + case R_ARC_PC32: + case R_ARC_32_PCREL: + if (bfd_link_pic (info) + && ((r_type != R_ARC_PC32 && r_type != R_ARC_32_PCREL) + || (h != NULL + && h->dynindx != -1 + && (!info->symbolic || !h->def_regular)))) + { + if (sreloc == NULL) + { + sreloc = _bfd_elf_make_dynamic_reloc_section (sec, dynobj, + 2, abfd, + /*rela*/ + TRUE); + + if (sreloc == NULL) + return FALSE; + } + sreloc->size += sizeof (Elf32_External_Rela); + + } + default: + break; + } + if (is_reloc_for_PLT (howto) == TRUE) { if (h == NULL) @@ -980,260 +1751,137 @@ elf_arc_check_relocs (bfd * abfd, if (h == NULL) { /* Local symbol. */ - local_got_offsets[r_symndx] = - ADD_SYMBOL_REF_SEC_AND_RELOC (got, bfd_link_pic (info), NULL); + if (local_got_ents[r_symndx] == NULL) + { + bfd_vma offset = + ADD_SYMBOL_REF_SEC_AND_RELOC (got, + bfd_link_pic (info), + NULL); + new_got_entry_to_list (&(local_got_ents[r_symndx]), + GOT_NORMAL, offset, NONE); + } } else { /* Global symbol. */ h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - h->got.offset = - ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h); + if (h->got.glist == NULL) + { + bfd_vma offset = + ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h); + new_got_entry_to_list (&h->got.glist, + GOT_NORMAL, offset, NONE); + } } } - } - - return TRUE; -} - -#define ELF_DYNAMIC_INTERPRETER "/sbin/ld-uClibc.so" - -/* Size of one plt entry in bytes. */ -#define PLT_ENTRY_SIZE 12 -#define PLT_ENTRY_SIZE_V2 16 - -/* Instructions appear in memory as a sequence of half-words (16 bit); - individual half-words are represented on the target in target byte order. - We use 'unsigned short' on the host to represent the PLT templates, - and translate to target byte order as we copy to the target. */ -typedef unsigned short insn_hword; - - -/* TODO: Make this PLT entry code be in a separate object file. */ -/* TODO: This is a linker BTW, we should be able to link. :) */ - -/* The zeroth entry in the absolute plt entry. */ -static const insn_hword elf_arc_abs_plt0_entry[2 * PLT_ENTRY_SIZE / 2] = -{ - 0x1600, /* ld %r11, [0] */ - 0x700b, - 0x0000, - 0x0000, - 0x1600, /* ld %r10, [0] */ - 0x700a, /* */ - 0, - 0, - 0x2020, /* j [%r10] */ - 0x0280, /* ---"---- */ - 0x0000, /* pad */ - 0x0000 /* pad */ -}; - -/* Contents of the subsequent entries in the absolute plt. */ -static const insn_hword elf_arc_abs_pltn_entry[PLT_ENTRY_SIZE / 2] = -{ - 0x2730, /* ld %r12, [%pc,func@gotpc] */ - 0x7f8c, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x7c20, /* j_s.d [%r12] */ - 0x74ef /* mov_s %r12, %pcl */ -}; - -/* The zeroth entry in the absolute plt entry for ARCv2. */ -static const insn_hword elf_arcV2_abs_plt0_entry[2 * PLT_ENTRY_SIZE_V2 / 2] = -{ - 0x1600, 0x700b, 0, 0, /* ld %r11, [0] */ - 0x1600, 0x700a, 0, 0, /* ld %r10, [0] */ - 0x2020, 0x0280, /* j [%r10] */ - 0x0000, 0x0000, /* -> RELOCATED TO ABS ADDRESS OF GOT <- */ - 0x0000, /* pad */ - 0x0000, /* pad */ - 0x0000, /* pad */ - 0x0000 /* pad */ -}; - -/* Contents of the subsequent entries in the absolute plt for ARCv2. */ -static const insn_hword elf_arcV2_abs_pltn_entry[PLT_ENTRY_SIZE_V2 / 2] = -{ - 0x2730, /* ld %r12, [%pcl,func@gotpc] */ - 0x7f8c, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x2021, /* j.d [%r12] */ - 0x0300, /* ------ " " -------------- */ - 0x240a, /* mov %r12, %pcl */ - 0x1fc0 /* ------ " " -------------- */ -}; - -/* The zeroth entry in the pic plt entry. */ -static const insn_hword elf_arc_pic_plt0_entry[2 * PLT_ENTRY_SIZE / 2] = -{ - 0x2730, /* ld %r11, [pcl,0] : 0 to be replaced by - _DYNAMIC@GOTPC+4 */ - 0x7f8b, - 0x0000, - 0x0000, - 0x2730, /* ld %r10, [pcl,0] : 0 to be replaced by - -DYNAMIC@GOTPC+8 */ - 0x7f8a, /* */ - 0, - 0, - 0x2020, /* j [%r10] */ - 0x0280, /* ---"---- */ - 0x0000, /* pad */ - 0x0000 /* pad */ -}; -/* Contents of the subsequent entries in the pic plt. */ -static const insn_hword elf_arc_pic_pltn_entry[PLT_ENTRY_SIZE / 2] = -{ - 0x2730, /* ld %r12, [%pc,func@got] */ - 0x7f8c, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x7c20, /* j_s.d [%r12] */ - 0x74ef, /* mov_s %r12, %pcl */ -}; + if (is_reloc_for_TLS (howto) == TRUE) + { + enum tls_type_e type = GOT_UNKNOWN; -/* The zeroth entry in the pic plt entry for ARCv2. */ -static const insn_hword elf_arcV2_pic_plt0_entry[2 * PLT_ENTRY_SIZE_V2 / 2] = -{ - 0x2730, /* ld %r11, [pcl,0] : 0 to be replaced by - _DYNAMIC@GOTPC+4 */ - 0x7f8b, - 0x0000, - 0x0000, - 0x2730, /* ld %r10, [pcl,0] : 0 to be replaced by - -DYNAMIC@GOTPC+8 */ - 0x7f8a, /* */ - 0, - 0, - 0x2020, /* j [%r10] */ - 0x0280, /* ---"---- */ - 0x0000, /* pad */ - 0x0000, /* pad */ - 0x0000, /* pad */ - 0x0000, /* pad */ - 0x0000, /* pad */ - 0x0000 /* pad */ -}; + switch (r_type) + { + case R_ARC_TLS_GD_GOT: + type = GOT_TLS_GD; + break; + case R_ARC_TLS_IE_GOT: + type = GOT_TLS_IE; + break; + default: + break; + } -#define elf_arcV2_pic_PLT0_ENTRY_SIZE (2 * PLT_ENTRY_SIZE_V2/2) + struct got_entry **list = NULL; + if (h != NULL) + list = &(h->got.glist); + else + list = &(local_got_ents[r_symndx]); -/* Contents of the subsequent entries in the pic plt for ARCv2. */ -static const insn_hword elf_arcV2_pic_pltn_entry[PLT_ENTRY_SIZE_V2 / 2] = -{ - 0x2730, /* ld %r12, [%pc,func@got] */ - 0x7f8c, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x0000, /* ------ " " -------------- */ - 0x2021, /* j.d [%r12] */ - 0x0300, /* ------ " " -------------- */ - 0x240a, /* mov %r12, %pcl */ - 0x1fc0 /* ------ " " -------------- */ -}; + if (type != GOT_UNKNOWN && !symbol_has_entry_of_type (*list, type)) + { + enum tls_got_entries entries = NONE; + bfd_vma offset = + ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h); -#define elf_arcV2_pic_PLTN_ENTRY_SIZE (PLT_ENTRY_SIZE_V2/2) + if (type == GOT_TLS_GD) + { + bfd_vma ATTRIBUTE_UNUSED notneeded = + ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h); + entries = MOD_AND_OFF; + } -enum plt_reloc_symbol -{ - LAST_RELOC = 0, + if (entries == NONE) + entries = OFF; - SGOT = 1, + new_got_entry_to_list (list, type, offset, entries); + } - RELATIVE = (1 << 8), - MIDDLE_ENDIAN = (1 << 9) -}; + } + } -#define IS_RELATIVE(S) ((S & RELATIVE) != 0) -#define IS_MIDDLE_ENDIAN(S) ((S & MIDDLE_ENDIAN) != 0) -#define SYM_ONLY(S) (S & 0xFF) + return TRUE; +} -struct plt_reloc -{ - bfd_vma offset; - bfd_vma size; - bfd_vma mask; - enum plt_reloc_symbol symbol; - bfd_vma addend; -}; +#define ELF_DYNAMIC_INTERPRETER "/sbin/ld-uClibc.so" -struct plt_version_t +static struct plt_version_t * +arc_get_plt_version (struct bfd_link_info *info) { - const insn_hword * entry; - const bfd_vma entry_size; - const insn_hword * elem; - const bfd_vma elem_size; - - struct plt_reloc entry_relocs[5]; - struct plt_reloc elem_relocs[5]; -}; - - -#define PLT_DATA(NAME, ...) \ - .entry = NAME##_plt0_entry, \ - .entry_size = NAME##_PLT0_ENTRY_SIZE, \ - .elem = NAME##_pltn_entry, \ - .elem_size = NAME##_PLTN_ENTRY_SIZE + int i; + for (i = 0; i < 1; i++) + { + ARC_DEBUG ("%d: size1 = %d, size2 = %d\n", i, + plt_versions[i].entry_size, + plt_versions[i].elem_size); + } -struct plt_version_t plt_versions[] = -{ - { - PLT_DATA (elf_arcV2_pic), - .entry_relocs = + if (bfd_get_mach (info->output_bfd) == bfd_mach_arc_arcv2) { - {4, 32, 0xFFFFFFFF, SGOT | RELATIVE | MIDDLE_ENDIAN, 4}, - {12, 32, 0xFFFFFFFF, SGOT | RELATIVE | MIDDLE_ENDIAN, 8}, - {20, 32, 0xFFFFFFFF, SGOT, 0}, - {0, 0, 0, LAST_RELOC, 0} - }, - .elem_relocs = + if (bfd_link_pic (info)) + return &(plt_versions[ELF_ARCV2_PIC]); + else + return &(plt_versions[ELF_ARCV2_ABS]); + } + else { - {4, 32, 0xFFFFFFFF, SGOT, 0}, - {0, 0, 0, LAST_RELOC, 0} + if (bfd_link_pic (info)) + return &(plt_versions[ELF_ARC_PIC]); + else + return &(plt_versions[ELF_ARC_ABS]); } - } -}; -#undef PLT_DATA - -static struct plt_version_t * -arc_get_plt_version (void) -{ - return &(plt_versions[0]); } static bfd_vma add_symbol_to_plt (struct bfd_link_info *info) { - bfd *dynobj = (elf_hash_table (info))->dynobj; - struct dynamic_sections ds = arc_create_dynamic_sections (dynobj, info); + struct elf_link_hash_table *htab = elf_hash_table (info); bfd_vma ret; - /* If this is the first .plt entry, make room for the special first entry. */ - if (ds.splt->size == 0) - ds.splt->size += 2 * - (bfd_get_mach (dynobj) == bfd_mach_arc_arcv2 - ? PLT_ENTRY_SIZE_V2 : PLT_ENTRY_SIZE); + struct plt_version_t *plt_data = arc_get_plt_version (info); + + /* If this is the first .plt entry, make room for the special first + entry. */ + if (htab->splt->size == 0) + htab->splt->size += plt_data->entry_size; + + ret = htab->splt->size; - ret = ds.splt->size; + htab->splt->size += plt_data->elem_size; + ARC_DEBUG ("PLT_SIZE = %d\n", htab->splt->size); - ds.splt->size += (bfd_get_mach (dynobj) == bfd_mach_arc_arcv2 - ? PLT_ENTRY_SIZE_V2 - : PLT_ENTRY_SIZE - ); - ds.sgotplt->size += 4; - ds.srelplt->size += sizeof (Elf32_External_Rela); + htab->sgotplt->size += 4; + htab->srelplt->size += sizeof (Elf32_External_Rela); return ret; } -#define PLT_DO_RELOCS_FOR_ENTRY(DS, RELOCS) \ - plt_do_relocs_for_symbol (DS, RELOCS, 0, 0) +#define PLT_DO_RELOCS_FOR_ENTRY(ABFD, DS, RELOCS) \ + plt_do_relocs_for_symbol (ABFD, DS, RELOCS, 0, 0) static void -plt_do_relocs_for_symbol (struct dynamic_sections *ds, - struct plt_reloc *reloc, +plt_do_relocs_for_symbol (bfd *abfd, + struct elf_link_hash_table *htab, + const struct plt_reloc *reloc, bfd_vma plt_offset, bfd_vma symbol_got_offset) { @@ -1245,17 +1893,26 @@ plt_do_relocs_for_symbol (struct dynamic_sections *ds, { case SGOT: relocation = - ds->sgotplt->output_section->vma + - ds->sgotplt->output_offset + symbol_got_offset; + htab->sgotplt->output_section->vma + + htab->sgotplt->output_offset + symbol_got_offset; break; } relocation += reloc->addend; - relocation -= (IS_RELATIVE (reloc->symbol)) - ? ds->splt->output_section->vma + ds->splt->output_offset + - plt_offset + reloc->offset : 0; + if (IS_RELATIVE (reloc->symbol)) + { + bfd_vma reloc_offset = reloc->offset; + reloc_offset -= (IS_INSN_32 (reloc->symbol)) ? 4 : 0; + reloc_offset -= (IS_INSN_24 (reloc->symbol)) ? 2 : 0; + + relocation -= htab->splt->output_section->vma + + htab->splt->output_offset + + plt_offset + reloc_offset; + } - if (IS_MIDDLE_ENDIAN (reloc->symbol)) + /* TODO: being ME is not a property of the relocation but of the + section of which is applying the relocation. */ + if (IS_MIDDLE_ENDIAN (reloc->symbol) || bfd_big_endian (abfd)) { relocation = ((relocation & 0xffff0000) >> 16) | @@ -1265,60 +1922,102 @@ plt_do_relocs_for_symbol (struct dynamic_sections *ds, switch (reloc->size) { case 32: - bfd_put_32 (ds->splt->output_section->owner, + bfd_put_32 (htab->splt->output_section->owner, relocation, - ds->splt->contents + plt_offset + reloc->offset); + htab->splt->contents + plt_offset + reloc->offset); break; } - reloc = &(reloc[1]); /* Jump to next relocation. */ + reloc = &(reloc[1]); /* Jump to next relocation. */ } } static void -relocate_plt_for_symbol (struct bfd_link_info *info, +relocate_plt_for_symbol (bfd *output_bfd, + struct bfd_link_info *info, struct elf_link_hash_entry *h) { - bfd * dynobj = elf_hash_table (info)->dynobj; - struct plt_version_t *plt_data = arc_get_plt_version (); - struct dynamic_sections ds = arc_create_dynamic_sections (dynobj, info); + struct plt_version_t *plt_data = arc_get_plt_version (info); + struct elf_link_hash_table *htab = elf_hash_table (info); - bfd_vma plt_index = h->plt.offset / plt_data->elem_size; + bfd_vma plt_index = (h->plt.offset - plt_data->entry_size) + / plt_data->elem_size; bfd_vma got_offset = (plt_index + 3) * 4; - memcpy (ds.splt->contents + h->plt.offset, plt_data->elem, - plt_data->elem_size * sizeof (insn_hword)); - plt_do_relocs_for_symbol (&ds, plt_data->elem_relocs, h->plt.offset, + ARC_DEBUG ("arc_info: PLT_OFFSET = 0x%x, PLT_ENTRY_VMA = 0x%x, \ +GOT_ENTRY_OFFSET = 0x%x, GOT_ENTRY_VMA = 0x%x, for symbol %s\n", + h->plt.offset, + htab->splt->output_section->vma + + htab->splt->output_offset + + h->plt.offset, + got_offset, + htab->sgotplt->output_section->vma + + htab->sgotplt->output_offset + + got_offset, + h->root.root.string); + + memcpy (htab->splt->contents + h->plt.offset, + plt_data->elem, + plt_data->elem_size); + plt_do_relocs_for_symbol (output_bfd, htab, + plt_data->elem_relocs, + h->plt.offset, got_offset); + + /* Fill in the entry in the global offset table. */ + bfd_put_32 (output_bfd, + (bfd_vma) (htab->splt->output_section->vma + + htab->splt->output_offset), + htab->sgotplt->contents + got_offset); + + /* TODO: Fill in the entry in the .rela.plt section. */ + { + Elf_Internal_Rela rel; + bfd_byte *loc; + + rel.r_offset = (htab->sgotplt->output_section->vma + + htab->sgotplt->output_offset + + got_offset); + rel.r_addend = 0; + rel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_JMP_SLOT); + + loc = htab->srelplt->contents; + loc += plt_index * sizeof (Elf32_External_Rela); /* relA */ + bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); + } } static void -relocate_plt_for_entry (struct bfd_link_info *info) +relocate_plt_for_entry (bfd *abfd, + struct bfd_link_info *info) { - bfd * dynobj = (elf_hash_table (info))->dynobj; - struct plt_version_t *plt_data = arc_get_plt_version (); - struct dynamic_sections ds = arc_create_dynamic_sections (dynobj, info); + struct plt_version_t *plt_data = arc_get_plt_version (info); + struct elf_link_hash_table *htab = elf_hash_table (info); - memcpy (ds.splt->contents, plt_data->entry, - plt_data->entry_size * sizeof (insn_hword)); - PLT_DO_RELOCS_FOR_ENTRY (&ds, plt_data->entry_relocs); + memcpy (htab->splt->contents, plt_data->entry, + plt_data->entry_size); + PLT_DO_RELOCS_FOR_ENTRY (abfd, htab, plt_data->entry_relocs); } -/* Desc : Adjust a symbol defined by a dynamic object and referenced by a - regular object. The current definition is in some section of the - dynamic object, but we're not including those sections. We have to - change the definition to something the rest of the link can +/* Desc : Adjust a symbol defined by a dynamic object and referenced + by a regular object. The current definition is in some section of + the dynamic object, but we're not including those sections. We + have to change the definition to something the rest of the link can understand. */ static bfd_boolean elf_arc_adjust_dynamic_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *h) { + asection *s; + unsigned int power_of_two; bfd *dynobj = (elf_hash_table (info))->dynobj; - struct dynamic_sections ds = arc_create_dynamic_sections (dynobj, info); + struct elf_link_hash_table *htab = elf_hash_table (info); - if (h->needs_plt == 1) + if (h->type == STT_FUNC + || h->type == STT_GNU_IFUNC + || h->needs_plt == 1) { if (!bfd_link_pic (info) && !h->def_dynamic && !h->ref_dynamic) { @@ -1336,71 +2035,230 @@ elf_arc_adjust_dynamic_symbol (struct bfd_link_info *info, && !bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; - if (bfd_link_pic (info) || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h)) + if (bfd_link_pic (info) + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h)) { bfd_vma loc = add_symbol_to_plt (info); if (!bfd_link_pic (info) && !h->def_regular) { - h->root.u.def.section = ds.splt; + h->root.u.def.section = htab->splt; h->root.u.def.value = loc; } h->plt.offset = loc; } + else + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + return TRUE; } - else + + /* 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) { - h->plt.offset = (bfd_vma) - 1; - h->needs_plt = 0; + 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; } - return TRUE; -} + /* If there are no non-GOT references, we do not need a copy + relocation. */ + if (!h->non_got_ref) + return TRUE; + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + + /* If we are creating a shared library, we must presume that the + only references to the symbol are via the global offset table. + For such cases we need not do anything here; the relocations will + be handled correctly by relocate_section. */ + if (bfd_link_pic (info)) + 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. */ + + s = bfd_get_section_by_name (dynobj, ".dynbss"); + BFD_ASSERT (s != NULL); + + /* We must generate a R_ARC_COPY reloc to tell the dynamic linker to + copy the initial value out of the dynamic object and into the + runtime process image. We need to remember the offset into the + .rela.bss section we are going to use. */ + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) + { + asection *srel; + + srel = bfd_get_section_by_name (dynobj, ".rela.bss"); + BFD_ASSERT (srel != NULL); + srel->size += sizeof (Elf32_External_Rela); + h->needs_copy = 1; + } + + /* We need to figure out the alignment required for this symbol. I + have no idea how ELF linkers handle this. */ + power_of_two = bfd_log2 (h->size); + if (power_of_two > 3) + power_of_two = 3; + + /* Apply the required alignment. */ + s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two)); + if (power_of_two > bfd_get_section_alignment (dynobj, s)) + { + if (! bfd_set_section_alignment (dynobj, s, power_of_two)) + return FALSE; + } + + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = s; + h->root.u.def.value = s->size; -#define ADD_RELA(BFD, SECTION, OFFSET, SYM_IDX, TYPE, ADDEND) \ -{\ - struct dynamic_sections ds = arc_create_dynamic_sections (output_bfd, info); \ - bfd_byte * rloc = ds.srel##SECTION->contents + \ - ((ds.srel##SECTION->reloc_count++) * sizeof (Elf32_External_Rela)); \ - Elf_Internal_Rela rel; \ - bfd_put_32 (output_bfd, (bfd_vma) 0, ds.s##SECTION->contents + OFFSET); \ - rel.r_addend = ADDEND; \ - rel.r_offset = (ds.s##SECTION)->output_section->vma + (ds.s##SECTION)->output_offset + OFFSET; \ - rel.r_info = ELF32_R_INFO (SYM_IDX, TYPE); \ - bfd_elf32_swap_reloca_out (BFD, &rel, rloc); \ + /* Increment the section size to make room for the symbol. */ + s->size += h->size; + + return TRUE; } /* Function : elf_arc_finish_dynamic_symbol Brief : Finish up dynamic symbol handling. We set the - contents of various dynamic sections here. + contents of various dynamic sections here. Args : output_bfd : - info : - h : - sym : + info : + h : + sym : Returns : True/False as the return status. */ + static bfd_boolean elf_arc_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info *info, struct elf_link_hash_entry *h, Elf_Internal_Sym * sym) { - if (h->plt.offset != (bfd_vma) - 1) - relocate_plt_for_symbol (info, h); - - if (h->got.offset != (bfd_vma) - 1) + if (h->plt.offset != (bfd_vma) -1) { - if (bfd_link_pic (info) && (info->symbolic || h->dynindx == -1) - && h->def_regular) + relocate_plt_for_symbol (output_bfd, info, h); + + if (!h->def_regular) { - ADD_RELA (output_bfd, got, h->got.offset, 0, R_ARC_RELATIVE, 0); + /* Mark the symbol as undefined, rather than as defined in + the .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; } - else + } + + if (h->got.glist != NULL) + { + struct got_entry *list = h->got.glist; + + /* Traverse the list of got entries for this symbol. */ + while (list) { - ADD_RELA (output_bfd, got, h->got.offset, h->dynindx, - R_ARC_GLOB_DAT, 0); + bfd_vma got_offset = h->got.glist->offset; + + if (list->type == GOT_NORMAL + && list->created_dyn_relocation == FALSE) + { + if (bfd_link_pic (info) + && (info->symbolic || h->dynindx == -1) + && h->def_regular) + { + ADD_RELA (output_bfd, got, got_offset, 0, R_ARC_RELATIVE, 0); + } + else + { + ADD_RELA (output_bfd, got, got_offset, h->dynindx, + R_ARC_GLOB_DAT, 0); + } + list->created_dyn_relocation = TRUE; + } + else if (list->existing_entries != NONE) + { + struct elf_link_hash_table *htab = elf_hash_table (info); + enum tls_got_entries e = list->existing_entries; + + BFD_ASSERT (list->type != GOT_TLS_GD + || list->existing_entries == MOD_AND_OFF); + + bfd_vma index = h->dynindx == -1 ? 0 : h->dynindx; + if (e == MOD_AND_OFF || e == MOD) + { + ADD_RELA (output_bfd, got, got_offset, index, + R_ARC_TLS_DTPMOD, 0); + ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \ +GOT_OFFSET = 0x%x, GOT_VMA = 0x%x, INDEX = %d, ADDEND = 0x%x\n", + list->type, + got_offset, + htab->sgot->output_section->vma + + htab->sgot->output_offset + got_offset, + index, 0); + } + if (e == MOD_AND_OFF || e == OFF) + { + bfd_vma addend = 0; + if (list->type == GOT_TLS_IE) + addend = bfd_get_32 (output_bfd, + htab->sgot->contents + got_offset); + + ADD_RELA (output_bfd, got, + got_offset + (e == MOD_AND_OFF ? 4 : 0), + index, + (list->type == GOT_TLS_IE ? + R_ARC_TLS_TPOFF : R_ARC_TLS_DTPOFF), + addend); + + ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \ +GOT_OFFSET = 0x%x, GOT_VMA = 0x%x, INDEX = %d, ADDEND = 0x%x\n", + list->type, + got_offset, + htab->sgot->output_section->vma + + htab->sgot->output_offset + got_offset, + index, addend); + } + } + + list = list->next; } + + h->got.glist = NULL; + } + if (h->needs_copy) + { + bfd_vma rel_offset = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + + asection *srelbss = + bfd_get_section_by_name (h->root.u.def.section->owner, + ".rela.bss"); + + bfd_vma loc = (bfd_vma) srelbss->contents + + (srelbss->reloc_count * sizeof (Elf32_External_Rela)); + srelbss->reloc_count++; + + Elf_Internal_Rela rel; + rel.r_addend = 0; + rel.r_offset = rel_offset; + rel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_COPY); + + bfd_elf32_swap_reloca_out (output_bfd, &rel, (bfd_byte *) loc); } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ if (strcmp (h->root.root.string, "_DYNAMIC") == 0 || strcmp (h->root.root.string, "__DYNAMIC") == 0 @@ -1410,31 +2268,35 @@ elf_arc_finish_dynamic_symbol (bfd * output_bfd, return TRUE; } -#define GET_SYMBOL_OR_SECTION(TAG, SYMBOL, SECTION) \ - case TAG: \ - if (SYMBOL != NULL) \ - { \ - h = elf_link_hash_lookup (elf_hash_table (info), SYMBOL, FALSE, FALSE, TRUE); \ - } \ - else if (SECTION != NULL) \ - { \ - s = bfd_get_section_by_name (output_bfd, SECTION); \ - BFD_ASSERT (s != NULL); \ - do_it = TRUE; \ - } \ - break; +#define GET_SYMBOL_OR_SECTION(TAG, SYMBOL, SECTION, ASSERT) \ + case TAG: \ + if (SYMBOL != NULL) \ + { \ + h = elf_link_hash_lookup (elf_hash_table (info), \ + SYMBOL, FALSE, FALSE, TRUE); \ + } \ + else if (SECTION != NULL) \ + { \ + s = bfd_get_section_by_name (output_bfd, SECTION); \ + BFD_ASSERT (s != NULL || !ASSERT); \ + do_it = TRUE; \ + } \ + break; /* Function : elf_arc_finish_dynamic_sections Brief : Finish up the dynamic sections handling. Args : output_bfd : - info : - h : - sym : + info : + h : + sym : Returns : True/False as the return status. */ + static bfd_boolean -elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) +elf_arc_finish_dynamic_sections (bfd * output_bfd, + struct bfd_link_info *info) { struct dynamic_sections ds = arc_create_dynamic_sections (output_bfd, info); + struct elf_link_hash_table *htab = elf_hash_table (info); bfd *dynobj = (elf_hash_table (info))->dynobj; if (ds.sdyn) @@ -1443,7 +2305,7 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) dyncon = (Elf32_External_Dyn *) ds.sdyn->contents; dynconend = - (Elf32_External_Dyn *) (ds.sdyn->contents + ds.sdyn->size); + (Elf32_External_Dyn *) (ds.sdyn->contents + ds.sdyn->size); for (; dyncon < dynconend; dyncon++) { Elf_Internal_Dyn internal_dyn; @@ -1456,12 +2318,15 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) switch (internal_dyn.d_tag) { - GET_SYMBOL_OR_SECTION (DT_INIT, "_init", NULL) - GET_SYMBOL_OR_SECTION (DT_FINI, "_fini", NULL) - GET_SYMBOL_OR_SECTION (DT_PLTGOT, NULL, ".plt") - GET_SYMBOL_OR_SECTION (DT_JMPREL, NULL, ".rela.plt") - GET_SYMBOL_OR_SECTION (DT_PLTRELSZ, NULL, ".rela.plt") - GET_SYMBOL_OR_SECTION (DT_RELASZ, NULL, ".rela.plt") + GET_SYMBOL_OR_SECTION (DT_INIT, "_init", NULL, TRUE) + GET_SYMBOL_OR_SECTION (DT_FINI, "_fini", NULL, TRUE) + GET_SYMBOL_OR_SECTION (DT_PLTGOT, NULL, ".plt", TRUE) + GET_SYMBOL_OR_SECTION (DT_JMPREL, NULL, ".rela.plt", TRUE) + GET_SYMBOL_OR_SECTION (DT_PLTRELSZ, NULL, ".rela.plt", TRUE) + GET_SYMBOL_OR_SECTION (DT_RELASZ, NULL, ".rela.plt", FALSE) + GET_SYMBOL_OR_SECTION (DT_VERSYM, NULL, ".gnu.version", TRUE) + GET_SYMBOL_OR_SECTION (DT_VERDEF, NULL, ".gnu.version_d", TRUE) + GET_SYMBOL_OR_SECTION (DT_VERNEED, NULL, ".gnu.version_r", TRUE) default: break; } @@ -1470,8 +2335,7 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) symbol. */ if (h != NULL && (h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - ) + || h->root.type == bfd_link_hash_defweak)) { asection *asec_ptr; @@ -1485,9 +2349,8 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) } else { - /* The symbol is imported from another - shared library and does not apply to this - one. */ + /* The symbol is imported from another shared + library and does not apply to this one. */ internal_dyn.d_un.d_val = 0; } do_it = TRUE; @@ -1498,6 +2361,9 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) { case DT_PLTGOT: case DT_JMPREL: + case DT_VERSYM: + case DT_VERDEF: + case DT_VERNEED: internal_dyn.d_un.d_ptr = s->vma; do_it = TRUE; break; @@ -1508,7 +2374,8 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) break; case DT_RELASZ: - internal_dyn.d_un.d_val -= s->size; + if (s != NULL) + internal_dyn.d_un.d_val -= s->size; do_it = TRUE; break; @@ -1521,60 +2388,54 @@ elf_arc_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) bfd_elf32_swap_dyn_out (output_bfd, &internal_dyn, dyncon); } - if (ds.splt->size > 0) + if (htab->splt->size > 0) { - relocate_plt_for_entry (info); + relocate_plt_for_entry (output_bfd, info); } - elf_section_data (ds.srelplt->output_section)->this_hdr.sh_entsize = 0xc; + /* TODO: Validate this. */ + elf_section_data (htab->srelplt->output_section)->this_hdr.sh_entsize + = 0xc; } /* Fill in the first three entries in the global offset table. */ - if (ds.sgot) + if (htab->sgot) { - if (ds.sgot->size > 0) + if (htab->sgot->size > 0 || htab->sgotplt->size > 0) { if (ds.sdyn == NULL) bfd_put_32 (output_bfd, (bfd_vma) 0, - ds.sgotplt->contents); + htab->sgotplt->contents); else bfd_put_32 (output_bfd, ds.sdyn->output_section->vma + ds.sdyn->output_offset, - ds.sgotplt->contents); - bfd_put_32 (output_bfd, (bfd_vma) 0, ds.sgotplt->contents + 4); - bfd_put_32 (output_bfd, (bfd_vma) 0, ds.sgotplt->contents + 8); - - elf_section_data (ds.sgot->output_section)->this_hdr.sh_entsize = 4; + htab->sgotplt->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0, htab->sgotplt->contents + 4); + bfd_put_32 (output_bfd, (bfd_vma) 0, htab->sgotplt->contents + 8); } } - if (ds.srelgot - /* Check that the linker script has not dumped the .srelgot section. */ - && ds.srelgot->output_section - && elf_section_data (ds.srelgot->output_section)) - { - /* TODO: Make it work even if I remove this. */ - elf_section_data (ds.srelgot->output_section)->this_hdr.sh_entsize = 0xc; - } - return TRUE; } -#define ADD_DYNAMIC_SYMBOL(NAME, TAG) \ - h = elf_link_hash_lookup (elf_hash_table (info), NAME, FALSE, FALSE, FALSE); \ - if ((h != NULL && (h->ref_regular || h->def_regular))) \ - if (! _bfd_elf_add_dynamic_entry (info, TAG, 0)) \ +#define ADD_DYNAMIC_SYMBOL(NAME, TAG) \ + h = elf_link_hash_lookup (elf_hash_table (info), \ + NAME, FALSE, FALSE, FALSE); \ + if ((h != NULL && (h->ref_regular || h->def_regular))) \ + if (! _bfd_elf_add_dynamic_entry (info, TAG, 0)) \ return FALSE; /* Set the sizes of the dynamic sections. */ static bfd_boolean -elf_arc_size_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) +elf_arc_size_dynamic_sections (bfd * output_bfd, + struct bfd_link_info *info) { - bfd * dynobj; + bfd * dynobj; asection * s; - bfd_boolean relocs_exist; - bfd_boolean reltext_exist; + bfd_boolean relocs_exist = FALSE; + bfd_boolean reltext_exist = FALSE; struct dynamic_sections ds = arc_create_dynamic_sections (output_bfd, info); + struct elf_link_hash_table *htab = elf_hash_table (info); dynobj = (elf_hash_table (info))->dynobj; BFD_ASSERT (dynobj != NULL); @@ -1583,75 +2444,92 @@ elf_arc_size_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) { struct elf_link_hash_entry *h; - /* Set the contents of the .interp section to the interpreter. */ + /* Set the contents of the .interp section to the + interpreter. */ if (!bfd_link_pic (info)) { s = bfd_get_section_by_name (dynobj, ".interp"); BFD_ASSERT (s != NULL); - s->size = sizeof ELF_DYNAMIC_INTERPRETER; + s->size = sizeof (ELF_DYNAMIC_INTERPRETER); s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; } - /* Add some entries to the .dynamic section. We fill in some of the - values later, in elf_bfd_final_link, but we must add the entries - now so that we know the final size of the .dynamic section. - Checking if the .init section is present. We also create DT_INIT - and DT_FINI entries if the init_str has been changed by the user. */ + /* Add some entries to the .dynamic section. We fill in some of + the values later, in elf_bfd_final_link, but we must add the + entries now so that we know the final size of the .dynamic + section. Checking if the .init section is present. We also + create DT_INIT and DT_FINI entries if the init_str has been + changed by the user. */ ADD_DYNAMIC_SYMBOL ("init", DT_INIT); ADD_DYNAMIC_SYMBOL ("fini", DT_FINI); } else { - /* We may have created entries in the .rela.got section. However, if - we are not creating the dynamic sections, we will not actually - use these entries. Reset the size of .rela.got, which will cause - it to get stripped from the output file below. */ - ds.srelgot->size = 0; + /* We may have created entries in the .rela.got section. + However, if we are not creating the dynamic sections, we will + not actually use these entries. Reset the size of .rela.got, + which will cause it to get stripped from the output file + below. */ + if (htab->srelgot != NULL) + htab->srelgot->size = 0; } + if (htab->splt != NULL && htab->splt->size == 0) + htab->splt->flags |= SEC_EXCLUDE; for (s = dynobj->sections; s != NULL; s = s->next) { - bfd_boolean is_dynamic_section = FALSE; - - /* Skip any non dynamic section. */ - if (strstr (s->name, ".plt") != NULL - || strstr (s->name, ".got") != NULL - || strstr (s->name, ".rel") != NULL) - is_dynamic_section = TRUE; - - /* Allocate memory for the section contents. */ - if (!is_dynamic_section) + if ((s->flags & SEC_LINKER_CREATED) == 0) continue; - s->contents = (bfd_byte *) bfd_alloc (dynobj, s->size); - if (s->contents == NULL && s->size != 0) - return FALSE; - - if (s->size == 0) + if (strncmp (s->name, ".rela", 5) == 0) { - s->flags |= SEC_EXCLUDE; - continue; - } + if (s->size == 0) + { + s->flags |= SEC_EXCLUDE; + } + else + { + if (strcmp (s->name, ".rela.plt") != 0) + { + const char *outname = + bfd_get_section_name (output_bfd, + htab->srelplt->output_section); + + asection *target = bfd_get_section_by_name (output_bfd, + outname + 4); + + relocs_exist = TRUE; + if (target != NULL && target->size != 0 + && (target->flags & SEC_READONLY) != 0 + && (target->flags & SEC_ALLOC) != 0) + reltext_exist = TRUE; + } + } - if (strcmp (s->name, ".rela.plt") != 0) - { - const char *outname = bfd_get_section_name (output_bfd, - s->output_section); - asection *target = bfd_get_section_by_name (output_bfd, - outname + 4); - - relocs_exist = TRUE; - if (target != NULL && target->size != 0 - && (target->flags & SEC_READONLY) != 0 - && (target->flags & SEC_ALLOC) != 0) - reltext_exist = TRUE; + /* We use the reloc_count field as a counter if we need to + copy relocs into the output file. */ + s->reloc_count = 0; } + + if (strcmp (s->name, ".dynamic") == 0) + continue; + + if (s->size != 0) + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); + + if (s->contents == NULL && s->size != 0) + return FALSE; } if (ds.sdyn) { - if (ds.splt && ds.splt->size != 0) + /* TODO: Check if this is needed. */ + if (!bfd_link_pic (info)) + if (!_bfd_elf_add_dynamic_entry (info, DT_DEBUG, 0)) + return FALSE; + + if (htab->splt && (htab->splt->flags & SEC_EXCLUDE) == 0) if (!_bfd_elf_add_dynamic_entry (info, DT_PLTGOT, 0) || !_bfd_elf_add_dynamic_entry (info, DT_PLTRELSZ, 0) || !_bfd_elf_add_dynamic_entry (info, DT_PLTREL, DT_RELA) @@ -1675,6 +2553,63 @@ elf_arc_size_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) return TRUE; } +const struct elf_size_info arc_elf32_size_info = +{ + sizeof (Elf32_External_Ehdr), + sizeof (Elf32_External_Phdr), + sizeof (Elf32_External_Shdr), + sizeof (Elf32_External_Rel), + sizeof (Elf32_External_Rela), + sizeof (Elf32_External_Sym), + sizeof (Elf32_External_Dyn), + sizeof (Elf_External_Note), + 4, + 1, + 32, 2, + ELFCLASS32, EV_CURRENT, + bfd_elf32_write_out_phdrs, + bfd_elf32_write_shdrs_and_ehdr, + bfd_elf32_checksum_contents, + bfd_elf32_write_relocs, + bfd_elf32_swap_symbol_in, + bfd_elf32_swap_symbol_out, + bfd_elf32_slurp_reloc_table, + bfd_elf32_slurp_symbol_table, + bfd_elf32_swap_dyn_in, + bfd_elf32_swap_dyn_out, + bfd_elf32_swap_reloc_in, + bfd_elf32_swap_reloc_out, + bfd_elf32_swap_reloca_in, + bfd_elf32_swap_reloca_out +}; + +#define elf_backend_size_info arc_elf32_size_info + +static struct bfd_link_hash_table * +arc_elf_link_hash_table_create (bfd *abfd) +{ + struct elf_link_hash_table *htab; + + htab = bfd_zmalloc (sizeof (*htab)); + if (htab == NULL) + return NULL; + + if (!_bfd_elf_link_hash_table_init (htab, abfd, + _bfd_elf_link_hash_newfunc, + sizeof (struct elf_link_hash_entry), + GENERIC_ELF_DATA)) + { + free (htab); + return NULL; + } + + htab->init_got_refcount.refcount = 0; + htab->init_got_refcount.glist = NULL; + htab->init_got_offset.offset = 0; + htab->init_got_offset.glist = NULL; + return (struct bfd_link_hash_table *) htab; +} + #define TARGET_LITTLE_SYM arc_elf32_le_vec #define TARGET_LITTLE_NAME "elf32-littlearc" @@ -1685,6 +2620,14 @@ elf_arc_size_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) #define ELF_MACHINE_ALT1 EM_ARC_COMPACT2 #define ELF_MAXPAGESIZE 0x2000 +#define bfd_elf32_bfd_link_hash_table_create arc_elf_link_hash_table_create + +#define bfd_elf32_bfd_merge_private_bfd_data arc_elf_merge_private_bfd_data +#define bfd_elf32_bfd_reloc_type_lookup arc_elf32_bfd_reloc_type_lookup +#define bfd_elf32_bfd_set_private_flags arc_elf_set_private_flags +#define bfd_elf32_bfd_print_private_bfd_data arc_elf_print_private_bfd_data +#define bfd_elf32_bfd_copy_private_bfd_data arc_elf_copy_private_bfd_data + #define elf_info_to_howto_rel arc_info_to_howto_rel #define elf_backend_object_p arc_elf_object_p #define elf_backend_final_write_processing arc_elf_final_write_processing @@ -1702,6 +2645,7 @@ elf_arc_size_dynamic_sections (bfd * output_bfd, struct bfd_link_info *info) #define elf_backend_can_gc_sections 1 #define elf_backend_want_got_plt 1 #define elf_backend_plt_readonly 1 +#define elf_backend_rela_plts_and_copies_p 1 #define elf_backend_want_plt_sym 0 #define elf_backend_got_header_size 12 diff --git a/include/elf/arc-reloc.def b/include/elf/arc-reloc.def index 893291f..96123de 100644 --- a/include/elf/arc-reloc.def +++ b/include/elf/arc-reloc.def @@ -31,7 +31,7 @@ ARC_RELOC_HOWTO(ARC_32, 4, \ 32, \ replace_word32, \ bitfield, \ - ( S + A )) + (NON_ME ( S + A ) )) ARC_RELOC_HOWTO(ARC_N8, 8, \ 0, \ @@ -360,7 +360,7 @@ ARC_RELOC_HOWTO(ARC_GOTPC, 58, \ 32, \ replace_word32, \ signed, \ - ( ( GOT + A ) - P )) + ( GOT_BEGIN - P )) ARC_RELOC_HOWTO(ARC_S21W_PCREL_PLT, 60, \ 2, \ @@ -395,12 +395,12 @@ ARC_RELOC_HOWTO(ARC_TLS_GD_GOT, 69, \ 32, \ replace_word32, \ dont, \ - 0) + ( G + GOT - P)) ARC_RELOC_HOWTO(ARC_TLS_GD_LD, 70, \ 2, \ - 32, \ - replace_word32, \ + 0, \ + replace_none, \ dont, \ 0) @@ -416,35 +416,35 @@ ARC_RELOC_HOWTO(ARC_TLS_IE_GOT, 72, \ 32, \ replace_word32, \ dont, \ - 0) + ( G + GOT - P)) ARC_RELOC_HOWTO(ARC_TLS_DTPOFF, 67, \ 2, \ 32, \ replace_word32, \ dont, \ - 0) + ( S - TLS_REL )) ARC_RELOC_HOWTO(ARC_TLS_DTPOFF_S9, 73, \ 2, \ 32, \ replace_word32, \ dont, \ - 0) + ( S - TLS_REL )) ARC_RELOC_HOWTO(ARC_TLS_LE_S9, 74, \ 2, \ 32, \ replace_word32, \ dont, \ - 0) + ( ( S + TCB_SIZE ) - TLS_REL )) ARC_RELOC_HOWTO(ARC_TLS_LE_32, 75, \ 2, \ 32, \ replace_word32, \ dont, \ - 0) + ( ( S + A + TCB_SIZE ) - TLS_REL )) ARC_RELOC_HOWTO(ARC_S25W_PCREL_PLT, 76, \ 2, \ diff --git a/include/opcode/arc-func.h b/include/opcode/arc-func.h index 9f0ea32..96ffcff 100644 --- a/include/opcode/arc-func.h +++ b/include/opcode/arc-func.h @@ -25,6 +25,7 @@ ATTRIBUTE_UNUSED static unsigned replace_none (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x00; return insn; } @@ -37,6 +38,7 @@ replace_none (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_bits8 (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xff; insn |= ((value >> 0) & 0x00ff) << 0; return insn; @@ -50,6 +52,7 @@ replace_bits8 (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_bits16 (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xffff; insn |= ((value >> 0) & 0xffff) << 0; return insn; @@ -63,6 +66,7 @@ replace_bits16 (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_bits24 (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xffffff; insn |= ((value >> 0) & 0xffffff) << 0; return insn; @@ -76,6 +80,7 @@ replace_bits24 (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_word32 (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xffffffff; insn |= ((value >> 0) & 0xffffffff) << 0; return insn; @@ -89,6 +94,7 @@ replace_word32 (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_limm (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xffffffff; insn |= ((value >> 0) & 0xffffffff) << 0; return insn; @@ -102,6 +108,7 @@ replace_limm (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_limms (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xffffffff; insn |= ((value >> 0) & 0xffffffff) << 0; return insn; @@ -115,6 +122,7 @@ replace_limms (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp21h (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x7feffc0; insn |= ((value >> 0) & 0x03ff) << 17; insn |= ((value >> 10) & 0x03ff) << 6; @@ -129,6 +137,7 @@ replace_disp21h (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp21w (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x7fcffc0; insn |= ((value >> 0) & 0x01ff) << 18; insn |= ((value >> 9) & 0x03ff) << 6; @@ -143,6 +152,7 @@ replace_disp21w (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp25h (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x7feffcf; insn |= ((value >> 0) & 0x03ff) << 17; insn |= ((value >> 10) & 0x03ff) << 6; insn |= ((value >> 20) & 0x000f) << 0; @@ -158,6 +168,7 @@ replace_disp25h (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp25w (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x7fcffcf; insn |= ((value >> 0) & 0x01ff) << 18; insn |= ((value >> 9) & 0x03ff) << 6; insn |= ((value >> 19) & 0x000f) << 0; @@ -173,6 +184,7 @@ replace_disp25w (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp9 (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x1ff; insn |= ((value >> 0) & 0x01ff) << 0; return insn; @@ -186,6 +198,7 @@ replace_disp9 (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp9ls (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0xff8000; insn |= ((value >> 0) & 0x00ff) << 16; insn |= ((value >> 8) & 0x0001) << 15; @@ -200,6 +213,7 @@ replace_disp9ls (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp9s (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x1ff; insn |= ((value >> 0) & 0x01ff) << 0; return insn; @@ -213,6 +227,7 @@ replace_disp9s (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp13s (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x7ff; insn |= ((value >> 0) & 0x07ff) << 0; return insn; @@ -226,6 +241,7 @@ replace_disp13s (unsigned insn, int value ATTRIBUTE_UNUSED) ATTRIBUTE_UNUSED static unsigned replace_disp9s1 (unsigned insn, int value ATTRIBUTE_UNUSED) { + insn = insn & ~0x7e7; insn |= ((value >> 0) & 0x0007) << 0; insn |= ((value >> 3) & 0x003f) << 5;