#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/i370.h"
#define USE_RELA
enum i370_reloc_type
{
R_I370_NONE = 0,
R_I370_ADDR31 = 1,
R_I370_ADDR32 = 2,
R_I370_ADDR16 = 3,
R_I370_REL31 = 4,
R_I370_REL32 = 5,
R_I370_ADDR12 = 6,
R_I370_REL12 = 7,
R_I370_ADDR8 = 8,
R_I370_REL8 = 9,
R_I370_COPY = 10,
R_I370_RELATIVE = 11,
R_I370_max
};
static reloc_howto_type *i370_elf_howto_table[ (int)R_I370_max ];
static reloc_howto_type i370_elf_howto_raw[] =
{
HOWTO (R_I370_NONE,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_NONE",
false,
0,
0,
false),
HOWTO (R_I370_ADDR31,
0,
2,
31,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_ADDR31",
false,
0,
0x7fffffff,
false),
HOWTO (R_I370_ADDR32,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_ADDR32",
false,
0,
0xffffffff,
false),
HOWTO (R_I370_ADDR16,
0,
1,
16,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_ADDR16",
false,
0,
0xffff,
false),
HOWTO (R_I370_REL31,
0,
2,
31,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_REL31",
false,
0,
0x7fffffff,
true),
HOWTO (R_I370_REL32,
0,
2,
32,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_REL32",
false,
0,
0xffffffff,
true),
HOWTO (R_I370_ADDR12,
0,
1,
12,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_ADDR12",
false,
0,
0xfff,
false),
HOWTO (R_I370_REL12,
0,
1,
12,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_REL12",
false,
0,
0xfff,
true),
HOWTO (R_I370_ADDR8,
0,
0,
8,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_ADDR8",
false,
0,
0xff,
false),
HOWTO (R_I370_REL8,
0,
0,
8,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_REL8",
false,
0,
0xff,
true),
HOWTO (R_I370_COPY,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_COPY",
false,
0,
0,
false),
HOWTO (R_I370_RELATIVE,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_I370_RELATIVE",
false,
0,
0xffffffff,
false),
};
static void i370_elf_howto_init PARAMS ((void));
static reloc_howto_type *i370_elf_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static void i370_elf_info_to_howto PARAMS ((bfd *abfd, arelent *cache_ptr,
Elf32_Internal_Rela *dst));
static boolean i370_elf_set_private_flags PARAMS ((bfd *, flagword));
static void
i370_elf_howto_init ()
{
unsigned int i, type;
for (i = 0; i < sizeof (i370_elf_howto_raw) / sizeof (i370_elf_howto_raw[0]); i++)
{
type = i370_elf_howto_raw[i].type;
BFD_ASSERT (type < sizeof (i370_elf_howto_table) / sizeof (i370_elf_howto_table[0]));
i370_elf_howto_table[type] = &i370_elf_howto_raw[i];
}
}
static reloc_howto_type *
i370_elf_reloc_type_lookup (abfd, code)
bfd *abfd ATTRIBUTE_UNUSED;
bfd_reloc_code_real_type code;
{
enum i370_reloc_type i370_reloc = R_I370_NONE;
if (!i370_elf_howto_table[ R_I370_ADDR31 ])
i370_elf_howto_init ();
switch ((int)code)
{
default:
return (reloc_howto_type *)NULL;
case BFD_RELOC_NONE: i370_reloc = R_I370_NONE; break;
case BFD_RELOC_32: i370_reloc = R_I370_ADDR31; break;
case BFD_RELOC_16: i370_reloc = R_I370_ADDR16; break;
case BFD_RELOC_32_PCREL: i370_reloc = R_I370_REL31; break;
case BFD_RELOC_CTOR: i370_reloc = R_I370_ADDR31; break;
case BFD_RELOC_I370_D12: i370_reloc = R_I370_ADDR12; break;
}
return i370_elf_howto_table[ (int)i370_reloc ];
};
static boolean i370_elf_merge_private_bfd_data PARAMS ((bfd *, bfd *));
static boolean i370_elf_relocate_section PARAMS ((bfd *,
struct bfd_link_info *info,
bfd *,
asection *,
bfd_byte *,
Elf_Internal_Rela *relocs,
Elf_Internal_Sym *local_syms,
asection **));
static void i370_elf_post_process_headers
PARAMS ((bfd *, struct bfd_link_info *));
static boolean i370_elf_create_dynamic_sections PARAMS ((bfd *,
struct bfd_link_info *));
static boolean i370_elf_section_from_shdr PARAMS ((bfd *,
Elf32_Internal_Shdr *,
char *));
static boolean i370_elf_fake_sections PARAMS ((bfd *,
Elf32_Internal_Shdr *,
asection *));
#if 0
static elf_linker_section_t *i370_elf_create_linker_section
PARAMS ((bfd *abfd,
struct bfd_link_info *info,
enum elf_linker_section_enum));
#endif
static boolean i370_elf_check_relocs PARAMS ((bfd *,
struct bfd_link_info *,
asection *,
const Elf_Internal_Rela *));
static boolean i370_elf_adjust_dynamic_symbol PARAMS ((struct bfd_link_info *,
struct elf_link_hash_entry *));
static boolean i370_elf_adjust_dynindx PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean i370_elf_size_dynamic_sections PARAMS ((bfd *, struct bfd_link_info *));
static boolean i370_elf_finish_dynamic_sections PARAMS ((bfd *, struct bfd_link_info *));
#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so"
static void
i370_elf_info_to_howto (abfd, cache_ptr, dst)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *cache_ptr;
Elf32_Internal_Rela *dst;
{
if (!i370_elf_howto_table[ R_I370_ADDR31 ])
i370_elf_howto_init ();
BFD_ASSERT (ELF32_R_TYPE (dst->r_info) < (unsigned int) R_I370_max);
cache_ptr->howto = i370_elf_howto_table[ELF32_R_TYPE (dst->r_info)];
}
static boolean
i370_elf_set_private_flags (abfd, flags)
bfd *abfd;
flagword flags;
{
BFD_ASSERT (!elf_flags_init (abfd)
|| elf_elfheader (abfd)->e_flags == flags);
elf_elfheader (abfd)->e_flags = flags;
elf_flags_init (abfd) = true;
return true;
}
static boolean
i370_elf_merge_private_bfd_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
flagword old_flags;
flagword new_flags;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
new_flags = elf_elfheader (ibfd)->e_flags;
old_flags = elf_elfheader (obfd)->e_flags;
if (!elf_flags_init (obfd))
{
elf_flags_init (obfd) = true;
elf_elfheader (obfd)->e_flags = new_flags;
}
else if (new_flags == old_flags)
;
else
{
(*_bfd_error_handler)
("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)",
bfd_archive_filename (ibfd), (long) new_flags, (long) old_flags);
bfd_set_error (bfd_error_bad_value);
return false;
}
return true;
}
static boolean
i370_elf_section_from_shdr (abfd, hdr, name)
bfd *abfd;
Elf32_Internal_Shdr *hdr;
char *name;
{
asection *newsect;
flagword flags;
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
return false;
newsect = hdr->bfd_section;
flags = bfd_get_section_flags (abfd, newsect);
if (hdr->sh_flags & SHF_EXCLUDE)
flags |= SEC_EXCLUDE;
if (hdr->sh_type == SHT_ORDERED)
flags |= SEC_SORT_ENTRIES;
bfd_set_section_flags (abfd, newsect, flags);
return true;
}
static boolean
i370_elf_fake_sections (abfd, shdr, asect)
bfd *abfd ATTRIBUTE_UNUSED;
Elf32_Internal_Shdr *shdr;
asection *asect;
{
if ((asect->flags & SEC_EXCLUDE) != 0)
shdr->sh_flags |= SHF_EXCLUDE;
if ((asect->flags & SEC_SORT_ENTRIES) != 0)
shdr->sh_type = SHT_ORDERED;
return true;
}
#if 0
static elf_linker_section_t *
i370_elf_create_linker_section (abfd, info, which)
bfd *abfd;
struct bfd_link_info *info;
enum elf_linker_section_enum which;
{
bfd *dynobj = elf_hash_table (info)->dynobj;
elf_linker_section_t *lsect;
if (!dynobj)
dynobj = elf_hash_table (info)->dynobj = abfd;
lsect = elf_linker_section (dynobj, which);
if (!lsect)
{
elf_linker_section_t defaults;
static elf_linker_section_t zero_section;
defaults = zero_section;
defaults.which = which;
defaults.hole_written_p = false;
defaults.alignment = 2;
defaults.flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
| SEC_IN_MEMORY);
switch (which)
{
default:
(*_bfd_error_handler) ("%s: Unknown special linker type %d",
bfd_archive_filename (abfd),
(int) which);
bfd_set_error (bfd_error_bad_value);
return (elf_linker_section_t *)0;
case LINKER_SECTION_SDATA:
defaults.name = ".sdata";
defaults.rel_name = ".rela.sdata";
defaults.bss_name = ".sbss";
defaults.sym_name = "_SDA_BASE_";
defaults.sym_offset = 32768;
break;
case LINKER_SECTION_SDATA2:
defaults.name = ".sdata2";
defaults.rel_name = ".rela.sdata2";
defaults.bss_name = ".sbss2";
defaults.sym_name = "_SDA2_BASE_";
defaults.sym_offset = 32768;
defaults.flags |= SEC_READONLY;
break;
}
lsect = _bfd_elf_create_linker_section (abfd, info, which, &defaults);
}
return lsect;
}
#endif
static boolean
i370_elf_create_dynamic_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
register asection *s;
flagword flags;
if (!_bfd_elf_create_dynamic_sections(abfd, info))
return false;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
s = bfd_make_section (abfd, ".dynsbss");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, SEC_ALLOC))
return false;
if (! info->shared)
{
s = bfd_make_section (abfd, ".rela.sbss");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, 2))
return false;
}
s = bfd_make_section (abfd, ".rela.text");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, 2))
return false;
return true;
}
static boolean
i370_elf_adjust_dynamic_symbol (info, h)
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
{
bfd *dynobj = elf_hash_table (info)->dynobj;
asection *s;
unsigned int power_of_two;
#ifdef DEBUG
fprintf (stderr, "i370_elf_adjust_dynamic_symbol called for %s\n",
h->root.root.string);
#endif
BFD_ASSERT (dynobj != NULL
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
|| h->weakdef != NULL
|| ((h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)));
s = bfd_get_section_by_name (dynobj, ".rela.text");
BFD_ASSERT (s != NULL);
s->_raw_size += sizeof (Elf32_External_Rela);
if (h->weakdef != NULL)
{
BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
|| h->weakdef->root.type == bfd_link_hash_defweak);
h->root.u.def.section = h->weakdef->root.u.def.section;
h->root.u.def.value = h->weakdef->root.u.def.value;
return true;
}
if (info->shared)
return true;
if (h->size <= elf_gp_size (dynobj))
s = bfd_get_section_by_name (dynobj, ".dynsbss");
else
s = bfd_get_section_by_name (dynobj, ".dynbss");
BFD_ASSERT (s != NULL);
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
{
asection *srel;
if (h->size <= elf_gp_size (dynobj))
srel = bfd_get_section_by_name (dynobj, ".rela.sbss");
else
srel = bfd_get_section_by_name (dynobj, ".rela.bss");
BFD_ASSERT (srel != NULL);
srel->_raw_size += sizeof (Elf32_External_Rela);
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY;
}
power_of_two = bfd_log2 (h->size);
if (power_of_two > 4)
power_of_two = 4;
s->_raw_size = BFD_ALIGN (s->_raw_size,
(bfd_size_type) (1 << power_of_two));
if (power_of_two > bfd_get_section_alignment (dynobj, s))
{
if (! bfd_set_section_alignment (dynobj, s, power_of_two))
return false;
}
h->root.u.def.section = s;
h->root.u.def.value = s->_raw_size;
s->_raw_size += h->size;
return true;
}
static boolean
i370_elf_adjust_dynindx (h, cparg)
struct elf_link_hash_entry *h;
PTR cparg;
{
int *cp = (int *) cparg;
#ifdef DEBUG
fprintf (stderr,
"i370_elf_adjust_dynindx called, h->dynindx = %d, *cp = %d\n",
h->dynindx, *cp);
#endif
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->dynindx != -1)
h->dynindx += *cp;
return true;
}
static boolean
i370_elf_size_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *s;
boolean plt;
boolean relocs;
boolean reltext;
#ifdef DEBUG
fprintf (stderr, "i370_elf_size_dynamic_sections called\n");
#endif
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
if (elf_hash_table (info)->dynamic_sections_created)
{
if (! info->shared)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER;
s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
}
}
else
{
static char *rela_sections[] = { ".rela.got", ".rela.sdata",
".rela.sdata2", ".rela.sbss",
(char *)0 };
char **p;
for (p = rela_sections; *p != (char *)0; p++)
{
s = bfd_get_section_by_name (dynobj, *p);
if (s != NULL)
s->_raw_size = 0;
}
}
plt = false;
relocs = false;
reltext = false;
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
boolean strip;
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
name = bfd_get_section_name (dynobj, s);
strip = false;
if (strcmp (name, ".plt") == 0)
{
if (s->_raw_size == 0)
{
strip = true;
}
else
{
plt = true;
}
}
else if (strncmp (name, ".rela", 5) == 0)
{
if (s->_raw_size == 0)
{
strip = true;
}
else
{
asection *target;
const char *outname;
relocs = true;
outname = bfd_get_section_name (output_bfd,
s->output_section);
target = bfd_get_section_by_name (output_bfd, outname + 5);
if (target != NULL
&& (target->flags & SEC_READONLY) != 0
&& (target->flags & SEC_ALLOC) != 0)
reltext = true;
s->reloc_count = 0;
}
}
else if (strcmp (name, ".got") != 0
&& strcmp (name, ".sdata") != 0
&& strcmp (name, ".sdata2") != 0)
{
continue;
}
if (strip)
{
asection **spp;
for (spp = &s->output_section->owner->sections;
*spp != NULL;
spp = &(*spp)->next)
{
if (*spp == s->output_section)
{
bfd_section_list_remove (s->output_section->owner, spp);
--s->output_section->owner->section_count;
break;
}
}
continue;
}
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
if (s->contents == NULL && s->_raw_size != 0)
return false;
}
if (elf_hash_table (info)->dynamic_sections_created)
{
#define add_dynamic_entry(TAG, VAL) \
bfd_elf32_add_dynamic_entry (info, (bfd_vma) (TAG), (bfd_vma) (VAL))
if (!info->shared)
{
if (!add_dynamic_entry (DT_DEBUG, 0))
return false;
}
if (plt)
{
if (!add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
|| !add_dynamic_entry (DT_PLTREL, DT_RELA)
|| !add_dynamic_entry (DT_JMPREL, 0))
return false;
}
if (relocs)
{
if (!add_dynamic_entry (DT_RELA, 0)
|| !add_dynamic_entry (DT_RELASZ, 0)
|| !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela)))
return false;
}
if (reltext)
{
if (!add_dynamic_entry (DT_TEXTREL, 0))
return false;
info->flags |= DF_TEXTREL;
}
}
#undef add_dynamic_entry
if (info->shared)
{
int c;
for (c = 0, s = output_bfd->sections; s != NULL; s = s->next)
{
if ((s->flags & SEC_LINKER_CREATED) != 0
|| (s->flags & SEC_ALLOC) == 0)
{
elf_section_data (s)->dynindx = -1;
continue;
}
elf_section_data (s)->dynindx = c + 1;
c++;
}
elf_link_hash_traverse (elf_hash_table (info),
i370_elf_adjust_dynindx,
(PTR) &c);
elf_hash_table (info)->dynsymcount += c;
}
return true;
}
static boolean
i370_elf_check_relocs (abfd, info, sec, 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;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
bfd_vma *local_got_offsets;
asection *sreloc;
if (info->relocateable)
return true;
#ifdef DEBUG
fprintf (stderr, "i370_elf_check_relocs called for section %s in %s\n",
bfd_get_section_name (abfd, sec),
bfd_archive_filename (abfd));
#endif
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);
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;
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];
if (info->shared)
{
#ifdef DEBUG
fprintf (stderr,
"i370_elf_check_relocs needs to create relocation for %s\n",
(h && h->root.root.string)
? h->root.root.string : "<unknown>");
#endif
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->_raw_size += sizeof (Elf32_External_Rela);
}
}
return true;
}
static boolean
i370_elf_finish_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
asection *sdyn;
bfd *dynobj = elf_hash_table (info)->dynobj;
asection *sgot = bfd_get_section_by_name (dynobj, ".got");
#ifdef DEBUG
fprintf (stderr, "i370_elf_finish_dynamic_sections called\n");
#endif
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->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
const char *name;
boolean size;
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
case DT_PLTGOT: name = ".plt"; size = false; break;
case DT_PLTRELSZ: name = ".rela.plt"; size = true; break;
case DT_JMPREL: name = ".rela.plt"; size = false; break;
default: name = NULL; size = false; break;
}
if (name != NULL)
{
asection *s;
s = bfd_get_section_by_name (output_bfd, name);
if (s == NULL)
dyn.d_un.d_val = 0;
else
{
if (! size)
dyn.d_un.d_ptr = s->vma;
else
{
if (s->_cooked_size != 0)
dyn.d_un.d_val = s->_cooked_size;
else
dyn.d_un.d_val = s->_raw_size;
}
}
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
}
}
}
if (sgot)
{
unsigned char *contents = sgot->contents;
bfd_put_32 (output_bfd, (bfd_vma) 0x4e800021 , contents);
if (sdyn == NULL)
bfd_put_32 (output_bfd, (bfd_vma) 0, contents+4);
else
bfd_put_32 (output_bfd,
sdyn->output_section->vma + sdyn->output_offset,
contents+4);
elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
}
if (info->shared)
{
asection *sdynsym;
asection *s;
Elf_Internal_Sym sym;
int maxdindx = 0;
sdynsym = bfd_get_section_by_name (dynobj, ".dynsym");
BFD_ASSERT (sdynsym != NULL);
sym.st_size = 0;
sym.st_name = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION);
sym.st_other = 0;
for (s = output_bfd->sections; s != NULL; s = s->next)
{
int indx, dindx;
Elf32_External_Sym *esym;
sym.st_value = s->vma;
indx = elf_section_data (s)->this_idx;
dindx = elf_section_data (s)->dynindx;
if (dindx != -1)
{
BFD_ASSERT(indx > 0);
BFD_ASSERT(dindx > 0);
if (dindx > maxdindx)
maxdindx = dindx;
sym.st_shndx = indx;
esym = (Elf32_External_Sym *) sdynsym->contents + dindx;
bfd_elf32_swap_symbol_out (output_bfd, &sym, (PTR) esym, (PTR) 0);
}
}
elf_section_data (sdynsym->output_section)->this_hdr.sh_info =
maxdindx + 1;
}
return true;
}
static boolean
i370_elf_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, local_syms, local_sections)
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 = &elf_tdata (input_bfd)->symtab_hdr;
struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
bfd *dynobj = elf_hash_table (info)->dynobj;
Elf_Internal_Rela *rel = relocs;
Elf_Internal_Rela *relend = relocs + input_section->reloc_count;
asection *sreloc = NULL;
bfd_vma *local_got_offsets;
boolean ret = true;
#ifdef DEBUG
fprintf (stderr, "i370_elf_relocate_section called for %s section %s, %ld relocations%s\n",
bfd_archive_filename (input_bfd),
bfd_section_name(input_bfd, input_section),
(long) input_section->reloc_count,
(info->relocateable) ? " (relocatable)" : "");
#endif
if (!i370_elf_howto_table[ R_I370_ADDR31 ])
i370_elf_howto_init ();
local_got_offsets = elf_local_got_offsets (input_bfd);
for (; rel < relend; rel++)
{
enum i370_reloc_type r_type = (enum i370_reloc_type)ELF32_R_TYPE (rel->r_info);
bfd_vma offset = rel->r_offset;
bfd_vma addend = rel->r_addend;
bfd_reloc_status_type r = bfd_reloc_other;
Elf_Internal_Sym *sym = (Elf_Internal_Sym *)0;
asection *sec = (asection *)0;
struct elf_link_hash_entry *h = (struct elf_link_hash_entry *)0;
const char *sym_name = (const char *)0;
reloc_howto_type *howto;
unsigned long r_symndx;
bfd_vma relocation;
if ((unsigned)r_type >= (unsigned)R_I370_max
|| !i370_elf_howto_table[(int)r_type])
{
(*_bfd_error_handler) ("%s: unknown relocation type %d",
bfd_archive_filename (input_bfd),
(int) r_type);
bfd_set_error (bfd_error_bad_value);
ret = false;
continue;
}
howto = i370_elf_howto_table[(int)r_type];
r_symndx = ELF32_R_SYM (rel->r_info);
if (info->relocateable)
{
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
if ((unsigned)ELF_ST_TYPE (sym->st_info) == STT_SECTION)
{
sec = local_sections[r_symndx];
addend = rel->r_addend += sec->output_offset + sym->st_value;
}
}
#ifdef DEBUG
fprintf (stderr, "\ttype = %s (%d), symbol index = %ld, offset = %ld, addend = %ld\n",
howto->name,
(int)r_type,
r_symndx,
(long)offset,
(long)addend);
#endif
continue;
}
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
sym_name = "<local symbol>";
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
addend = rel->r_addend;
}
else
{
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;
sym_name = h->root.root.string;
if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
sec = h->root.u.def.section;
if (info->shared
&& ((! info->symbolic && h->dynindx != -1)
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)
&& (input_section->flags & SEC_ALLOC) != 0
&& (r_type == R_I370_ADDR31
|| r_type == R_I370_COPY
|| r_type == R_I370_ADDR16
|| r_type == R_I370_RELATIVE))
{
relocation = 0;
}
else
relocation = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
}
else if (h->root.type == bfd_link_hash_undefweak)
relocation = 0;
else if (info->shared
&& ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
relocation = 0;
else
{
(*info->callbacks->undefined_symbol) (info,
h->root.root.string,
input_bfd,
input_section,
rel->r_offset,
true);
ret = false;
continue;
}
}
switch ((int) r_type)
{
default:
(*_bfd_error_handler)
("%s: unknown relocation type %d for symbol %s",
bfd_archive_filename (input_bfd),
(int) r_type, sym_name);
bfd_set_error (bfd_error_bad_value);
ret = false;
continue;
case (int)R_I370_NONE:
continue;
case (int)R_I370_REL31:
if (h == NULL
|| strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
break;
case (int)R_I370_ADDR31:
case (int)R_I370_ADDR16:
if (info->shared
&& r_symndx != 0)
{
Elf_Internal_Rela outrel;
int skip;
#ifdef DEBUG
fprintf (stderr,
"i370_elf_relocate_section needs to create relocation for %s\n",
(h && h->root.root.string) ? h->root.root.string : "<unknown>");
#endif
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 = 0;
outrel.r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset);
if (outrel.r_offset == (bfd_vma) -1
|| outrel.r_offset == (bfd_vma) -2)
skip = (int) outrel.r_offset;
outrel.r_offset += (input_section->output_section->vma
+ input_section->output_offset);
if (skip)
memset (&outrel, 0, sizeof outrel);
else if (h != NULL
&& ((! info->symbolic && h->dynindx != -1)
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
{
BFD_ASSERT (h->dynindx != -1);
outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
outrel.r_addend = rel->r_addend;
}
else
{
if (r_type == R_I370_ADDR31)
{
outrel.r_info = ELF32_R_INFO (0, R_I370_RELATIVE);
outrel.r_addend = relocation + rel->r_addend;
}
else
{
long indx;
if (h == NULL)
sec = local_sections[r_symndx];
else
{
BFD_ASSERT (h->root.type == bfd_link_hash_defined
|| (h->root.type
== bfd_link_hash_defweak));
sec = h->root.u.def.section;
}
if (sec != NULL && bfd_is_abs_section (sec))
indx = 0;
else if (sec == NULL || sec->owner == NULL)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
else
{
asection *osec;
osec = sec->output_section;
indx = elf_section_data (osec)->dynindx;
BFD_ASSERT(indx > 0);
#ifdef DEBUG
if (indx <= 0)
{
printf ("indx=%d section=%s flags=%08x name=%s\n",
indx, osec->name, osec->flags,
h->root.root.string);
}
#endif
}
outrel.r_info = ELF32_R_INFO (indx, r_type);
outrel.r_addend = relocation + rel->r_addend;
}
}
bfd_elf32_swap_reloca_out (output_bfd, &outrel,
(((Elf32_External_Rela *)
sreloc->contents)
+ sreloc->reloc_count));
++sreloc->reloc_count;
if (skip == -1
|| (input_section->flags & SEC_ALLOC) != 0
|| ELF32_R_TYPE (outrel.r_info) != R_I370_RELATIVE)
continue;
}
break;
case (int)R_I370_COPY:
case (int)R_I370_RELATIVE:
(*_bfd_error_handler)
("%s: Relocation %s is not yet supported for symbol %s.",
bfd_archive_filename (input_bfd),
i370_elf_howto_table[(int) r_type]->name,
sym_name);
bfd_set_error (bfd_error_invalid_operation);
ret = false;
continue;
}
#ifdef DEBUG
fprintf (stderr, "\ttype = %s (%d), name = %s, symbol index = %ld, offset = %ld, addend = %ld\n",
howto->name,
(int)r_type,
sym_name,
r_symndx,
(long)offset,
(long)addend);
#endif
r = _bfd_final_link_relocate (howto,
input_bfd,
input_section,
contents,
offset,
relocation,
addend);
if (r != bfd_reloc_ok)
{
ret = false;
switch (r)
{
default:
break;
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)
break;
if (*name == '\0')
name = bfd_section_name (input_bfd, sec);
}
(*info->callbacks->reloc_overflow) (info,
name,
howto->name,
(bfd_vma) 0,
input_bfd,
input_section,
offset);
}
break;
}
}
}
#ifdef DEBUG
fprintf (stderr, "\n");
#endif
return ret;
}
static void
i370_elf_post_process_headers (abfd, link_info)
bfd * abfd;
struct bfd_link_info * link_info ATTRIBUTE_UNUSED;
{
Elf_Internal_Ehdr * i_ehdrp;
i_ehdrp = elf_elfheader (abfd);
i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_LINUX;
}
#define TARGET_BIG_SYM bfd_elf32_i370_vec
#define TARGET_BIG_NAME "elf32-i370"
#define ELF_ARCH bfd_arch_i370
#define ELF_MACHINE_CODE EM_S370
#ifdef EM_I370_OLD
#define ELF_MACHINE_ALT1 EM_I370_OLD
#endif
#define ELF_MAXPAGESIZE 0x1000
#define elf_info_to_howto i370_elf_info_to_howto
#define elf_backend_plt_not_loaded 1
#define elf_backend_got_symbol_offset 4
#define bfd_elf32_bfd_reloc_type_lookup i370_elf_reloc_type_lookup
#define bfd_elf32_bfd_set_private_flags i370_elf_set_private_flags
#define bfd_elf32_bfd_merge_private_bfd_data i370_elf_merge_private_bfd_data
#define elf_backend_relocate_section i370_elf_relocate_section
#define elf_backend_create_dynamic_sections i370_elf_create_dynamic_sections
#define elf_backend_size_dynamic_sections i370_elf_size_dynamic_sections
#define elf_backend_finish_dynamic_sections i370_elf_finish_dynamic_sections
#define elf_backend_fake_sections i370_elf_fake_sections
#define elf_backend_section_from_shdr i370_elf_section_from_shdr
#define elf_backend_adjust_dynamic_symbol i370_elf_adjust_dynamic_symbol
#define elf_backend_check_relocs i370_elf_check_relocs
#define elf_backend_post_process_headers i370_elf_post_process_headers
static int i370_noop PARAMS ((void));
static int i370_noop ()
{
return 1;
}
#define elf_backend_add_symbol_hook \
(boolean (*) PARAMS ((bfd *, struct bfd_link_info *, \
const Elf_Internal_Sym *, const char **, flagword *, \
asection **, bfd_vma *))) i370_noop
#define elf_backend_finish_dynamic_symbol \
(boolean (*) PARAMS ((bfd *, struct bfd_link_info *, \
struct elf_link_hash_entry *, \
Elf_Internal_Sym *))) i370_noop
#define elf_backend_additional_program_headers \
(int (*) PARAMS ((bfd *))) i370_noop
#define elf_backend_modify_segment_map \
(boolean (*) PARAMS ((bfd *))) i370_noop
#include "elf32-target.h"