diff options
Diffstat (limited to 'bfd/elf32-arc.c')
-rw-r--r-- | bfd/elf32-arc.c | 1734 |
1 files changed, 1587 insertions, 147 deletions
diff --git a/bfd/elf32-arc.c b/bfd/elf32-arc.c index 07af56b..68a47ca 100644 --- a/bfd/elf32-arc.c +++ b/bfd/elf32-arc.c @@ -1,6 +1,6 @@ /* ARC-specific support for 32-bit ELF Copyright (C) 1994-2015 Free Software Foundation, Inc. - Contributed by Doug Evans (dje@cygnus.com). + Contributed by Cupertino Miranda (cmiranda@synopsys.com). This file is part of BFD, the Binary File Descriptor library. @@ -25,189 +25,348 @@ #include "elf-bfd.h" #include "elf/arc.h" #include "libiberty.h" +#include "opcode/arc-func.h" + +#define ARC_DEBUG(...) +#define DEBUG(...) printf (__ARGV__) +#define DEBUG_ARC_RELOC(A) + +struct arc_local_data +{ + bfd_vma sdata_begin_symbol_vma; + asection * sdata_output_section; + bfd_vma got_symbol_vma; +}; + +struct arc_local_data global_arc_data = +{ + .sdata_begin_symbol_vma = 0, + .sdata_output_section = NULL, + .got_symbol_vma = 0, +}; + +struct dynamic_sections +{ + bfd_boolean initialized; + asection * sgot; + asection * srelgot; + asection * sgotplt; + asection * sdyn; + asection * splt; + asection * srelplt; +}; + +static struct dynamic_sections +arc_create_dynamic_sections (bfd * abfd, struct bfd_link_info *info); + +enum dyn_section_types +{ + got = 0, + relgot, + gotplt, + dyn, + plt, + relplt, + DYN_SECTION_TYPES_END +}; + +const char * dyn_section_names[DYN_SECTION_TYPES_END] = +{ + ".got", + ".rela.got", + ".got.plt", + ".dynamic", + ".plt", + ".rela.plt" +}; + +/* 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. */ +#define INIT_SYM_STRING "_init" +#define FINI_SYM_STRING "_fini" + +char * init_str = INIT_SYM_STRING; +char * fini_str = FINI_SYM_STRING; + + +#define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + case VALUE: \ + return #TYPE; \ + break; + +static ATTRIBUTE_UNUSED const char * +reloc_type_to_name (unsigned int type) +{ + switch (type) + { + #include "elf/arc-reloc.def" + + default: + return "UNKNOWN"; + break; + } +} +#undef ARC_RELOC_HOWTO /* Try to minimize the amount of space occupied by relocation tables on the ROM (not that the ROM won't be swamped by other ELF overhead). */ -#define USE_REL 1 +#define USE_REL 1 + +static ATTRIBUTE_UNUSED bfd_boolean +is_reloc_PC_relative (reloc_howto_type *howto) +{ + return (strstr (howto->name, "PC") != NULL) ? TRUE : FALSE; +} + +static bfd_boolean +is_reloc_SDA_relative (reloc_howto_type *howto) +{ + return (strstr (howto->name, "SDA") != NULL) ? TRUE : FALSE; +} + +static bfd_boolean +is_reloc_for_GOT (reloc_howto_type * howto) +{ + return (strstr (howto->name, "GOT") != NULL) ? TRUE : FALSE; +} + +static bfd_boolean +is_reloc_for_PLT (reloc_howto_type * howto) +{ + return (strstr (howto->name, "PLT") != 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) +#define arc_bfd_put_16(A,B,C,D) bfd_put_16(A,B,C) + +static long +arc_bfd_get_32 (bfd * abfd, void *loc, asection * input_section) +{ + long insn = bfd_get_32 (abfd, loc); + + if (!bfd_big_endian (abfd) + && input_section + && (input_section->flags & SEC_CODE)) + insn = ((0x0000fffff & insn) << 16) | ((0xffff0000 & insn) >> 16); + + return insn; +} + +static void +arc_bfd_put_32 (bfd * abfd, long insn, void *loc, asection * input_section) +{ + if (!bfd_big_endian (abfd) + && input_section + && (input_section->flags & SEC_CODE)) + insn = ((0x0000fffff & insn) << 16) | ((0xffff0000 & insn) >> 16); + + bfd_put_32 (abfd, insn, loc); +} static bfd_reloc_status_type -arc_elf_b22_pcrel (bfd * abfd, - arelent * reloc_entry, - asymbol * symbol, - void * data, - asection * input_section, - bfd * output_bfd, - char ** error_message) -{ - /* If linking, back up the final symbol address by the address of the - reloc. This cannot be accomplished by setting the pcrel_offset - field to TRUE, as bfd_install_relocation will detect this and refuse - to install the offset in the first place, but bfd_perform_relocation - will still insist on removing it. */ - if (output_bfd == NULL) - reloc_entry->addend -= reloc_entry->address; - - /* Fall through to the default elf reloc handler. */ - return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, - input_section, output_bfd, error_message); +arc_elf_reloc (bfd *abfd ATTRIBUTE_UNUSED, + arelent *reloc_entry, + asymbol *symbol_in, + void *data ATTRIBUTE_UNUSED, + asection *input_section, + bfd *output_bfd, + char ** error_message ATTRIBUTE_UNUSED) +{ + if (output_bfd != NULL) + { + reloc_entry->address += input_section->output_offset; + + /* In case of relocateable link and if the reloc is against a + section symbol, the addend needs to be adjusted according to + where the section symbol winds up in the output section. */ + if ((symbol_in->flags & BSF_SECTION_SYM) && symbol_in->section) + reloc_entry->addend += symbol_in->section->output_offset; + + return bfd_reloc_ok; + } + + return bfd_reloc_continue; +} + + +#define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + TYPE = VALUE, +enum howto_list +{ +#include "elf/arc-reloc.def" + HOWTO_LIST_LAST +}; +#undef ARC_RELOC_HOWTO + +#define ARC_RELOC_HOWTO(TYPE, VALUE, RSIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + [TYPE] = HOWTO (R_##TYPE, 0, RSIZE, BITSIZE, FALSE, 0, complain_overflow_##OVERFLOW, arc_elf_reloc, #TYPE, FALSE, 0, 0, FALSE), + +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. + HOWTO (R_ARC_NONE, // Type. + 0, // Rightshift. + 2, // Size (0 = byte, 1 = short, 2 = long). + 32, // Bitsize. + FALSE, // PC_relative. + 0, // Bitpos. + complain_overflow_bitfield, // Complain_on_overflow. + bfd_elf_generic_reloc, // Special_function. + "R_ARC_NONE", // Name. + TRUE, // Partial_inplace. + 0, // Src_mask. + 0, // Dst_mask. + FALSE), // PCrel_offset. +*/ +}; +#undef ARC_RELOC_HOWTO + +static void arc_elf_howto_init (void) +{ +#define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + elf_arc_howto_table[TYPE].pc_relative = (strstr (#FORMULA, " P ") != NULL); + + #include "elf/arc-reloc.def" } +#undef ARC_RELOC_HOWTO -static reloc_howto_type elf_arc_howto_table[] = -{ - /* This reloc does nothing. */ - HOWTO (R_ARC_NONE, /* Type. */ - 0, /* Rightshift. */ - 3, /* Size (0 = byte, 1 = short, 2 = long). */ - 0, /* Bitsize. */ - FALSE, /* PC_relative. */ - 0, /* Bitpos. */ - complain_overflow_bitfield, /* Complain_on_overflow. */ - bfd_elf_generic_reloc, /* Special_function. */ - "R_ARC_NONE", /* Name. */ - TRUE, /* Partial_inplace. */ - 0, /* Src_mask. */ - 0, /* Dst_mask. */ - FALSE), /* PCrel_offset. */ - - /* A standard 32 bit relocation. */ - HOWTO (R_ARC_32, /* Type. */ - 0, /* Rightshift. */ - 2, /* Size (0 = byte, 1 = short, 2 = long). */ - 32, /* Bitsize. */ - FALSE, /* PC_relative. */ - 0, /* Bitpos. */ - complain_overflow_bitfield, /* Complain_on_overflow. */ - bfd_elf_generic_reloc, /* Special_function. */ - "R_ARC_32", /* Name. */ - TRUE, /* Partial_inplace. */ - 0xffffffff, /* Src_mask. */ - 0xffffffff, /* Dst_mask. */ - FALSE), /* PCrel_offset. */ - - /* A 26 bit absolute branch, right shifted by 2. */ - HOWTO (R_ARC_B26, /* Type. */ - 2, /* Rightshift. */ - 2, /* Size (0 = byte, 1 = short, 2 = long). */ - 26, /* Bitsize. */ - FALSE, /* PC_relative. */ - 0, /* Bitpos. */ - complain_overflow_bitfield, /* Complain_on_overflow. */ - bfd_elf_generic_reloc, /* Special_function. */ - "R_ARC_B26", /* Name. */ - TRUE, /* Partial_inplace. */ - 0x00ffffff, /* Src_mask. */ - 0x00ffffff, /* Dst_mask. */ - FALSE), /* PCrel_offset. */ - - /* A relative 22 bit branch; bits 21-2 are stored in bits 26-7. */ - HOWTO (R_ARC_B22_PCREL, /* Type. */ - 2, /* Rightshift. */ - 2, /* Size (0 = byte, 1 = short, 2 = long). */ - 22, /* Bitsize. */ - TRUE, /* PC_relative. */ - 7, /* Bitpos. */ - complain_overflow_signed, /* Complain_on_overflow. */ - arc_elf_b22_pcrel, /* Special_function. */ - "R_ARC_B22_PCREL", /* Name. */ - TRUE, /* Partial_inplace. */ - 0x07ffff80, /* Src_mask. */ - 0x07ffff80, /* Dst_mask. */ - FALSE), /* PCrel_offset. */ + +#define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + [TYPE] = VALUE, +const int howto_table_lookup[] = +{ + #include "elf/arc-reloc.def" }; +#undef ARC_RELOC_HOWTO + +#define ARC_ELF_HOWTO(r_type) \ + (&elf_arc_howto_table[r_type]) /* Map BFD reloc types to ARC ELF reloc types. */ struct arc_reloc_map { bfd_reloc_code_real_type bfd_reloc_val; - unsigned char elf_reloc_val; + unsigned char elf_reloc_val; }; +#define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + { BFD_RELOC_##TYPE, R_##TYPE }, static const struct arc_reloc_map arc_reloc_map[] = { - { BFD_RELOC_NONE, R_ARC_NONE, }, - { BFD_RELOC_32, R_ARC_32 }, - { BFD_RELOC_CTOR, R_ARC_32 }, - { BFD_RELOC_ARC_B26, R_ARC_B26 }, - { BFD_RELOC_ARC_B22_PCREL, R_ARC_B22_PCREL }, + #include "elf/arc-reloc.def" + {BFD_RELOC_NONE, R_ARC_NONE}, + {BFD_RELOC_8, R_ARC_8}, + {BFD_RELOC_16, R_ARC_16}, + {BFD_RELOC_24, R_ARC_24}, + {BFD_RELOC_32, R_ARC_32}, }; +#undef ARC_RELOC_HOWTO static reloc_howto_type * -bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, +bfd_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) { unsigned int i; + static int fully_initialized = FALSE; + + if (fully_initialized == FALSE) + { + arc_elf_howto_init (); + fully_initialized = TRUE; /* TODO: CHECK THIS IF IT STOPS WORKING. */ + } for (i = ARRAY_SIZE (arc_reloc_map); i--;) - if (arc_reloc_map[i].bfd_reloc_val == code) - return elf_arc_howto_table + arc_reloc_map[i].elf_reloc_val; + { + if (arc_reloc_map[i].bfd_reloc_val == code) + return elf_arc_howto_table + arc_reloc_map[i].elf_reloc_val; + } return NULL; } 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; - for (i = 0; - i < sizeof (elf_arc_howto_table) / sizeof (elf_arc_howto_table[0]); - i++) + for (i = 0; i < ARRAY_SIZE (elf_arc_howto_table); i++) if (elf_arc_howto_table[i].name != NULL && strcasecmp (elf_arc_howto_table[i].name, r_name) == 0) - return &elf_arc_howto_table[i]; + return elf_arc_howto_table + i; return NULL; } -/* Set the howto pointer for an ARC ELF reloc. */ +/* Set the howto pointer for an ARC ELF reloc. */ static void -arc_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED, - arelent *cache_ptr, - Elf_Internal_Rela *dst) +arc_info_to_howto_rel (bfd * abfd ATTRIBUTE_UNUSED, + arelent * cache_ptr, + Elf_Internal_Rela * dst) { unsigned int r_type; r_type = ELF32_R_TYPE (dst->r_info); - if (r_type >= (unsigned int) R_ARC_max) - { - _bfd_error_handler (_("%B: invalid ARC reloc number: %d"), abfd, r_type); - r_type = 0; - } + BFD_ASSERT (r_type < (unsigned int) R_ARC_max); cache_ptr->howto = &elf_arc_howto_table[r_type]; } /* Set the right machine number for an ARC ELF file. */ - static bfd_boolean -arc_elf_object_p (bfd *abfd) +arc_elf_object_p (bfd * abfd) { - unsigned int mach = bfd_mach_arc_6; + /* Make sure this is initialised, or you'll have the potential of passing + garbage---or misleading values---into the call to + bfd_default_set_arch_mach (). */ + int mach = bfd_mach_arc_arc700; + unsigned long arch = elf_elfheader (abfd)->e_flags & EF_ARC_MACH_MSK; + unsigned e_machine = elf_elfheader (abfd)->e_machine; - if (elf_elfheader(abfd)->e_machine == EM_ARC) + if (e_machine == EM_ARC_COMPACT || e_machine == EM_ARC_COMPACT2) { - unsigned long arch = elf_elfheader (abfd)->e_flags & EF_ARC_MACH; - switch (arch) { - case E_ARC_MACH_ARC5: - mach = bfd_mach_arc_5; - break; - default: - case E_ARC_MACH_ARC6: - mach = bfd_mach_arc_6; - break; - case E_ARC_MACH_ARC7: - mach = bfd_mach_arc_7; - break; - case E_ARC_MACH_ARC8: - mach = bfd_mach_arc_8; - break; + case E_ARC_MACH_ARC600: + mach = bfd_mach_arc_arc600; + break; + case E_ARC_MACH_ARC601: + mach = bfd_mach_arc_arc601; + break; + case E_ARC_MACH_ARC700: + mach = bfd_mach_arc_arc700; + break; + case EF_ARC_CPU_ARCV2HS: + case EF_ARC_CPU_ARCV2EM: + mach = bfd_mach_arc_arcv2; + break; + default: + mach = (e_machine == EM_ARC_COMPACT) ? + bfd_mach_arc_arc700 : bfd_mach_arc_arcv2; + break; + } + } + else + { + if (e_machine == EM_ARC) + { + (*_bfd_error_handler) + (_("Error: The ARC4 architecture is no longer supported.\n")); + return FALSE; + } + else + { + (*_bfd_error_handler) + (_("Warning: unset or old architecture flags. \n" + " Use default machine.\n")); } } + return bfd_default_set_arch_mach (abfd, bfd_arch_arc, mach); } @@ -215,42 +374,1323 @@ 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; switch (bfd_get_mach (abfd)) { - case bfd_mach_arc_5: - val = E_ARC_MACH_ARC5; + case bfd_mach_arc_arc600: + val = E_ARC_MACH_ARC600; + emf = EM_ARC_COMPACT; break; - default: - case bfd_mach_arc_6: - val = E_ARC_MACH_ARC6; + case bfd_mach_arc_arc601: + val = E_ARC_MACH_ARC601; + emf = EM_ARC_COMPACT; break; - case bfd_mach_arc_7: - val = E_ARC_MACH_ARC7; + case bfd_mach_arc_arc700: + val = E_ARC_MACH_ARC700; + emf = EM_ARC_COMPACT; break; - case bfd_mach_arc_8: - val = E_ARC_MACH_ARC8; + case bfd_mach_arc_arcv2: + val = EF_ARC_CPU_GENERIC; + emf = EM_ARC_COMPACT2; + /* TODO: Check validity of this. It can also be ARCV2EM here. + Previous version sets the e_machine here. */ break; + default: + abort (); } - elf_elfheader (abfd)->e_flags &=~ EF_ARC_MACH; + 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. */ + elf_elfheader (abfd)->e_flags |= E_ARC_OSABI_CURRENT; } +#define BFD_DEBUG_PIC(...) + +struct arc_relocation_data +{ + bfd_vma reloc_offset; + bfd_vma reloc_addend; + bfd_vma got_offset_value; + + bfd_vma sym_value; + asection * sym_section; + + reloc_howto_type *howto; + + asection * input_section; + + bfd_vma sdata_begin_symbol_vma; + bfd_boolean sdata_begin_symbol_vma_set; + bfd_vma got_symbol_vma; + + bfd_boolean should_relocate; +}; + +static void +debug_arc_reloc (struct arc_relocation_data reloc_data) +{ + fprintf (stderr, "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", + (unsigned int) reloc_data.reloc_offset, + (unsigned int) reloc_data.reloc_addend); + fprintf (stderr, " Symbol:\n"); + fprintf (stderr, " 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", + reloc_data.sym_section->name, + (unsigned int) reloc_data.sym_section->output_offset, + (unsigned int) reloc_data.sym_section->output_section->vma); + } + else + fprintf (stderr, " symbol section is NULL\n"); + + fprintf (stderr, " Input_section:\n"); + if (reloc_data.input_section != NULL) + { + fprintf (stderr, + " 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", + (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"); +} + +#define S (reloc_data.sym_value \ + + reloc_data.sym_section->output_offset \ + + reloc_data.sym_section->output_section->vma) +#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 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)) \ + ) & ~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 none (0) + +#define ARC_RELOC_HOWTO(TYPE, VALUE, SIZE, BITSIZE, RELOC_FUNCTION, OVERFLOW, FORMULA) \ + case R_##TYPE: \ + { \ + bfd_vma bitsize ATTRIBUTE_UNUSED = BITSIZE; \ + relocation = FORMULA ; \ + insn = RELOC_FUNCTION (insn, relocation); \ + } \ + break; + +static bfd_reloc_status_type +arc_do_relocation (bfd_byte * contents, struct arc_relocation_data reloc_data) +{ + bfd_vma relocation = 0; + bfd_vma insn; + bfd_vma orig_insn ATTRIBUTE_UNUSED; + + if (reloc_data.should_relocate == FALSE) + return bfd_reloc_notsupported; + + switch (reloc_data.howto->size) + { + case 2: + insn = arc_bfd_get_32 (reloc_data.input_section->owner, + contents + reloc_data.reloc_offset, + reloc_data.input_section); + break; + case 1: + case 0: + insn = arc_bfd_get_16 (reloc_data.input_section->owner, + contents + reloc_data.reloc_offset, + reloc_data.input_section); + break; + default: + insn = 0; + BFD_ASSERT (0); + break; + } + + orig_insn = insn; + + switch (reloc_data.howto->type) + { + #include "elf/arc-reloc.def" + + default: + BFD_ASSERT (0); + break; + } + + /* Check for relocation overflow. */ + if (reloc_data.howto->complain_on_overflow != complain_overflow_dont) + { + bfd_reloc_status_type flag; + flag = bfd_check_overflow (reloc_data.howto->complain_on_overflow, + reloc_data.howto->bitsize, + reloc_data.howto->rightshift, + bfd_arch_bits_per_address (reloc_data.input_section->owner), + relocation); + +#undef DEBUG_ARC_RELOC +#define DEBUG_ARC_RELOC(A) debug_arc_reloc (A) + if (flag != bfd_reloc_ok) + { + fprintf (stderr, "Relocation overflows !!!!\n"); + + DEBUG_ARC_RELOC (reloc_data); + + fprintf (stderr, + "Relocation value = signed -> %d, unsigned -> %u, hex -> (0x%08x)\n", + (int) relocation, + (unsigned int) relocation, + (unsigned int) relocation); + return flag; + } + } +#undef DEBUG_ARC_RELOC +#define DEBUG_ARC_RELOC(A) + + switch (reloc_data.howto->size) + { + case 2: + arc_bfd_put_32 (reloc_data.input_section->owner, insn, + contents + reloc_data.reloc_offset, + reloc_data.input_section); + break; + case 1: + case 0: + arc_bfd_put_16 (reloc_data.input_section->owner, insn, + contents + reloc_data.reloc_offset, + reloc_data.input_section); + break; + default: + ARC_DEBUG ("size = %d\n", reloc_data.howto->size); + BFD_ASSERT (0); + break; + } + + return bfd_reloc_ok; +} +#undef S +#undef A +#undef B +#undef G +#undef GOT +#undef L +#undef MES +#undef P +#undef SECTSTAR +#undef SECTSTART +#undef _SDA_BASE_ +#undef none + +#undef ARC_RELOC_HOWTO + +static bfd_vma * +arc_get_local_got_offsets (bfd * abfd) +{ + static bfd_vma *local_got_offsets = NULL; + + if (local_got_offsets == 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) + 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; + } + + return local_got_offsets; +} + + +/* Relocate an arc ELF section. + Function : elf_arc_relocate_section + Brief : Relocate an arc section, by handling all the relocations + 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. */ +static bfd_boolean +elf_arc_relocate_section (bfd * output_bfd, + struct bfd_link_info * info, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, + Elf_Internal_Rela * relocs, + Elf_Internal_Sym * local_syms, + asection ** local_sections) +{ + Elf_Internal_Shdr * symtab_hdr; + struct elf_link_hash_entry ** sym_hashes; + bfd_vma * local_got_offsets; + Elf_Internal_Rela * rel; + Elf_Internal_Rela * relend; + + symtab_hdr = &((elf_tdata (input_bfd))->symtab_hdr); + sym_hashes = elf_sym_hashes (input_bfd); + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + enum elf_arc_reloc_type r_type; + reloc_howto_type * howto; + unsigned long r_symndx; + struct elf_link_hash_entry * h; + Elf_Internal_Sym * sym; + asection * sec; + + struct arc_relocation_data reloc_data = + { + .reloc_offset = 0, /* bfd_vma reloc_offset; */ + .reloc_addend = 0, /* bfd_vma reloc_addend; */ + .got_offset_value = 0, /* bfd_vma got_offset_value; */ + .sym_value = 0, /* bfd_vma sym_value; */ + .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; */ + .got_symbol_vma = 0, /* bfd_vma got_symbol_vma; */ + .should_relocate = FALSE /* bfd_boolean should_relocate; */ + }; + + struct elf_link_hash_entry *h2; + + 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) + { + reloc_data.sdata_begin_symbol_vma = + (h2->root.u.def.value + + h2->root.u.def.section->output_section->vma); + 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 = &elf_arc_howto_table[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; + sec = NULL; + + if (r_symndx < symtab_hdr->sh_info) /* A local symbol. */ + { + 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]; + } + + reloc_data.should_relocate = TRUE; + } + else /* Global symbol. */ + { + /* Get the symbol's entry in the symtab. */ + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + BFD_ASSERT ((h->dynindx == -1) >= (h->forced_local != 0)); + /* If we have encountered a definition for this symbol. */ + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + reloc_data.sym_value = h->root.u.def.value; + reloc_data.sym_section = h->root.u.def.section; + + reloc_data.should_relocate = TRUE; + + if (is_reloc_for_GOT (howto)) + { + struct dynamic_sections ds = + arc_create_dynamic_sections (output_bfd, info); + + /* TODO: Change it to use arc_do_relocation with ARC_32 + reloc. */ + 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); + + } + } + else if (h->root.type == bfd_link_hash_undefweak) + { + /* Is weak symbol and has no definition. */ + 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.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.should_relocate = TRUE; + } + else if (!(*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, input_section, + rel->r_offset,!bfd_link_pic (info))) + { + return FALSE; + } + } + + reloc_data.got_offset_value = h->got.offset; + } + + if (is_reloc_SDA_relative (howto) && reloc_data.sdata_begin_symbol_vma_set == FALSE) + { + (*_bfd_error_handler) + ("Error: Linker symbol __SDATA_BEGIN__ not found"); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + DEBUG_ARC_RELOC (reloc_data); + if (arc_do_relocation (contents, reloc_data) != bfd_reloc_ok) + return FALSE; + } + + return TRUE; +} + +static struct dynamic_sections +arc_create_dynamic_sections (bfd * abfd, struct bfd_link_info *info) +{ + static bfd * dynobj = NULL; + 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; + } + else + dynobj = (elf_hash_table (info))->dynobj; + + ds.sgot = bfd_get_section_by_name (dynobj, ".got"); + + ds.srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + if (ds.srelgot == NULL) + { + 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.sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); + + 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.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; \ + } + +static bfd_boolean +elf_arc_check_relocs (bfd * abfd, + struct bfd_link_info * info, + 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; + + 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); + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + enum elf_arc_reloc_type r_type; + reloc_howto_type *howto; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + + 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]; + + /* Load symbol information. */ + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) /* Is a local symbol. */ + h = NULL; + else /* Global one. */ + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + if (is_reloc_for_PLT (howto) == TRUE) + { + if (h == NULL) + continue; + else + h->needs_plt = 1; + } + + if (is_reloc_for_GOT (howto) == TRUE) + { + if (h == NULL) + { + /* Local symbol. */ + local_got_offsets[r_symndx] = + ADD_SYMBOL_REF_SEC_AND_RELOC (got, bfd_link_pic (info), NULL); + } + else + { + /* Global symbol. */ + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + h->got.offset = + ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h); + } + } + } + + 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 */ +}; + +/* 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 */ +}; + +#define elf_arcV2_pic_PLT0_ENTRY_SIZE (2 * PLT_ENTRY_SIZE_V2/2) + +/* 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 /* ------ " " -------------- */ +}; + +#define elf_arcV2_pic_PLTN_ENTRY_SIZE (PLT_ENTRY_SIZE_V2/2) + +enum plt_reloc_symbol +{ + LAST_RELOC = 0, + + SGOT = 1, + + 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) + +struct plt_reloc +{ + bfd_vma offset; + bfd_vma size; + bfd_vma mask; + enum plt_reloc_symbol symbol; + bfd_vma addend; +}; + +struct plt_version_t +{ + 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 + +struct plt_version_t plt_versions[] = +{ + { + PLT_DATA (elf_arcV2_pic), + .entry_relocs = + { + {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 = + { + {4, 32, 0xFFFFFFFF, SGOT, 0}, + {0, 0, 0, LAST_RELOC, 0} + } + } +}; +#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); + 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); + + ret = ds.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); + + return ret; +} + +#define PLT_DO_RELOCS_FOR_ENTRY(DS, RELOCS) \ + plt_do_relocs_for_symbol (DS, RELOCS, 0, 0) + +static void +plt_do_relocs_for_symbol (struct dynamic_sections *ds, + struct plt_reloc *reloc, + bfd_vma plt_offset, + bfd_vma symbol_got_offset) +{ + while (SYM_ONLY (reloc->symbol) != LAST_RELOC) + { + bfd_vma relocation = 0; + + switch (SYM_ONLY (reloc->symbol)) + { + case SGOT: + relocation = + ds->sgotplt->output_section->vma + + ds->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_MIDDLE_ENDIAN (reloc->symbol)) + { + relocation = + ((relocation & 0xffff0000) >> 16) | + ((relocation & 0xffff) << 16); + } + + switch (reloc->size) + { + case 32: + bfd_put_32 (ds->splt->output_section->owner, + relocation, + ds->splt->contents + plt_offset + reloc->offset); + break; + } + + reloc = &(reloc[1]); /* Jump to next relocation. */ + } +} + +static void +relocate_plt_for_symbol (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); + + bfd_vma plt_index = h->plt.offset / 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, + got_offset); +} + +static void +relocate_plt_for_entry (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); + + memcpy (ds.splt->contents, plt_data->entry, + plt_data->entry_size * sizeof (insn_hword)); + PLT_DO_RELOCS_FOR_ENTRY (&ds, 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 + understand. */ + +static bfd_boolean +elf_arc_adjust_dynamic_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *h) +{ + bfd *dynobj = (elf_hash_table (info))->dynobj; + struct dynamic_sections ds = arc_create_dynamic_sections (dynobj, info); + + if (h->needs_plt == 1) + { + if (!bfd_link_pic (info) && !h->def_dynamic && !h->ref_dynamic) + { + /* This case can occur if we saw a PLT32 reloc in an input + file, but the symbol was never referred to by a dynamic + object. In such a case, we don't actually need to build + a procedure linkage table, and we can just do a PC32 + reloc instead. */ + BFD_ASSERT (h->needs_plt); + return TRUE; + } + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1 && !h->forced_local + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + + 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.value = loc; + } + h->plt.offset = loc; + } + } + else + { + h->plt.offset = (bfd_vma) - 1; + h->needs_plt = 0; + } + + return TRUE; +} + +#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); \ +} + +/* Function : elf_arc_finish_dynamic_symbol + Brief : Finish up dynamic symbol handling. We set the + contents of various dynamic sections here. + Args : output_bfd : + 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 (bfd_link_pic (info) && (info->symbolic || h->dynindx == -1) + && h->def_regular) + { + ADD_RELA (output_bfd, got, h->got.offset, 0, R_ARC_RELATIVE, 0); + } + else + { + ADD_RELA (output_bfd, got, h->got.offset, h->dynindx, + R_ARC_GLOB_DAT, 0); + } + } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "__DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + + 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; + +/* Function : elf_arc_finish_dynamic_sections + Brief : Finish up the dynamic sections handling. + Args : output_bfd : + 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) +{ + struct dynamic_sections ds = arc_create_dynamic_sections (output_bfd, info); + bfd *dynobj = (elf_hash_table (info))->dynobj; + + if (ds.sdyn) + { + Elf32_External_Dyn *dyncon, *dynconend; + + dyncon = (Elf32_External_Dyn *) ds.sdyn->contents; + dynconend = + (Elf32_External_Dyn *) (ds.sdyn->contents + ds.sdyn->size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn internal_dyn; + bfd_boolean do_it = FALSE; + + struct elf_link_hash_entry *h = NULL; + asection *s = NULL; + + bfd_elf32_swap_dyn_in (dynobj, dyncon, &internal_dyn); + + 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") + default: + break; + } + + /* In case the dynamic symbols should be updated with a + symbol. */ + if (h != NULL + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + ) + { + asection *asec_ptr; + + internal_dyn.d_un.d_val = h->root.u.def.value; + asec_ptr = h->root.u.def.section; + if (asec_ptr->output_section != NULL) + { + internal_dyn.d_un.d_val += + (asec_ptr->output_section->vma + + asec_ptr->output_offset); + } + else + { + /* 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; + } + else if (s != NULL) /* With a section information. */ + { + switch (internal_dyn.d_tag) + { + case DT_PLTGOT: + case DT_JMPREL: + internal_dyn.d_un.d_ptr = s->vma; + do_it = TRUE; + break; + + case DT_PLTRELSZ: + internal_dyn.d_un.d_val = s->size; + do_it = TRUE; + break; + + case DT_RELASZ: + internal_dyn.d_un.d_val -= s->size; + do_it = TRUE; + break; + + default: + break; + } + } + + if (do_it == TRUE) + bfd_elf32_swap_dyn_out (output_bfd, &internal_dyn, dyncon); + } + + if (ds.splt->size > 0) + { + relocate_plt_for_entry (info); + } + + elf_section_data (ds.srelplt->output_section)->this_hdr.sh_entsize = 0xc; + } + + /* Fill in the first three entries in the global offset table. */ + if (ds.sgot) + { + if (ds.sgot->size > 0) + { + if (ds.sdyn == NULL) + bfd_put_32 (output_bfd, (bfd_vma) 0, + ds.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; + } + } + + 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)) \ + 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) +{ + bfd * dynobj; + asection * s; + bfd_boolean relocs_exist; + bfd_boolean reltext_exist; + struct dynamic_sections ds = arc_create_dynamic_sections (output_bfd, info); + + dynobj = (elf_hash_table (info))->dynobj; + BFD_ASSERT (dynobj != NULL); + + if ((elf_hash_table (info))->dynamic_sections_created) + { + struct elf_link_hash_entry *h; + + /* 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->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_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; + } + + 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) + continue; + + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->size); + if (s->contents == NULL && s->size != 0) + return FALSE; + + if (s->size == 0) + { + s->flags |= SEC_EXCLUDE; + continue; + } + + 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; + } + } + + if (ds.sdyn) + { + if (ds.splt && ds.splt->size != 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) + || !_bfd_elf_add_dynamic_entry (info, DT_JMPREL, 0) + ) + return FALSE; + + if (relocs_exist == TRUE) + if (!_bfd_elf_add_dynamic_entry (info, DT_RELA, 0) + || !_bfd_elf_add_dynamic_entry (info, DT_RELASZ, 0) + || !_bfd_elf_add_dynamic_entry (info, DT_RELENT, + sizeof (Elf32_External_Rela)) + ) + return FALSE; + + if (reltext_exist == TRUE) + if (!_bfd_elf_add_dynamic_entry (info, DT_TEXTREL, 0)) + return FALSE; + } + + return TRUE; +} + + #define TARGET_LITTLE_SYM arc_elf32_le_vec #define TARGET_LITTLE_NAME "elf32-littlearc" -#define TARGET_BIG_SYM arc_elf32_be_vec -#define TARGET_BIG_NAME "elf32-bigarc" -#define ELF_ARCH bfd_arch_arc -#define ELF_MACHINE_CODE EM_ARC -#define ELF_MAXPAGESIZE 0x1000 - -#define elf_info_to_howto 0 -#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 +#define TARGET_BIG_SYM arc_elf32_be_vec +#define TARGET_BIG_NAME "elf32-bigarc" +#define ELF_ARCH bfd_arch_arc +#define ELF_MACHINE_CODE EM_ARC_COMPACT +#define ELF_MACHINE_ALT1 EM_ARC_COMPACT2 +#define ELF_MAXPAGESIZE 0x2000 + +#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 + +#define elf_backend_relocate_section elf_arc_relocate_section +#define elf_backend_check_relocs elf_arc_check_relocs +#define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections + +#define elf_backend_adjust_dynamic_symbol elf_arc_adjust_dynamic_symbol +#define elf_backend_finish_dynamic_symbol elf_arc_finish_dynamic_symbol + +#define elf_backend_finish_dynamic_sections elf_arc_finish_dynamic_sections +#define elf_backend_size_dynamic_sections elf_arc_size_dynamic_sections + +#define elf_backend_can_gc_sections 1 +#define elf_backend_want_got_plt 1 +#define elf_backend_plt_readonly 1 +#define elf_backend_want_plt_sym 0 +#define elf_backend_got_header_size 12 + +#define elf_backend_may_use_rel_p 0 +#define elf_backend_may_use_rela_p 1 +#define elf_backend_default_use_rela_p 1 #include "elf32-target.h" |