blob: 43ff90ef1b9b78ccd0595a346fde8de680955504 [file] [log] [blame]
/* ARC-specific support for 32-bit ELF
Copyright 1994, 1995, 1997, 1999, 2001, 2002, 2005, 2007
Free Software Foundation, Inc.
Contributed by Doug Evans (dje@cygnus.com).
This file is part of BFD, the Binary File Descriptor library.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/arc.h"
/* ****************************************************************
* NOTE: The pic related work starts after the comment marked as
* ~~~~~~ "* PIC-related routines for the arc backend "
* ****************************************************************/
#define BFD_DEBUG_PIC(x)
/*#define BFD_DEBUG_PIC(x) (fprintf(stderr,"DEBUG: %d@%s: ",__LINE__,__PRETTY_FUNCTION__),x)*/
static reloc_howto_type *arc_elf32_bfd_reloc_type_lookup
PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
static void arc_info_to_howto_rel
PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
static bfd_boolean arc_elf_object_p PARAMS ((bfd *));
static void arc_elf_final_write_processing PARAMS ((bfd *, bfd_boolean));
static bfd_reloc_status_type arc_elf_b22_pcrel
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
/* We must define USE_RELA to get the proper fixups for PC relative
branches to symbols defined in other object files. The addend is
used to account for the PC having been incremented before the PC
relative address is calculated. mlm */
#define USE_RELA
#define bfd_put32(a,b,c)
static bfd_vma bfd_get_32_me
PARAMS ((bfd *, const unsigned char *));
static void bfd_put_32_me
PARAMS ((bfd *, bfd_vma, unsigned char *));
static bfd_reloc_status_type arcompact_elf_me_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type arc_unsupported_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_boolean arc_elf_merge_private_bfd_data
PARAMS ((bfd *ibfd, bfd *obfd));
static reloc_howto_type * arc_elf_calculate_howto_index
(enum elf_arc_reloc_type r_type);
#define INIT_SYM_STRING "init"
#define FINI_SYM_STRING "fini"
/* The default symbols representing the init and fini dyn values */
char * init_str = INIT_SYM_STRING;
char * fini_str = FINI_SYM_STRING;
/* The ARC linker needs to keep track of the number of relocs that it
decides to copy in check_relocs for each symbol. This is so that
it can discard PC relative relocs if it doesn't need them when
linking with -Bsymbolic. We store the information in a field
extending the regular ELF linker hash table. */
/* This structure keeps track of the number of PC relative relocs we
have copied for a given symbol. */
#define bfd_elf32_bfd_link_hash_table_create \
elf_ARC_link_hash_table_create
struct elf_ARC_pcrel_relocs_copied
{
/* Next section. */
struct elf_ARC_pcrel_relocs_copied *next;
/* A section in dynobj. */
asection *section;
/* Number of relocs copied in this section. */
bfd_size_type count;
};
/* ARC ELF linker hash entry. */
struct elf_ARC_link_hash_entry
{
struct elf_link_hash_entry root;
/* Number of PC relative relocs copied for this symbol. */
struct elf_ARC_pcrel_relocs_copied *pcrel_relocs_copied;
};
/* ARC ELF linker hash table. */
struct elf_ARC_link_hash_table
{
struct elf_link_hash_table root;
};
/* Declare this now that the above structures are defined. */
static bfd_boolean elf_ARC_discard_copies
PARAMS ((struct elf_ARC_link_hash_entry *, PTR));
/* Traverse an ARC ELF linker hash table. */
#define elf_ARC_link_hash_traverse(table, func, info) \
(elf_link_hash_traverse \
(&(table)->root, \
(bfd_boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
(info)))
/* Get the ARC ELF linker hash table from a link_info structure. */
#define elf_ARC_hash_table(p) \
((struct elf_ARC_link_hash_table *) ((p)->hash))
/* Create an entry in an ARC ELF linker hash table. */
static struct bfd_hash_entry *
elf_ARC_link_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
struct elf_ARC_link_hash_entry *ret =
(struct elf_ARC_link_hash_entry *) entry;
/* Allocate the structure if it has not already been allocated by a
subclass. */
if (ret == (struct elf_ARC_link_hash_entry *) NULL)
ret = ((struct elf_ARC_link_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct elf_ARC_link_hash_entry)));
if (ret == (struct elf_ARC_link_hash_entry *) NULL)
return (struct bfd_hash_entry *) ret;
/* Call the allocation method of the superclass. */
ret = ((struct elf_ARC_link_hash_entry *)
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
table, string));
if (ret != (struct elf_ARC_link_hash_entry *) NULL)
{
ret->pcrel_relocs_copied = NULL;
}
return (struct bfd_hash_entry *) ret;
}
/* Create an ARC ELF linker hash table. */
static struct bfd_link_hash_table *
elf_ARC_link_hash_table_create (bfd * abfd)
{
struct elf_ARC_link_hash_table *ret;
ret = ((struct elf_ARC_link_hash_table *)
bfd_alloc (abfd, sizeof (struct elf_ARC_link_hash_table)));
if (ret == (struct elf_ARC_link_hash_table *) NULL)
return NULL;
if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
elf_ARC_link_hash_newfunc,/* stub for ARC (according to ChangeLog 2006-05-24 Bjoern Haase )*/0 ))
{
bfd_release (abfd, ret);
return NULL;
}
return &ret->root.root;
}
/* This function is called via elf_ARC_link_hash_traverse if we are
creating a shared object with -Bsymbolic. It discards the space
allocated to copy PC relative relocs against symbols which are
defined in regular objects. We allocated space for them in the
check_relocs routine, but we won't fill them in in the
relocate_section routine. */
/*ARGSUSED*/
static bfd_boolean
elf_ARC_discard_copies (struct elf_ARC_link_hash_entry * h,
PTR ignore ATTRIBUTE_UNUSED)
{
struct elf_ARC_pcrel_relocs_copied *s;
/* We only discard relocs for symbols defined in a regular object. */
if((h->root.def_regular) == 0)
return TRUE;
for (s = h->pcrel_relocs_copied; s != NULL; s = s->next)
s->section->rawsize -= s->count * sizeof (Elf32_External_Rel);
return TRUE;
}
/* The HOWTO Array needs to be specified as follows.
HOWTO
{
type --- > Relocation Type
rightshift --- > Rightshift the value by this amount.
size --- > Size 0- byte , 1-short, 2 -long
bitsize --- > Exact bitsize.
pcrel --- > PC Relative reloc.
bitpos --- > Bit Position.
complain_on_overflow ---> What complaint on overflow.
function --- > Any special function to be used .
name --- > Relocation Name.
partial_inplace--> Addend sits partially in place and in
Reloc Table.
srcmask ---> Source Mask 0 for RELA and corresponding
field if USE_REL or partial_inplace
is set.
dstmask ---> Destination Mask . Destination field mask.
pcreloffset ---> pcrel offset . If a PCREL reloc is created
and the assembler leaves an offset in here.
}
If in the backend you need to access the howto array, please
use the arc_elf_calculate_howto_index function. All changes in
the HOWTO array need a corresponding change in the above mentioned
function. The need for this function is the presence of a hole
in the ARC ABI.
*/
#define ARC_RELA_HOWTO(type,rightshift,size,bitsz,pcrel,bitpos , \
function,name,dstmask) \
\
HOWTO( type,rightshift,size,bitsz,pcrel,bitpos, \
complain_overflow_bitfield,function, \
name,FALSE,0,dstmask,FALSE)
#define ARCOMPACT_RELA_HOWTO(type,rightshift,size,bitsz,pcrel,bitpos, \
function,name,dstmask) \
\
HOWTO( type,rightshift,size,bitsz,pcrel,bitpos, \
complain_overflow_signed,function, \
name,FALSE,0,dstmask,FALSE)
#define ARC_UNSUPPORTED_HOWTO(type,name) \
ARC_RELA_HOWTO (type ,0 ,2 ,32,FALSE,0,arc_unsupported_reloc,name,0)
static reloc_howto_type elf_arc_howto_table[] =
{
/* This reloc does nothing. */
ARC_RELA_HOWTO (R_ARC_NONE ,0 ,2 ,32,FALSE,0,bfd_elf_generic_reloc,
"R_ARC_NONE",0),
ARC_RELA_HOWTO (R_ARC_8 ,0 ,0 , 8,FALSE,0,bfd_elf_generic_reloc,
"R_ARC_8" ,0xff),
ARC_RELA_HOWTO (R_ARC_16 ,0 ,1 ,16,FALSE,0,bfd_elf_generic_reloc,
"R_ARC_16",0xffff),
ARC_RELA_HOWTO (R_ARC_24 ,0 ,2 ,24,FALSE,0,bfd_elf_generic_reloc,
"R_ARC_24",0xffffff),
ARC_RELA_HOWTO (R_ARC_32 ,0 ,3 ,32,FALSE,0,bfd_elf_generic_reloc,
"R_ARC_32",-1),
ARC_RELA_HOWTO (R_ARC_B26 ,2 ,2 ,26,FALSE,0,bfd_elf_generic_reloc,
"R_ARC_B26",0xffffff),
ARC_RELA_HOWTO (R_ARC_B22_PCREL,2,2,22,TRUE,7,arc_elf_b22_pcrel,
"R_ARC_B22_PCREL",0x7ffff80),
ARC_RELA_HOWTO (R_ARC_H30 ,2 ,3 ,32, FALSE, 0, bfd_elf_generic_reloc,
"R_ARC_H30",-1),
ARC_UNSUPPORTED_HOWTO(R_ARC_N8,"R_ARC_N8"),
ARC_UNSUPPORTED_HOWTO(R_ARC_N16,"R_ARC_N16"),
ARC_UNSUPPORTED_HOWTO(R_ARC_N24,"R_ARC_N24"),
ARC_UNSUPPORTED_HOWTO(R_ARC_N32,"R_ARC_N32"),
ARC_UNSUPPORTED_HOWTO(R_ARC_SDA,"R_ARC_SDA"),
ARC_UNSUPPORTED_HOWTO(R_ARC_SECTOFF,"R_ARC_SECTOFF"),
/* FIXME: Change complaint to complain_overflow_signed. */
/* Tangent-A5 relocations. */
ARCOMPACT_RELA_HOWTO (R_ARC_S21H_PCREL,1,2,21,TRUE,0,arcompact_elf_me_reloc,
"R_ARC_S21H_PCREL",0x7feffc0),
ARCOMPACT_RELA_HOWTO (R_ARC_S21W_PCREL,2,2,21,TRUE,0,arcompact_elf_me_reloc,
"R_ARC_S21W_PCREL",0x7fcffc0),
ARCOMPACT_RELA_HOWTO (R_ARC_S25H_PCREL,1,2,25,TRUE,0,arcompact_elf_me_reloc,
"R_ARC_S25H_PCREL",0x7feffcf),
ARCOMPACT_RELA_HOWTO (R_ARC_S25W_PCREL,2,2,25,TRUE,0,arcompact_elf_me_reloc,
"R_ARC_S25W_PCREL",0x7fcffcf),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA32,"R_ARC_SDA32"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA_LDST,"R_ARC_SDALDST"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA_LDST1,"R_ARC_SDALDST1"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA_LDST2,"R_ARC_SDALDST2"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA16_LD,"R_ARC_SDA16_LD"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA16_LD1,"R_ARC_SDA16_LD1"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA16_LD2,"R_ARC_SDA16_LD2"),
ARCOMPACT_RELA_HOWTO (R_ARC_S13_PCREL,2,1,13,TRUE,0,arcompact_elf_me_reloc,
"R_ARC_S13_PCREL",0x7ff),
ARC_UNSUPPORTED_HOWTO (R_ARC_W,"R_ARC_W"),
/* 'Middle-endian' (ME) 32-bit word relocations, stored in two half-words.
The individual half-words are stored in the native endian of the
machine; this is how all 32-bit instructions and long-words are stored
in the ARCompact ISA in the executable section. */
ARC_RELA_HOWTO (R_ARC_32_ME ,0 ,2 ,32, FALSE, 0, arcompact_elf_me_reloc,
"R_ARC_32_ME",-1),
ARC_UNSUPPORTED_HOWTO (R_ARC_N32_ME,"R_ARC_N32_ME"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_ME,"R_ARC_SECTOFF_ME"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SDA32_ME,"R_ARC_SDA32_ME"),
ARC_UNSUPPORTED_HOWTO (R_ARC_W_ME,"R_ARC_W_ME"),
ARC_UNSUPPORTED_HOWTO (R_ARC_H30_ME,"R_ARC_H30_ME"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_U8,"R_ARC_SECTOFF_U8"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_S9,"R_ARC_SECTOFF_S9"),
ARC_UNSUPPORTED_HOWTO (R_AC_SECTOFF_U8,"R_AC_SECTOFF_U8"),
ARC_UNSUPPORTED_HOWTO (R_AC_SECTOFF_U8_1,"R_AC_SECTOFF_U8_1"),
ARC_UNSUPPORTED_HOWTO (R_AC_SECTOFF_U8_2,"R_ARC_SECTOFF_U8_2"),
ARC_UNSUPPORTED_HOWTO (R_AC_SECTOFF_S9,"R_AC_SECTOFF_S9"),
ARC_UNSUPPORTED_HOWTO (R_AC_SECTOFF_S9_1,"R_AC_SECTOFF_S9_1"),
ARC_UNSUPPORTED_HOWTO (R_AC_SECTOFF_S9_2,"R_AC_SECTOFF_S9_2"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_ME_1,"R_ARC_SECTOFF_ME_1"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_ME_2,"R_ARC_SECTOFF_ME_2"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_1,"R_ARC_SECTOFF_1"),
ARC_UNSUPPORTED_HOWTO (R_ARC_SECTOFF_2,"R_ARC_SECTOFF_2"),
/* There is a gap here of 5. */
#define R_ARC_hole_base 0x2d
#define R_ARC_reloc_hole_gap 5
ARC_RELA_HOWTO (R_ARC_PC32, 0, 2, 32, FALSE, 0, arcompact_elf_me_reloc,
"R_ARC_PC32",-1),
/* PC relative was true for this earlier. */
ARC_RELA_HOWTO (R_ARC_GOTPC32, 0, 2, 32, FALSE, 0, arcompact_elf_me_reloc,
"R_ARC_GOTPC32",-1),
ARC_RELA_HOWTO (R_ARC_PLT32, 0, 2, 32, FALSE, 0, arcompact_elf_me_reloc,
"R_ARC_PLT32",-1),
ARC_RELA_HOWTO (R_ARC_COPY, 0, 2, 32, FALSE,0 , arcompact_elf_me_reloc,
"R_ARC_COPY",-1),
ARC_RELA_HOWTO (R_ARC_GLOB_DAT, 0, 2, 32, FALSE,0 , arcompact_elf_me_reloc,
"R_ARC_GLOB_DAT",-1),
ARC_RELA_HOWTO (R_ARC_JMP_SLOT, 0, 2, 32, FALSE,0 , arcompact_elf_me_reloc,
"R_ARC_JMP_SLOT",-1),
ARC_RELA_HOWTO (R_ARC_RELATIVE, 0, 2, 32, FALSE,0 , arcompact_elf_me_reloc,
"R_ARC_RELATIVE",-1),
ARC_RELA_HOWTO (R_ARC_GOTOFF, 0, 2, 32, FALSE,0 , arcompact_elf_me_reloc,
"R_ARC_GOTOFF",-1),
ARC_RELA_HOWTO (R_ARC_GOTPC, 0, 2, 32, FALSE,0 , arcompact_elf_me_reloc,
"R_ARC_GOTPC",-1),
};
static bfd_reloc_status_type
arc_unsupported_reloc (bfd * ibfd ATTRIBUTE_UNUSED,
arelent * rel ATTRIBUTE_UNUSED,
asymbol * sym ATTRIBUTE_UNUSED,
PTR ptr ATTRIBUTE_UNUSED,
asection * section ATTRIBUTE_UNUSED,
bfd *obfd ATTRIBUTE_UNUSED,
char ** data ATTRIBUTE_UNUSED
)
{
return bfd_reloc_notsupported;
}
/* Map BFD reloc types to ARC ELF reloc types. */
struct arc_reloc_map
{
enum bfd_reloc_code_real bfd_reloc_val;
enum elf_arc_reloc_type elf_reloc_val;
};
static const struct arc_reloc_map arc_reloc_map[] =
{
{ 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 },
{ BFD_RELOC_CTOR, R_ARC_32 },
{ BFD_RELOC_ARC_B26, R_ARC_B26 },
{ BFD_RELOC_ARC_B22_PCREL, R_ARC_B22_PCREL },
{ BFD_RELOC_ARC_S21H_PCREL, R_ARC_S21H_PCREL },
{ BFD_RELOC_ARC_S21W_PCREL, R_ARC_S21W_PCREL },
{ BFD_RELOC_ARC_S25H_PCREL, R_ARC_S25H_PCREL },
{ BFD_RELOC_ARC_S25W_PCREL, R_ARC_S25W_PCREL },
{ BFD_RELOC_ARC_S13_PCREL, R_ARC_S13_PCREL },
{ BFD_RELOC_ARC_32_ME, R_ARC_32_ME },
{ BFD_RELOC_ARC_PC32, R_ARC_PC32 },
{ BFD_RELOC_ARC_GOTPC32, R_ARC_GOTPC32 },
{ BFD_RELOC_ARC_COPY , R_ARC_COPY},
{ BFD_RELOC_ARC_JMP_SLOT, R_ARC_JMP_SLOT },
{ BFD_RELOC_ARC_GLOB_DAT, R_ARC_GLOB_DAT },
{ BFD_RELOC_ARC_GOTOFF , R_ARC_GOTOFF},
{ BFD_RELOC_ARC_GOTPC , R_ARC_GOTPC},
{ BFD_RELOC_ARC_PLT32 , R_ARC_PLT32 },
};
static reloc_howto_type *
arc_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
bfd_reloc_code_real_type code)
{
unsigned int i;
for (i = 0;
i < sizeof (arc_reloc_map) / sizeof (struct arc_reloc_map);
i++)
{
if (arc_reloc_map[i].bfd_reloc_val == code)
{
enum elf_arc_reloc_type r_type;
r_type = arc_reloc_map[i].elf_reloc_val;
return arc_elf_calculate_howto_index(r_type);
}
}
return NULL;
}
/* Calculate the howto index. */
static reloc_howto_type *
arc_elf_calculate_howto_index(enum elf_arc_reloc_type r_type)
{
BFD_ASSERT (r_type < (unsigned int) R_ARC_max);
BFD_ASSERT ((r_type < (unsigned int)R_ARC_hole_base) ||
(r_type >= (unsigned int) R_ARC_hole_base +
R_ARC_reloc_hole_gap));
if(r_type > R_ARC_hole_base)
r_type -= R_ARC_reloc_hole_gap;
return &elf_arc_howto_table[r_type];
}
/* 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)
{
enum elf_arc_reloc_type r_type;
r_type = ELF32_R_TYPE (dst->r_info);
cache_ptr->howto = arc_elf_calculate_howto_index(r_type);
}
/* Merge backend specific data from an object file to the output
object file when linking. */
static bfd_boolean
arc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
{
unsigned short mach_ibfd;
static unsigned short mach_obfd = EM_NONE;
if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return TRUE;
if(bfd_count_sections(ibfd) == 0)
return TRUE ; /* For the case of empty archive files */
mach_ibfd = elf_elfheader(ibfd)->e_machine;
/* Check if we have the same endianess. */
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
{
return FALSE;
}
if(mach_obfd == EM_NONE)
{
mach_obfd = mach_ibfd;
}
else
{
if((mach_ibfd==EM_ARC && mach_obfd==EM_ARCOMPACT) ||
(mach_ibfd==EM_ARCOMPACT && mach_obfd==EM_ARC))
{
_bfd_error_handler(_("\ERROR: Attempting to link an %s binary(%s) \
with a binary incompatible %s binary(%s)"),
(mach_ibfd == EM_ARC)?"A4":"ARCompact",
bfd_get_filename(ibfd) ,
(mach_obfd == EM_ARC)?"A4":"ARCompact",
bfd_get_filename(obfd));
return FALSE;
}
}
if (bfd_get_mach (obfd) < bfd_get_mach (ibfd))
{
return bfd_set_arch_mach (obfd, bfd_arch_arc, bfd_get_mach(ibfd));
}
return TRUE;
}
/* Set the right machine number for an ARC ELF file. */
static bfd_boolean
arc_elf_object_p (abfd)
bfd *abfd;
{
int mach;
unsigned long arch = elf_elfheader (abfd)->e_flags & EF_ARC_MACH;
switch (arch)
{
case E_ARC_MACH_A5:
mach = bfd_mach_arc_a5;
break;
case E_ARC_MACH_ARC600:
mach = bfd_mach_arc_arc600;
break;
case E_ARC_MACH_ARC700:
mach = bfd_mach_arc_arc700;
break;
default:
/* Unknown cpu type. ??? What to do? */
return FALSE;
}
(void) bfd_default_set_arch_mach (abfd, bfd_arch_arc, mach);
return TRUE;
}
/* The final processing done just before writing out an ARC ELF object file.
This gets the ARC architecture right based on the machine number. */
static void
arc_elf_final_write_processing (abfd, linker)
bfd *abfd;
bfd_boolean linker ATTRIBUTE_UNUSED;
{
int mach;
unsigned long val;
switch (mach = bfd_get_mach (abfd))
{
case bfd_mach_arc_a5:
val = E_ARC_MACH_A5;
elf_elfheader (abfd)->e_machine = EM_ARCOMPACT;
break;
case bfd_mach_arc_arc600:
val = E_ARC_MACH_ARC600;
elf_elfheader (abfd)->e_machine = EM_ARCOMPACT;
break;
case bfd_mach_arc_arc700:
val = E_ARC_MACH_ARC700;
elf_elfheader (abfd)->e_machine = EM_ARCOMPACT;
break;
default:
abort();
}
elf_elfheader (abfd)->e_flags &=~ EF_ARC_MACH;
elf_elfheader (abfd)->e_flags |= val;
}
/* Handle an ARCompact 'middle-endian' relocation. */
static bfd_reloc_status_type
arcompact_elf_me_reloc (bfd *abfd ,
arelent *reloc_entry,
asymbol *symbol_in,
PTR data,
asection *input_section,
bfd *output_bfd,
char ** error_message ATTRIBUTE_UNUSED)
{
unsigned long insn;
#ifdef USE_REL
unsigned long offset
#endif
bfd_vma sym_value;
enum elf_arc_reloc_type r_type;
bfd_vma addr = reloc_entry->address;
bfd_byte *hit_data = addr + (bfd_byte *) data;
r_type = reloc_entry->howto->type;
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 an error if the symbol is not defined. An undefined weak
symbol is considered to have a value of zero (SVR4 ABI, p. 4-27). */
if (symbol_in != NULL && bfd_is_und_section (symbol_in->section)
&& ((symbol_in->flags & BSF_WEAK) == 0))
return bfd_reloc_undefined;
if (bfd_is_com_section (symbol_in->section))
sym_value = 0;
else
sym_value = (symbol_in->value
+ symbol_in->section->output_section->vma
+ symbol_in->section->output_offset);
sym_value += reloc_entry->addend;
if (r_type != R_ARC_32_ME) {
sym_value -= (input_section->output_section->vma
+ input_section->output_offset);
sym_value -= (reloc_entry->address & ~0x3);
}
insn = bfd_get_32_me(abfd, hit_data);
switch(r_type)
{
case R_ARC_S21H_PCREL:
#ifdef USE_REL
/* Retrieve the offset from the instruction, if any. */
/* Extract the first 10 bits from Position 6 to 15 in insn. */
offset = ((insn << 16) >> 22) << 10;
/* Extract the remaining 10 bits from Position 17 to 26 in insn. */
offset |= ((insn << 5) >> 22);
/* Fill in 1 bit to get the 21 bit Offset Value. */
offset = offset << 1;
/* Ramana : No addends remain in place. */
/* sym_value += offset; */
#endif /* USE_REL. */
/* Extract the instruction opcode alone from 'insn'. */
insn = insn & 0xf801003f;
insn |= ((((sym_value >> 1) & 0x3ff) << 17)
| (((sym_value >> 1) & 0xffc00) >> 4));
break;
case R_ARC_S21W_PCREL:
#ifdef USE_REL
/* Retrieve the offset from the instruction, if any */
/* Extract the first 10 bits from Position 6 to 15 in insn */
offset = ((insn << 16) >> 22) << 9;
/* Extract the remaining 9 bits from Position 18 to 26 in insn */
offset |= ((insn << 5) >> 23);
/* Fill in 2 bits to get the 25 bit Offset Value */
offset = offset << 2;
/* No addends remain in place */
/* sym_value += offset; */
#endif /* USE_REL. */
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf803003f;
insn |= ((((sym_value >> 2) & 0x1ff) << 18)
| (((sym_value >> 2) & 0x7fe00) >> 3));
break;
case R_ARC_S25H_PCREL:
#ifdef USE_REL
/* Retrieve the offset from the instruction, if any */
/* Extract the high 4 bits from Position 0 to 3 in insn */
offset = ((insn << 28) >> 28) << 10;
/* Extract the next 10 bits from Position 6 to 15 in insn */
offset |= ((insn << 16) >> 22);
offset = offset << 10;
/* Extract the remaining 10 bits from Position 17 to 26 in insn */
offset |= ((insn << 5) >> 22);
/* Fill in 1 bit to get the 25 bit Offset Value */
offset = offset << 1;
/* Ramana : No addends remain in place. */
/* sym_value += offset; */
#endif /* USE_REL. */
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf8010030;
insn |= ((((sym_value >> 1) & 0x3ff) << 17)
| (((sym_value >> 1) & 0xffc00) >> 4)
| (((sym_value >> 1) & 0xf00000) >> 20));
break;
case R_ARC_PLT32:
break;
case R_ARC_S25W_PCREL:
#ifdef USE_REL
/* Retrieve the offset from the instruction, if any */
/* Extract the high 4 bits from Position 0 to 3 in insn */
offset = ((insn << 28) >> 28) << 10;
/* Extract the next 10 bits from Position 6 to 15 in insn */
offset |= ((insn << 16) >> 22);
offset = offset << 9;
/* Extract the remaining 9 bits from Position 18 to 26 in insn */
offset |= ((insn << 5) >> 23);
/* Fill in 2 bits to get the 25 bit Offset Value */
offset = offset << 2;
/* Ramana : No addends remain in place */
/* sym_value += offset; */
#endif /* USE_REL. */
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf8030030;
insn |= ((((sym_value >> 2) & 0x1ff) << 18)
| (((sym_value >> 2) & 0x7fe00) >> 3)
| (((sym_value >> 2) & 0x780000) >> 19));
break;
case R_ARC_S13_PCREL:
#ifdef USE_REL
/* Retrieve the offset from the instruction, if any */
/* Extract the 11 bits from Position 0 to 10 in insn */
offset = (insn << 5) >> 21;
/* Fill in 2 bits to get the 13 bit Offset Value */
offset = offset << 2;
/* No addends remain in place */
/* sym_value += offset; */
#endif
/* Extract the instruction opcode alone from 'insn' */
insn = (insn & 0xf800ffff);
insn |= ((sym_value >> 2) & 0x7ff) << 16;
break;
case R_ARC_GOTPC32:
case R_ARC_32_ME:
insn = sym_value;
break;
default:
return bfd_reloc_notsupported;
break;
}
/* Middle-Endian Instruction Encoding only for executable code */
/* FIXME:: I am still not sure about this. Ramana . */
if (input_section && (input_section->flags & SEC_CODE))
bfd_put_32_me(abfd, insn, hit_data);
else
bfd_put_32(abfd, insn, hit_data);
return bfd_reloc_ok;
}
static bfd_vma
bfd_get_32_me (bfd * abfd,const unsigned char * data)
{
bfd_vma value = 0;
if (bfd_big_endian(abfd)) {
value = bfd_get_32 (abfd, data);
}
else {
value = ((bfd_get_8 (abfd, data) & 255) << 16);
value |= ((bfd_get_8 (abfd, data + 1) & 255) << 24);
value |= (bfd_get_8 (abfd, data + 2) & 255);
value |= ((bfd_get_8 (abfd, data + 3) & 255) << 8);
}
return value;
}
static void
bfd_put_32_me (bfd *abfd, bfd_vma value,unsigned char *data)
{
bfd_put_16 (abfd, (value & 0xffff0000) >> 16, data);
bfd_put_16 (abfd, value & 0xffff, data + 2);
}
/* Handle PC relative relocation */
bfd_reloc_status_type
arc_elf_b22_pcrel (bfd *abfd ATTRIBUTE_UNUSED,
arelent *reloc_entry,
asymbol *symbol,
PTR data ATTRIBUTE_UNUSED,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
/* If incremental linking, update the address of the relocation with the
section offset */
if (output_bfd != (bfd *) NULL)
{
reloc_entry->address += input_section->output_offset;
if ((symbol->flags & BSF_SECTION_SYM) && symbol->section)
reloc_entry->addend += ((**(reloc_entry->sym_ptr_ptr)).section)->output_offset;
return bfd_reloc_ok;
}
return bfd_reloc_continue;
}
/* ******************************************
* PIC-related routines for the arc backend
* ******************************************/
/* This will be overridden by the interpreter ld-linux.so specified in
the linker specs */
#define ELF_DYNAMIC_INTERPRETER "/sbin/ld-uClibc-0.9.26.so"
/*"/usr/local/arc/lib/libc.so.1"*/
/* size of one plt entry */
#define PLT_ENTRY_SIZE 12
/* The zeroth entry in the absolute plt entry */
static const bfd_byte elf_arc_abs_plt0_entry [2 * PLT_ENTRY_SIZE] =
{
0x00, 0x16, /* ld %r11, [0] */
0x0b, 0x70,
0x00, 0x00,
0x00, 0x00,
0x00, 0x16, /* ld %r10, [0] */
0x0a, 0x70, /* */
0,0,
0,0,
0x20, 0x20, /* j [%r10] */
0x80, 0x02, /* ---"---- */
0x00, 0x00, /* pad */
0x00, 0x00 /* pad */
};
/* Contents of the subsequent entries in the absolute plt */
static const bfd_byte elf_arc_abs_pltn_entry [PLT_ENTRY_SIZE] =
{
0x30, 0x27, /* ld %r12, [%pc,func@gotpc] */
0x8c, 0x7f, /* ------ " " -------------- */
0x00, 0x00, /* ------ " " -------------- */
0x00, 0x00, /* ------ " " -------------- */
0x20, 0x7c, /* j_s.d [%r12] */
0xef, 0x74, /* mov_s %r12, %pcl */
};
/* The zeroth entry in the pic plt entry */
static const bfd_byte elf_arc_pic_plt0_entry [2 * PLT_ENTRY_SIZE] =
{
0x30, 0x27, /* ld %r11, [pcl,0] : 0 to be replaced by _DYNAMIC@GOTPC+4 */
0x8b, 0x7f,
0x00, 0x00,
0x00, 0x00,
0x30, 0x27, /* ld %r10, [pcl,0] : 0 to be replaced by -DYNAMIC@GOTPC+8 */
0x8a, 0x7f, /* */
0,0,
0,0,
0x20, 0x20, /* j [%r10] */
0x80, 0x02, /* ---"---- */
0x00, 0x00, /* pad */
0x00, 0x00 /* pad */
};
/* Contents of the subsequent entries in the pic plt */
static const bfd_byte elf_arc_pic_pltn_entry [PLT_ENTRY_SIZE] =
{
0x30, 0x27, /* ld %r12, [%pc,func@got] */
0x8c, 0x7f, /* ------ " " -------------- */
0x00, 0x00, /* ------ " " -------------- */
0x00, 0x00, /* ------ " " -------------- */
0x20, 0x7c, /* j_s.d [%r12] */
0xef, 0x74, /* mov_s %r12, %pcl */
};
/* Function: arc_plugin_one_reloc
* Brief : Fill in the relocated value of the symbol into an insn
* depending on the relocation type. The instruction is
* assumed to have been read in the correct format (ME / LE/ BE)
* Args : 1. insn : the original insn into which the relocated
* value has to be filled in.
* 2. r_type : the relocation type.
* 3. value : the value to be plugged in the insn.
* Returns : the insn with the relocated value plugged in.
*/
/* FIXME::: Ramana this whole function needs to check for overflows. */
static unsigned long
arc_plugin_one_reloc (unsigned long insn, enum elf_arc_reloc_type r_type,
int value)
{
unsigned long offset;
switch(r_type)
{
case R_ARC_B26:
/* Retrieve the offset from the instruction, if any */
/* Extract the last 24 bits from Position 0 to 23 in insn */
offset = insn & 0x00ffffff;
/* Fill in 2 bit to get the 26 bit Offset Value */
offset = offset << 2;
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xff000000;
/* With the addend now being in the addend table, there is no
* need to use this
*/
/* Ramana : No longer required since
* addends no longer exist in place
*/
/* value += offset; */
insn |= ((value >> 2) & (~0xff000000));
break;
case R_ARC_B22_PCREL:
/* Retrieve the offset from the instruction, if any */
/* Extract the first 10 bits from Position 6 to 15 in insn */
offset = ((insn << 5) >> 12);
/* Fill in 2 bit to get the 22 bit Offset Value */
offset = offset << 2;
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf800007f;
value += offset;
insn |= ((value >> 2) << 7) & (~0xf800007f);
break;
case R_ARC_S21H_PCREL:
/* Retrieve the offset from the instruction, if any */
/* Extract the first 10 bits from Position 6 to 15 in insn */
offset = ((insn << 16) >> 22) << 10;
/* Extract the remaining 10 bits from Position 17 to 26 in insn */
offset |= ((insn << 5) >> 22);
/* Fill in 1 bit to get the 21 bit Offset Value */
offset = offset << 1;
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf801003f;
/* Ramana: All addends exist in the relocation table. Ignore
* the in place addend
*/
/*value += offset; */
insn |= ((((value >> 1) & 0x3ff) << 17)
| (((value >> 1) & 0xffc00) >> 4));
break;
case R_ARC_S21W_PCREL:
/* Retrieve the offset from the instruction, if any */
/* Extract the first 10 bits from Position 6 to 15 in insn */
offset = ((insn << 16) >> 22) << 9;
/* Extract the remaining 9 bits from Position 18 to 26 in insn */
offset |= ((insn << 5) >> 23);
/* Fill in 2 bits to get the 25 bit Offset Value */
offset = offset << 2;
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf803003f;
/* Ramana: All addends exist in the relocation table. Ignore
* the in place addend
*/
/*value += offset;*/
insn |= ((((value >> 2) & 0x1ff) << 18)
| (((value >> 2) & 0x7fe00) >> 3));
break;
case R_ARC_S25H_PCREL:
/* Retrieve the offset from the instruction, if any */
/* Extract the high 4 bits from Position 0 to 3 in insn */
offset = ((insn << 28) >> 28) << 10;
/* Extract the next 10 bits from Position 6 to 15 in insn */
offset |= ((insn << 16) >> 22);
offset = offset << 10;
/* Extract the remaining 10 bits from Position 17 to 26 in insn */
offset |= ((insn << 5) >> 22);
/* Fill in 1 bit to get the 25 bit Offset Value */
offset = offset << 1;
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf8010030;
/* Ramana: All addends exist in the relocation table. Ignore
* the in place addend
*/
/* value += offset; */
insn |= ((((value >> 1) & 0x3ff) << 17)
| (((value >> 1) & 0xffc00) >> 4)
| (((value >> 1) & 0xf00000) >> 20));
break;
case R_ARC_PLT32:
BFD_DEBUG_PIC (fprintf(stderr,"plt for %x value=0x%x\n",insn,value));
/*
Relocations of the type R_ARC_PLT32 are for the BLcc
instructions. However the BL instruction takes a 25-bit relative
displacement while the BLcc instruction takes a 21-bit relative
displacement. We are using bit-17 to distinguish between these two
cases and handle them differently.
*/
if(insn & 0x00020000) /* Non-conditional */
{
insn = insn & 0xf8030030;
insn |= (((value >> 2) & 0x780000) >> 19);
}
else /* Conditional */
{
insn = insn & 0xf803003f;
}
insn |= ((((value >> 2) & 0x1ff) << 18)
| (((value >> 2) & 0x7fe00) >> 3));
break;
case R_ARC_S25W_PCREL:
/* Retrieve the offset from the instruction, if any */
/* Extract the high 4 bits from Position 0 to 3 in insn */
offset = ((insn << 28) >> 28) << 10;
/* Extract the next 10 bits from Position 6 to 15 in insn */
offset |= ((insn << 16) >> 22);
offset = offset << 9;
/* Extract the remaining 9 bits from Position 18 to 26 in insn */
offset |= ((insn << 5) >> 23);
/* Fill in 2 bits to get the 25 bit Offset Value */
offset = offset << 2;
/* Extract the instruction opcode alone from 'insn' */
insn = insn & 0xf8030030;
/* Ramana: All addends exist in the relocation table. Ignore
* the in place addend
*/
/* value += offset; */
insn |= ((((value >> 2) & 0x1ff) << 18)
| (((value >> 2) & 0x7fe00) >> 3)
| (((value >> 2) & 0x780000) >> 19));
break;
case R_ARC_S13_PCREL:
/* Retrieve the offset from the instruction, if any */
/* Extract the 11 bits from Position 0 to 10 in insn */
offset = (insn << 5) >> 21;
/* Fill in 2 bits to get the 13 bit Offset Value */
offset = offset << 2;
/* Extract the instruction opcode alone from 'insn' */
insn = (insn & 0xf800ffff);
/* Ramana: All addends exist in the relocation table. Ignore
* the in place addend
*/
/* value += offset; */
insn |= ((value >> 2) & 0x7ff) << 16;
break;
case R_ARC_32:
case R_ARC_GOTPC:
case R_ARC_GOTOFF:
case R_ARC_GOTPC32:
case R_ARC_32_ME:
insn = value;
case R_ARC_8:
case R_ARC_16:
case R_ARC_24:
/* One would have to OR the value here since
insn would contain the bits read in correctly. */
insn |= value ;
break;
default:
/* FIXME:: This should go away once the HOWTO Array
is used for this purpose.
*/
break;
}
return insn;
}
/* Function : elf_arc_check_relocs
* Brief : Check the relocation entries and take any special
* actions, depending on the relocation type if needed.
* Args : 1. abfd : The input bfd
* 2. info : link information
* 3. sec : section being relocated
* 4. relocs : the list of relocations.
* Returns : True/False as the return status.
*/
static bfd_boolean
elf_arc_check_relocs (bfd *abfd,
struct bfd_link_info *info,
asection *sec,
const Elf_Internal_Rela *relocs)
{
bfd *dynobj;
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;
asection *sgot;
asection *srelgot;
asection *sreloc;
if (info->relocatable)
return TRUE;
dynobj = elf_hash_table (info)->dynobj;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_offsets = elf_local_got_offsets (abfd);
sgot = NULL;
srelgot = NULL;
sreloc = NULL;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
unsigned long r_symndx;
struct elf_link_hash_entry *h;
BFD_DEBUG_PIC (fprintf(stderr,"Processing reloc #%d in %s\n",
rel-relocs,__PRETTY_FUNCTION__));
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
/* Some relocs require a global offset table. */
if (dynobj == NULL)
{
switch (ELF32_R_TYPE (rel->r_info))
{
case R_ARC_GOTPC32:
case R_ARC_GOTOFF:
case R_ARC_GOTPC:
elf_hash_table (info)->dynobj = dynobj = abfd;
if (! _bfd_elf_create_got_section (dynobj, info))
return FALSE;
break;
default:
break;
}
}
switch (ELF32_R_TYPE (rel->r_info))
{
case R_ARC_GOTPC32:
/* This symbol requires a global offset table entry. */
if (sgot == NULL)
{
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
if (srelgot == NULL
&& (h != NULL || info->shared))
{
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
if (srelgot == NULL)
{
srelgot = bfd_make_section (dynobj, ".rela.got");
if (srelgot == NULL
|| ! bfd_set_section_flags (dynobj, srelgot,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED
| SEC_READONLY))
|| ! bfd_set_section_alignment (dynobj, srelgot, 2))
return FALSE;
}
}
if (h != NULL)
{
if (h->got.offset != (bfd_vma) -1)
{
BFD_DEBUG_PIC(fprintf(stderr, "got entry stab entry already done%d\n",r_symndx));
/* We have already allocated space in the .got. */
break;
}
h->got.offset = sgot->rawsize;
BFD_DEBUG_PIC(fprintf(stderr, "got entry stab entry %d got offset=0x%x\n",r_symndx,
h->got.offset));
/* Make sure this symbol is output as a dynamic symbol. */
if (h->dynindx == -1)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
BFD_DEBUG_PIC(fprintf (stderr, "Got raw size increased\n"));
srelgot->rawsize += sizeof (Elf32_External_Rela);
}
else
{
/* This is a global offset table entry for a local
symbol. */
if (local_got_offsets == NULL)
{
size_t size;
register unsigned int i;
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;
}
if (local_got_offsets[r_symndx] != (bfd_vma) -1)
{
BFD_DEBUG_PIC(fprintf(stderr, "got entry stab entry already done%d\n",r_symndx));
/* We have already allocated space in the .got. */
break;
}
BFD_DEBUG_PIC(fprintf(stderr, "got entry stab entry %d\n",r_symndx));
local_got_offsets[r_symndx] = sgot->rawsize;
if (info->shared)
{
/* If we are generating a shared object, we need to
output a R_ARC_RELATIVE reloc so that the dynamic
linker can adjust this GOT entry. */
srelgot->rawsize += sizeof (Elf32_External_Rela);
}
}
BFD_DEBUG_PIC(fprintf (stderr, "Got raw size increased\n"));
sgot->rawsize += 4;
break;
case R_ARC_PLT32:
/* This symbol requires a procedure linkage table entry. We
actually build the entry in adjust_dynamic_symbol,
because this might be a case of linking PIC code which is
never referenced by a dynamic object, in which case we
don't need to generate a procedure linkage table entry
after all. */
/* If this is a local symbol, we resolve it directly without
creating a procedure linkage table entry. */
if (h == NULL)
continue;
h->needs_plt = 1;
break;
case R_ARC_32:
case R_ARC_32_ME:
case R_ARC_PC32:
/* If we are creating a shared library, and this is a reloc
against a global symbol, or a non PC relative reloc
against a local symbol, then we need to copy the reloc
into the shared library. However, if we are linking with
-Bsymbolic, we do not need to copy a reloc against a
global symbol which is defined in an object we are
including in the link (i.e., DEF_REGULAR is set). At
this point we have not seen all the input files, so it is
possible that DEF_REGULAR is not set now but will be set
later (it is never cleared). We account for that
possibility below by storing information in the
pcrel_relocs_copied field of the hash table entry. */
if (info->shared
&& (ELF32_R_TYPE (rel->r_info) != R_ARC_PC32
|| (h != NULL
&& (! info->symbolic
|| (h->def_regular) == 0))))
{
/* When creating a shared object, we must copy these
reloc types into the output file. We create a reloc
section in dynobj and make room for this reloc. */
if (sreloc == NULL)
{
const char *name;
name = (bfd_elf_string_from_elf_section
(abfd,
elf_elfheader (abfd)->e_shstrndx,
elf_section_data (sec)->rel_hdr.sh_name));
if (name == NULL)
return FALSE;
BFD_ASSERT (strncmp (name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (abfd, sec),
name + 5) == 0);
sreloc = bfd_get_section_by_name (dynobj, name);
if (sreloc == NULL)
{
flagword flags;
sreloc = bfd_make_section (dynobj, name);
flags = (SEC_HAS_CONTENTS | SEC_READONLY
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
if ((sec->flags & SEC_ALLOC) != 0)
flags |= SEC_ALLOC | SEC_LOAD;
if (sreloc == NULL
|| ! bfd_set_section_flags (dynobj, sreloc, flags)
|| ! bfd_set_section_alignment (dynobj, sreloc, 2))
return FALSE;
}
}
sreloc->rawsize += sizeof (Elf32_External_Rela);
/* If we are linking with -Bsymbolic, and this is a
global symbol, we count the number of PC relative
relocations we have entered for this symbol, so that
we can discard them again if the symbol is later
defined by a regular object. Note that this function
is only called if we are using an elf_ARC linker
hash table, which means that h is really a pointer to
an elf_ARC_link_hash_entry. */
if (h != NULL && info->symbolic
&& ELF32_R_TYPE (rel->r_info) == R_ARC_PC32)
{
struct elf_ARC_link_hash_entry *eh;
struct elf_ARC_pcrel_relocs_copied *p;
eh = (struct elf_ARC_link_hash_entry *) h;
for (p = eh->pcrel_relocs_copied; p != NULL; p = p->next)
if (p->section == sreloc)
break;
if (p == NULL)
{
p = ((struct elf_ARC_pcrel_relocs_copied *)
bfd_alloc (dynobj, sizeof *p));
if (p == NULL)
return FALSE;
p->next = eh->pcrel_relocs_copied;
eh->pcrel_relocs_copied = p;
p->section = sreloc;
p->count = 0;
}
++p->count;
}
}
break;
default:
break;
}
}
return TRUE;
}
/* 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.
* Returns :
*/
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)
{
bfd *dynobj;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_vma *local_got_offsets;
asection *sgot;
asection *splt;
asection *sreloc;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
dynobj = elf_hash_table (info)->dynobj;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
local_got_offsets = elf_local_got_offsets (input_bfd);
sgot = NULL;
splt = NULL;
sreloc = NULL;
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;
bfd_vma relocation;
bfd_reloc_status_type r;
/* Distance of the relocation slot in the insn .This value is used for
handling relative relocations. */
long offset_in_insn = 0;
/* The insn bytes */
unsigned long insn;
r_type = ELF32_R_TYPE (rel->r_info);
if (r_type >= (int) R_ARC_max)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
howto = arc_elf_calculate_howto_index(r_type);
BFD_DEBUG_PIC (fprintf(stderr,"Reloc type=%s in %s\n",
howto->name,
__PRETTY_FUNCTION__));
r_symndx = ELF32_R_SYM (rel->r_info);
if (info->relocatable)
{
/* This is a relocateable link. We don't have to change
anything, unless the reloc is against a section symbol,
in which case we have to adjust according to where the
section symbol winds up in the output section. */
/* Checks if this is a local symbol
* and thus the reloc might (will??) be against a section symbol.
*/
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
{
sec = local_sections[r_symndx];
/* for RELA relocs.Just adjust the addend
value in the relocation entry. */
rel->r_addend += sec->output_offset + sym->st_value;
BFD_DEBUG_PIC(fprintf (stderr, "local symbols reloc \
(section=%d %s) seen in %s\n", r_symndx, local_sections[r_symndx]->name, \
__PRETTY_FUNCTION__));
}
}
continue;
}
/* This is a final link. */
h = NULL;
sym = NULL;
sec = NULL;
if (r_symndx < symtab_hdr->sh_info)
{
/* This is a local symbol */
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
relocation = (sec->output_section->vma
+ sec->output_offset
+ sym->st_value);
/* Mergeable section handling */
if ((sec->flags & SEC_MERGE)
&& ELF_ST_TYPE (sym->st_info) == STT_SECTION)
{
asection *msec;
msec = sec;
rel->r_addend = _bfd_elf_rel_local_sym (output_bfd, sym,
&msec, rel->r_addend);
rel->r_addend -= relocation;
rel->r_addend += msec->output_section->vma + msec->output_offset;
}
relocation += rel->r_addend;
}
else
{
/* Global symbols */
/* 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;
/* if we have encountered a definition for this symbol */
if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
sec = h->root.u.def.section;
if (r_type == R_ARC_GOTPC
|| (r_type == R_ARC_PLT32
&& h->plt.offset != (bfd_vma) -1)
|| (r_type == R_ARC_GOTPC32
&& elf_hash_table (info)->dynamic_sections_created
&& (! info->shared
|| (! info->symbolic && h->dynindx != -1)
|| (h->def_regular) == 0))
|| (info->shared
&& ((! info->symbolic && h->dynindx != -1)
|| (h->def_regular) == 0)
&& (r_type == R_ARC_32
|| r_type == R_ARC_PC32)
&& (input_section->flags & SEC_ALLOC) != 0))
{
/* In these cases, we don't need the relocation
value. We check specially because in some
obscure cases sec->output_section will be NULL. */
relocation = 0;
}
else if (sec->output_section == NULL)
{
(*_bfd_error_handler)
("%s: warning: unresolvable relocation against symbol `%s' from %s section",
bfd_get_filename (input_bfd), h->root.root.string,
bfd_get_section_name (input_bfd, input_section));
relocation = 0;
}
else
{
relocation = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
/* add the addend since the arc has RELA relocations */
relocation += rel->r_addend;
}
}
else if (h->root.type == bfd_link_hash_undefweak)
relocation = 0;
else if (info->shared && !info->symbolic)
relocation = 0;
else
{
if (! ((*info->callbacks->undefined_symbol)
(info, h->root.root.string,
input_bfd, input_section, rel->r_offset, TRUE)))
return FALSE;
relocation = 0;
}
}
BFD_DEBUG_PIC ( fprintf (stderr, "Relocation = %d (%x)\n", relocation, relocation));
switch (r_type)
{
case R_ARC_GOTPC32:
/* Relocation is to the entry for this symbol in the global
offset table. */
if (sgot == NULL)
{
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_DEBUG_PIC (fprintf (stderr, "made got\n"));
BFD_ASSERT (sgot != NULL);
}
if (h != NULL)
{
bfd_vma off;
off = h->got.offset;
BFD_ASSERT (off != (bfd_vma) -1);
if (! elf_hash_table (info)->dynamic_sections_created
|| (info->shared
&& (info->symbolic || h->dynindx == -1)
&& (h->def_regular)))
{
/* This is actually a static link, or it is a
-Bsymbolic link and the symbol is defined
locally, or the symbol was forced to be local
because of a version file. We must initialize
this entry in the global offset table. Since the
offset must always be a multiple of 4, we use the
least significant bit to record whether we have
initialized it already.
When doing a dynamic link, we create a .rela.got
relocation entry to initialize the value. This
is done in the finish_dynamic_symbol routine. */
if ((off & 1) != 0)
off &= ~1;
else
{
bfd_put_32 (output_bfd, relocation,
sgot->contents + off);
h->got.offset |= 1;
}
}
relocation = sgot->output_section->vma + sgot->output_offset + off;
BFD_DEBUG_PIC(fprintf(stderr, "OFFSET=0x%x output_offset=%x (1)\n", off, sgot->output_offset));
}
else
{
bfd_vma off;
BFD_ASSERT (local_got_offsets != NULL
&& local_got_offsets[r_symndx] != (bfd_vma) -1);
off = local_got_offsets[r_symndx];
/* The offset must always be a multiple of 4. We use
the least significant bit to record whether we have
already generated the necessary reloc. */
if ((off & 1) != 0)
off &= ~1;
else
{
bfd_put_32 (output_bfd, relocation,
sgot->contents + off);
if (info->shared)
{
asection *srelgot;
Elf_Internal_Rela outrel;
bfd_byte *loc;
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
BFD_ASSERT (srelgot != NULL);
outrel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ off);
/* RELA relocs */
outrel.r_addend = 0;
outrel.r_info = ELF32_R_INFO (0, R_ARC_RELATIVE);
loc = srelgot->contents;
loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rela); /* relA */
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
}
local_got_offsets[r_symndx] |= 1;
}
relocation = sgot->output_section->vma + sgot->output_offset + off;
BFD_DEBUG_PIC(fprintf(stderr, "OFFSET=0x%x (2)\n", off));
}
BFD_DEBUG_PIC(fprintf(stderr, "RELOCATION =%x\n",relocation));
/* the data in GOTPC32 relocs is 4 bytes into the insn */
offset_in_insn = 4;
break;
case R_ARC_GOTOFF:
/* Relocation is relative to the start of the global offset
table. */
if (sgot == NULL)
{
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
/* Note that sgot->output_offset is not involved in this
calculation. We always want the start of .got. If we
defined _GLOBAL_OFFSET_TABLE in a different way, as is
permitted by the ABI, we might have to change this
calculation. */
BFD_DEBUG_PIC(fprintf(stderr,"GOTOFF relocation = %x. Subtracting %x\n",relocation, sgot->output_section->vma));
relocation -= sgot->output_section->vma;
break;
case R_ARC_GOTPC:
/* Use global offset table as symbol value. */
if (sgot == NULL)
{
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
relocation = sgot->output_section->vma;
offset_in_insn = 4;
break;
case R_ARC_PLT32:
/* Relocation is to the entry for this symbol in the
procedure linkage table. */
/* Resolve a PLT32 reloc again a local symbol directly,
without using the procedure linkage table. */
if (h == NULL)
break;
if (h->plt.offset == (bfd_vma) -1)
{
/* We didn't make a PLT entry for this symbol. This
happens when statically linking PIC code, or when
using -Bsymbolic. */
break;
}
if (splt == NULL)
{
splt = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (splt != NULL);
}
relocation = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
break;
case R_ARC_32:
case R_ARC_32_ME:
case R_ARC_PC32:
if (info->shared
&& (r_type != R_ARC_PC32
|| (h != NULL
&& h->dynindx != -1
&& (! info->symbolic
|| (h->def_regular) == 0))))
{
Elf_Internal_Rela outrel;
bfd_boolean skip, relocate;
bfd_byte *loc;
/* When generating a shared object, these relocations
are copied into the output file to be resolved at run
time. */
if (sreloc == NULL)
{
const char *name;
name = (bfd_elf_string_from_elf_section
(input_bfd,
elf_elfheader (input_bfd)->e_shstrndx,
elf_section_data (input_section)->rel_hdr.sh_name));
if (name == NULL)
return FALSE;
BFD_ASSERT (strncmp (name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (input_bfd,
input_section),
name + 5) == 0);
sreloc = bfd_get_section_by_name (dynobj, name);
BFD_ASSERT (sreloc != NULL);
}
skip = FALSE;
outrel.r_offset = _bfd_elf_section_offset (output_bfd,
info,
input_section,
rel->r_offset);
if (outrel.r_offset == (bfd_vma) -1)
skip = TRUE;
outrel.r_addend = 0;
outrel.r_offset += (input_section->output_section->vma
+ input_section->output_offset);
if (skip)
{
memset (&outrel, 0, sizeof outrel);
relocate = FALSE;
}
else if (r_type == R_ARC_PC32)
{
BFD_ASSERT (h != NULL && h->dynindx != -1);
if ((input_section->flags & SEC_ALLOC) != 0)
relocate = FALSE;
else
relocate = TRUE;
outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_PC32);
}
else
{
/* h->dynindx may be -1 if this symbol was marked to
become local. */
if (h == NULL
|| ((info->symbolic || h->dynindx == -1)
&& (h->def_regular) != 0))
{
relocate = TRUE;
outrel.r_addend = 0;
outrel.r_info = ELF32_R_INFO (0, R_ARC_RELATIVE);
}
else
{
BFD_ASSERT (h->dynindx != -1);
if ((input_section->flags & SEC_ALLOC) != 0)
relocate = FALSE;
else
relocate = TRUE;
outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_32);
}
}
BFD_ASSERT(sreloc->contents != 0);
loc = sreloc->contents;
loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela); /* relA */
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
/* If this reloc is against an external symbol, we do
not want to fiddle with the addend. Otherwise, we
need to include the symbol value so that it becomes
an addend for the dynamic reloc. */
if (! relocate)
continue;
}
/* PLT32 has to be w.r.t the instruction's start */
offset_in_insn = 0;
break;
case R_ARC_B22_PCREL:
/* 'offset_in_insn' in case of the A4 is from the instruction in
the delay slot of the branch instruction hence the -4 offset. */
offset_in_insn = -4;
break;
default:
break;
}
/* get the insn bytes here */
if(elf_elfheader(input_bfd)->e_machine == EM_ARC)
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
else
if(input_section && (input_section->flags & SEC_CODE))
insn = bfd_get_32_me (input_bfd, contents + rel->r_offset);
else
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
BFD_DEBUG_PIC(fprintf(stderr, "relocation before the pc relative stuff @offset 0x%x= %d[0x%x]\n",
rel->r_offset,relocation, relocation));
BFD_DEBUG_PIC(fprintf(stderr,"addend = 0x%x\n",rel->r_addend));
/* For branches we need to find the offset from pcl rounded down to 4 byte boundary.Hence the (& ~3) */
if (howto->pc_relative || r_type==R_ARC_PLT32 || r_type==R_ARC_GOTPC)
{
relocation -= (((input_section->output_section->vma + input_section->output_offset +
rel->r_offset) & ~3) - offset_in_insn );
}
else if (r_type==R_ARC_GOTPC32)
{
relocation -= (input_section->output_section->vma +
input_section->output_offset + rel->r_offset
- offset_in_insn );
}
BFD_DEBUG_PIC(fprintf(stderr, \
"relocation AFTER the pc relative \
handling = %d[0x%x]\n", relocation, relocation));
/* What does the modified insn look like */
insn = arc_plugin_one_reloc (insn, r_type, relocation);
BFD_DEBUG_PIC (fprintf (stderr, "Relocation = %d [0x%x]\n", \
relocation, relocation));
/* now write back into the section, with middle endian encoding
only for executable section */
if(elf_elfheader(input_bfd)->e_machine == EM_ARC)
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
else
if (input_section && (input_section->flags & SEC_CODE))
bfd_put_32_me (input_bfd, insn, contents + rel->r_offset);
else
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
r = bfd_reloc_ok;
if (r != bfd_reloc_ok)
{
switch (r)
{
default:
case bfd_reloc_outofrange:
abort ();
case bfd_reloc_overflow:
{
const char *name;
if (h != NULL)
name = h->root.root.string;
else
{
name = bfd_elf_string_from_elf_section (input_bfd,
symtab_hdr->sh_link,
sym->st_name);
if (name == NULL)
return FALSE;
if (*name == '\0')
name = bfd_section_name (input_bfd, sec);
}
/* <irf-todo>
if (! ((*info->callbacks->reloc_overflow)
(info, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
return FALSE;*/
}
break;
}
}
}
return TRUE;
}
/* 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)
{
bfd *dynobj;
dynobj = elf_hash_table (info)->dynobj;
if (h->plt.offset != (bfd_vma) -1)
{
asection *splt;
asection *sgot;
asection *srel;
bfd_vma plt_index;
bfd_vma got_offset;
Elf_Internal_Rela rel;
bfd_byte *loc;
/* This symbol has an entry in the procedure linkage table. Set
it up. */
BFD_ASSERT (h->dynindx != -1);
splt = bfd_get_section_by_name (dynobj, ".plt");
sgot = bfd_get_section_by_name (dynobj, ".got.plt");
srel = bfd_get_section_by_name (dynobj, ".rela.plt");
BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL);
/* Get the index in the procedure linkage table which
corresponds to this symbol. This is the index of this symbol
in all the symbols for which we are making plt entries. The
first TWO entries in the procedure linkage table are reserved. */
plt_index = h->plt.offset / PLT_ENTRY_SIZE - 2;
/* Get the offset into the .got table of the entry that
corresponds to this function. Each .got entry is 4 bytes.
The first three are reserved. */
got_offset = (plt_index + 3) * 4;
/* Fill in the entry in the procedure linkage table. */
if (! info->shared)
{
memcpy (splt->contents + h->plt.offset, elf_arc_abs_pltn_entry,
PLT_ENTRY_SIZE);
/* fill in the limm in the plt entry to make it jump through its corresponding *(gotentry) */
bfd_put_32_me (output_bfd,
(sgot-> output_section->vma + sgot->output_offset + got_offset)
-(splt->output_section->vma + splt->output_offset + h->plt.offset),
splt->contents + h->plt.offset + 4);
}
else
{
memcpy (splt->contents + h->plt.offset, elf_arc_pic_pltn_entry,
PLT_ENTRY_SIZE);
/* fill in the limm in the plt entry to make it jump through its corresponding *(gotentry) */
bfd_put_32_me (output_bfd,
(sgot-> output_section->vma + sgot->output_offset + got_offset)
-(splt->output_section->vma + splt->output_offset + h->plt.offset),
splt->contents + h->plt.offset + 4);
}
/* Fill in the entry in the global offset table. */
bfd_put_32 (output_bfd,
(splt->output_section->vma
+ splt->output_offset),
sgot->contents + got_offset);
/* Fill in the entry in the .rela.plt section. */
rel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ got_offset);
/* RELA relocs */
rel.r_addend = 0;
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_JMP_SLOT);
loc = srel->contents;
loc += plt_index * sizeof (Elf32_External_Rela); /* relA */
bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
if ((h->def_regular) == 0)
{
/* Mark the symbol as undefined, rather than as defined in
the .plt section. Leave the value alone. */
sym->st_shndx = SHN_UNDEF;
}
}
if (h->got.offset != (bfd_vma) -1)
{
asection *sgot;
asection *srel;
Elf_Internal_Rela rel;
bfd_byte *loc;
/* This symbol has an entry in the global offset table. Set it
up. */
sgot = bfd_get_section_by_name (dynobj, ".got");
srel = bfd_get_section_by_name (dynobj, ".rela.got");
BFD_ASSERT (sgot != NULL && srel != NULL);
rel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ (h->got.offset &~ 1));
/* If this is a -Bsymbolic link, and the symbol is defined
locally, we just want to emit a RELATIVE reloc. Likewise if
the symbol was forced to be local because of a version file.
The entry in the global offset table will already have been
initialized in the relocate_section function. */
if (info->shared
&& (info->symbolic || h->dynindx == -1)
&& (h->def_regular))
{
rel.r_addend = 0;
rel.r_info = ELF32_R_INFO (0, R_ARC_RELATIVE);
}
else
{
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
/* RELA relocs */
rel.r_addend = 0;
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_GLOB_DAT);
}
loc = srel->contents;
loc += srel->reloc_count++ * sizeof (Elf32_External_Rel);
bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
}
if ((h->needs_copy) != 0)
{
asection *s;
Elf_Internal_Rela rel;
bfd_byte *loc;
/* This symbol needs a copy reloc. Set it up. */
BFD_ASSERT (h->dynindx != -1
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak));
s = bfd_get_section_by_name (h->root.u.def.section->owner,
".rela.bss");
BFD_ASSERT (s != NULL);
rel.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARC_COPY);
loc = s->contents;
loc += s->reloc_count++ * sizeof (Elf32_External_Rela); /* relA */
bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
}
/* 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;
}
/* 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)
{
bfd *dynobj;
asection *sgot;
asection *sdyn;
asection *sec_ptr;
char * oldname;
dynobj = elf_hash_table (info)->dynobj;
sgot = bfd_get_section_by_name (dynobj, ".got.plt");
BFD_ASSERT (sgot != NULL);
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (elf_hash_table (info)->dynamic_sections_created)
{
asection *splt;
Elf32_External_Dyn *dyncon, *dynconend;
splt = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (splt != NULL && sdyn != NULL);
dyncon = (Elf32_External_Dyn *) sdyn->contents;
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->rawsize);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
const char *name;
asection *s;
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
default:
break;
case DT_INIT:
oldname = INIT_SYM_STRING;
name = init_str;
goto get_sym;
case DT_FINI:
oldname = FINI_SYM_STRING;
name = fini_str;
goto get_sym;
get_sym:
{
struct elf_link_hash_entry *h;
h = elf_link_hash_lookup (elf_hash_table (info), name,
FALSE, FALSE, TRUE);
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
dyn.d_un.d_val = h->root.u.def.value;
sec_ptr = h->root.u.def.section;
if (sec_ptr->output_section != NULL)
dyn.d_un.d_val += (sec_ptr->output_section->vma
+ sec_ptr->output_offset);
else
{
/* The symbol is imported from another shared
library and does not apply to this one. */
dyn.d_un.d_val = 0;
}
bfd_elf32_swap_dyn_out (dynobj, &dyn, dyncon);
}
else
{
(*_bfd_error_handler)
("warning: specified init/fini symbol %s not found.Defaulting to address of symbol %s", name, oldname);
/* restore the default name */
name = oldname;
h = elf_link_hash_lookup (elf_hash_table (info), name,
FALSE, FALSE, TRUE);
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
dyn.d_un.d_val = h->root.u.def.value;
sec_ptr = h->root.u.def.section;
if (sec_ptr->output_section != NULL)
dyn.d_un.d_val += (sec_ptr->output_section->vma
+ sec_ptr->output_offset);
else
{
/* The symbol is imported from another shared
library and does not apply to this one. */
dyn.d_un.d_val = 0;
}
bfd_elf32_swap_dyn_out (dynobj, &dyn, dyncon);
}
}
}
break;
case DT_PLTGOT:
name = ".plt";
goto get_vma;
case DT_JMPREL:
name = ".rela.plt";
get_vma:
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_PLTRELSZ:
s = bfd_get_section_by_name (output_bfd, ".rela.plt");
BFD_ASSERT (s != NULL);
if (s->size != 0)
dyn.d_un.d_val = s->size;
else
dyn.d_un.d_val = s->rawsize;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_RELASZ:
/* My reading of the SVR4 ABI indicates that the
procedure linkage table relocs (DT_JMPREL) should be
included in the overall relocs (DT_REL). This is
what Solaris does. However, UnixWare can not handle
that case. Therefore, we override the DT_RELASZ entry
here to make it not include the JMPREL relocs. Since
the linker script arranges for .rela.plt to follow all
other relocation sections, we don't have to worry
about changing the DT_REL entry. */
s = bfd_get_section_by_name (output_bfd, ".rela.plt");
if (s != NULL)
{
if (s->size != 0)
dyn.d_un.d_val -= s->size;
else
dyn.d_un.d_val -= s->rawsize;
}
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
}
}
/* Fill in the first entry in the procedure linkage table. */
if (splt->rawsize > 0)
{
if (info->shared)
{
memcpy (splt->contents, elf_arc_pic_plt0_entry, 2 * PLT_ENTRY_SIZE);
/* fill in the _DYNAMIC@GOTPC+4 and _DYNAMIC@GOTPC+8 at PLT0+4 and PLT0+12 */
bfd_put_32_me (output_bfd,
( sgot->output_section->vma + sgot->output_offset + 4 )
-(splt->output_section->vma + splt->output_offset ),
splt->contents + 4);
bfd_put_32_me (output_bfd,
(sgot->output_section->vma + sgot->output_offset + 8)
-(splt->output_section->vma + splt->output_offset +8),
splt->contents + 12);
/* put got base at plt0+12 */
bfd_put_32 (output_bfd,
(sgot->output_section->vma + sgot->output_offset),
splt->contents + 20);
}
else
{
memcpy (splt->contents, elf_arc_abs_plt0_entry, 2 * PLT_ENTRY_SIZE);
/* in the executable, fill in the exact got addresses
for the module id ptr (gotbase+4) and the dl resolve
routine (gotbase+8) in the middle endian format */
bfd_put_32_me (output_bfd,
sgot->output_section->vma + sgot->output_offset + 4,
splt->contents + 4);
bfd_put_32_me (output_bfd,
sgot->output_section->vma + sgot->output_offset + 8,
splt->contents + 12);
/* put got base at plt0+12 */
bfd_put_32 (output_bfd,
(sgot->output_section->vma + sgot->output_offset),
splt->contents + 20);
}
}
/* UnixWare sets the entsize of .plt to 4, although that doesn't
really seem like the right value. */
elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
}
/* Fill in the first three entries in the global offset table. */
if (sgot->rawsize > 0)
{
if (sdyn == NULL)
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents);
else
bfd_put_32 (output_bfd,
sdyn->output_section->vma + sdyn->output_offset,
sgot->contents);
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4);
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8);
}
elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
return TRUE;
}
/* 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;
asection *s;
unsigned int power_of_two;
dynobj = elf_hash_table (info)->dynobj;
/* Make sure we know what is going on here. */
BFD_ASSERT (dynobj != NULL
&& ((h->needs_plt)
|| h->u.weakdef != NULL
|| ((h->def_dynamic) != 0
&& (h->ref_regular) != 0
&& (h->def_regular) == 0)));
/* If this is a function, put it in the procedure linkage table. We
will fill in the contents of the procedure linkage table later,
when we know the address of the .got section. */
if (h->type == STT_FUNC
|| (h->needs_plt) != 0)
{
if (! info->shared
&& (h->def_dynamic) == 0
&& (h->ref_dynamic) == 0)
{
/* 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) != 0);
return TRUE;
}
/* Make sure this symbol is output as a dynamic symbol. */
if (h->dynindx == -1)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
s = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (s != NULL);
/* If this is the first .plt entry, make room for the special
first entry. */
if (s->rawsize == 0)
{
s->rawsize += 2 *PLT_ENTRY_SIZE;
BFD_DEBUG_PIC (fprintf (stderr, "first plt entry at %d\n", s->rawsize));
}
else {
BFD_DEBUG_PIC (fprintf (stderr, "Next plt entry at %d\n", s->rawsize));
printf("Unimplimented path: elf32-arc.c: <Irf-todo>");
}
/* If this symbol is not defined in a regular file, and we are
not generating a shared library, then set the symbol to this
location in the .plt. This is required to make function
pointers compare as equal between the normal executable and
the shared library. */
if (! info->shared
&& (h->def_regular) == 0)
{
h->root.u.def.section = s;
h->root.u.def.value = s->rawsize;
}
h->plt.offset = s->rawsize;
/* Make room for this entry. */
s->rawsize += PLT_ENTRY_SIZE;
/* We also need to make an entry in the .got.plt section, which
will be placed in the .got section by the linker script. */
s = bfd_get_section_by_name (dynobj, ".got.plt");
BFD_ASSERT (s != NULL);
s->rawsize += 4;
/* We also need to make an entry in the .rela.plt section. */
s = bfd_get_section_by_name (dynobj, ".rela.plt");
BFD_ASSERT (s != NULL);
s->rawsize += sizeof (Elf32_External_Rela);
return TRUE;
}
/* If this is a weak symbol, and there is a real definition, the
processor independent code will have arranged for us to see the
real definition first, and we can just use the same value. */
if (h->u.weakdef != NULL)
{
BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
|| h->u.weakdef->root.type == bfd_link_hash_defweak);
h->root.u.def.section = h->u.weakdef->root.u.def.section;
h->root.u.def.value = h->u.weakdef->root.u.def.value;
return TRUE;
}
/* This is a reference to a symbol defined by a dynamic object which
is not a function. */
/* If we are creating a shared library, we must presume that the
only references to the symbol are via the global offset table.
For such cases we need not do anything here; the relocations will
be handled correctly by relocate_section. */
if (info->shared)
return TRUE;
/* We must allocate the symbol in our .dynbss section, which will
become part of the .bss section of the executable. There will be
an entry for this symbol in the .dynsym section. The dynamic
object will contain position independent code, so all references
from the dynamic object to this symbol will go through the global
offset table. The dynamic linker will use the .dynsym entry to
determine the address it must put in the global offset table, so
both the dynamic object and the regular object will refer to the
same memory location for the variable. */
s = bfd_get_section_by_name (dynobj, ".dynbss");
BFD_ASSERT (s != NULL);
/* We must generate a R_ARC_COPY reloc to tell the dynamic linker to
copy the initial value out of the dynamic object and into the
runtime process image. We need to remember the offset into the
.rela.bss section we are going to use. */
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
{
asection *srel;
srel = bfd_get_section_by_name (dynobj, ".rela.bss");
BFD_ASSERT (srel != NULL);
srel->rawsize += sizeof (Elf32_External_Rela);
h->needs_copy = 1;
}
/* We need to figure out the alignment required for this symbol. I
have no idea how ELF linkers handle this. */
power_of_two = bfd_log2 (h->size);
if (power_of_two > 3)
power_of_two = 3;
/* Apply the required alignment. */
s->rawsize = BFD_ALIGN (s->rawsize,
(bfd_size_type) (1 << power_of_two));
if (power_of_two > bfd_get_section_alignment (dynobj, s))
{
if (! bfd_set_section_alignment (dynobj, s, power_of_two))
return FALSE;
}
/* Define the symbol as being at this point in the section. */
h->root.u.def.section = s;
h->root.u.def.value = s->rawsize;
/* Increment the section size to make room for the symbol. */
s->rawsize += h->size;
return TRUE;
}
/* 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 plt;
bfd_boolean relocs;
bfd_boolean reltext;
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 (! info->shared)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->rawsize = 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 / DT_FINE
* entries if the init_str has been changed by the user
*/
h = elf_link_hash_lookup (elf_hash_table (info), "init", FALSE,
FALSE, FALSE);
if ((h != NULL
&& (h->ref_regular || h->def_regular) != 0)
|| (strcmp(init_str,INIT_SYM_STRING) != 0))
{
/*Ravi: changed from bfd_elf32_add_dynamic_entry */
if (! _bfd_elf_add_dynamic_entry (info, DT_INIT, 0))
return FALSE;
}
h = elf_link_hash_lookup (elf_hash_table (info), "fini", FALSE,
FALSE, FALSE);
if ((h != NULL
&& (h->ref_regular || h->def_regular) != 0)
|| (strcmp(fini_str,FINI_SYM_STRING) != 0))
{
/*Ravi: changed from bfd_elf32_add_dynamic_entry */
if (! _bfd_elf_add_dynamic_entry (info, DT_FINI, 0))
return FALSE;
}
}
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. */
s = bfd_get_section_by_name (dynobj, ".rela.got");
if (s != NULL)
s->rawsize = 0;
}
/* If this is a -Bsymbolic shared link, then we need to discard all
PC relative relocs against symbols defined in a regular object.
We allocated space for them in the check_relocs routine, but we
will not fill them in in the relocate_section routine. */
if (info->shared && info->symbolic)
elf_ARC_link_hash_traverse (elf_ARC_hash_table (info),
elf_ARC_discard_copies,
(PTR) NULL);
/* The check_relocs and adjust_dynamic_symbol entry points have
determined the sizes of the various dynamic sections. Allocate
memory for them. */
plt = FALSE;
relocs = FALSE;
reltext = FALSE;
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
bfd_boolean strip;
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
/* It's OK to base decisions on the section name, because none
of the dynobj section names depend upon the input files. */
name = bfd_get_section_name (dynobj, s);
strip = FALSE;
if (strcmp (name, ".plt") == 0)
{
if (s->rawsize == 0)
{
/* Strip this section if we don't need it; see the
comment below. */
strip = TRUE;
}
else
{
/* Remember whether there is a PLT. */
plt = TRUE;
}
}
else if (strncmp (name, ".rela", 5) == 0)
{
if (s->rawsize == 0)
{
/* If we don't need this section, strip it from the
output file. This is mostly to handle .rela.bss and
.rela.plt. We must create both sections in
create_dynamic_sections, because they must be created
before the linker maps input sections to output
sections. The linker does that before
adjust_dynamic_symbol is called, and it is that
function which decides whether anything needs to go
into these sections. */
strip = TRUE;
}
else
{
asection *target;
/* Remember whether there are any reloc sections other
than .rela.plt. */
if (strcmp (name, ".rela.plt") != 0)
{
const char *outname;
relocs = TRUE;
/* If this relocation section applies to a read only
section, then we probably need a DT_TEXTREL
entry. The entries in the .rela.plt section
really apply to the .got section, which we
created ourselves and so know is not readonly. */
outname = bfd_get_section_name (output_bfd,
s->output_section);
target = bfd_get_section_by_name (output_bfd, outname + 4);
if (target != NULL
&& (target->flags & SEC_READONLY) != 0
&& (target->flags & SEC_ALLOC) != 0)
reltext = TRUE;
}
/* We use the reloc_count field as a counter if we need
to copy relocs into the output file. */
s->reloc_count = 0;
}
}
else if (strncmp (name, ".got", 4) != 0)
{
/* It's not one of our sections, so don't allocate space. */
continue;
}
if (strip)
{
asection **spp;
for (spp = &s->output_section->owner->sections;
*spp != s->output_section;
spp = &(*spp)->next)
;
*spp = s->output_section->next;
--s->output_section->owner->section_count;
continue;
}
/* Allocate memory for the section contents. */
s->contents = (bfd_byte *) bfd_alloc (dynobj, s->rawsize);
if (s->contents == NULL && s->rawsize != 0)
return FALSE;
}
if (elf_hash_table (info)->dynamic_sections_created)
{
/* Add some entries to the .dynamic section. We fill in the
values later, in elf_arc_finish_dynamic_sections, but we
must add the entries now so that we get the correct size for
the .dynamic section. The DT_DEBUG entry is filled in by the
dynamic linker and used by the debugger. */
if (! info->shared)
{
/*Ravi: changed from bfd_elf32_add_dynamic_entry */
if (! _bfd_elf_add_dynamic_entry (info, DT_DEBUG, 0))
return FALSE;
}
if (plt)
{
/*Ravi: changed from bfd_elf32_add_dynamic_entry */
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)
{
/*Ravi: changed from bfd_elf32_add_dynamic_entry */
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)
{
/*Ravi: changed from bfd_elf32_add_dynamic_entry */
if (! _bfd_elf_add_dynamic_entry (info, DT_TEXTREL, 0))
return FALSE;
}
}
return TRUE;
}
/* Core file support. */
/* Support for core dump NOTE sections. */
static bfd_boolean
elf_arc_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
{
int offset;
size_t size;
switch (note->descsz)
{
default:
return FALSE;
case 240: /* Linux/ARC700 */
/* pr_cursig */
elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
/* pr_pid */
elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24);
/* pr_reg */
offset = 72;
size = 164;
break;
}
/* Make a ".reg/999" section. */
return _bfd_elfcore_make_pseudosection (abfd, ".reg",
size, note->descpos + offset);
}
static bfd_boolean
elf_arc_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
{
switch (note->descsz)
{
default:
return FALSE;
case 124: /* ARC / Linux elf_prpsinfo. */
elf_tdata (abfd)->core_program
= _bfd_elfcore_strndup (abfd, note->descdata + 28, 16);
elf_tdata (abfd)->core_command
= _bfd_elfcore_strndup (abfd, note->descdata + 44, 80);
}
/* Note that for some reason, a spurious space is tacked
onto the end of the args in some (at least one anyway)
implementations, so strip it off if it exists. */
{
char *command = elf_tdata (abfd)->core_command;
int n = strlen (command);
if (0 < n && command[n - 1] == ' ')
command[n - 1] = '\0';
}
return TRUE;
}
#define TARGET_LITTLE_SYM bfd_elf32_littlearc_vec
#define TARGET_LITTLE_NAME "elf32-littlearc"
#define TARGET_BIG_SYM bfd_elf32_bigarc_vec
#define TARGET_BIG_NAME "elf32-bigarc"
#define ELF_ARCH bfd_arch_arc
#define ELF_MACHINE_CODE EM_ARC
#define ELF_MACHINE_ALT1 EM_ARCOMPACT
#define ELF_MAXPAGESIZE 0x1000
#define elf_info_to_howto arc_info_to_howto_rel
#define elf_info_to_howto_rel arc_info_to_howto_rel
#define bfd_elf32_bfd_merge_private_bfd_data \
arc_elf_merge_private_bfd_data
#define bfd_elf32_bfd_reloc_type_lookup \
arc_elf32_bfd_reloc_type_lookup
static reloc_howto_type *
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++)
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 NULL;
}
#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_adjust_dynamic_symbol \
elf_arc_adjust_dynamic_symbol
#define elf_backend_finish_dynamic_sections \
elf_arc_finish_dynamic_sections
#define elf_backend_finish_dynamic_symbol \
elf_arc_finish_dynamic_symbol
#define elf_backend_create_dynamic_sections \
_bfd_elf_create_dynamic_sections
#define elf_backend_size_dynamic_sections \
elf_arc_size_dynamic_sections
#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_grok_psinfo elf_arc_grok_psinfo
#define elf_backend_grok_prstatus elf_arc_grok_prstatus
#include "elf32-target.h"