aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-arc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-arc.c')
-rw-r--r--bfd/elf32-arc.c1734
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"