#include "alloca-conf.h"
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/hppa.h"
#include "libhppa.h"
#include "elf64-hppa.h"
#define ARCH_SIZE 64
#define PLT_ENTRY_SIZE 0x10
#define DLT_ENTRY_SIZE 0x8
#define OPD_ENTRY_SIZE 0x20
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/pa20_64/dld.sl"
static char plt_stub[] = {0x53, 0x61, 0x00, 0x00, 0xe8, 0x20, 0xd0, 0x00,
0x53, 0x7b, 0x00, 0x00 };
struct elf64_hppa_dyn_hash_entry
{
struct bfd_hash_entry root;
bfd_vma dlt_offset;
bfd_vma plt_offset;
bfd_vma opd_offset;
bfd_vma stub_offset;
struct elf_link_hash_entry *h;
long sym_indx;
bfd *owner;
bfd_vma st_value;
int st_shndx;
struct elf64_hppa_dyn_reloc_entry
{
struct elf64_hppa_dyn_reloc_entry *next;
int type;
asection *sec;
int sec_symndx;
bfd_vma offset;
bfd_vma addend;
} *reloc_entries;
unsigned want_dlt;
unsigned want_plt;
unsigned want_opd;
unsigned want_stub;
};
struct elf64_hppa_dyn_hash_table
{
struct bfd_hash_table root;
};
struct elf64_hppa_link_hash_table
{
struct elf_link_hash_table root;
asection *dlt_sec;
asection *dlt_rel_sec;
asection *plt_sec;
asection *plt_rel_sec;
asection *opd_sec;
asection *opd_rel_sec;
asection *other_rel_sec;
bfd_vma gp_offset;
asection *stub_sec;
bfd_vma text_segment_base;
bfd_vma data_segment_base;
struct elf64_hppa_dyn_hash_table dyn_hash_table;
bfd *section_syms_bfd;
int *section_syms;
};
#define elf64_hppa_hash_table(p) \
((struct elf64_hppa_link_hash_table *) ((p)->hash))
typedef struct bfd_hash_entry *(*new_hash_entry_func)
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static bfd_boolean elf64_hppa_dyn_hash_table_init
PARAMS ((struct elf64_hppa_dyn_hash_table *ht, bfd *abfd,
new_hash_entry_func new));
static struct bfd_hash_entry *elf64_hppa_new_dyn_hash_entry
PARAMS ((struct bfd_hash_entry *entry, struct bfd_hash_table *table,
const char *string));
static struct bfd_link_hash_table *elf64_hppa_hash_table_create
PARAMS ((bfd *abfd));
static struct elf64_hppa_dyn_hash_entry *elf64_hppa_dyn_hash_lookup
PARAMS ((struct elf64_hppa_dyn_hash_table *table, const char *string,
bfd_boolean create, bfd_boolean copy));
static void elf64_hppa_dyn_hash_traverse
PARAMS ((struct elf64_hppa_dyn_hash_table *table,
bfd_boolean (*func) (struct elf64_hppa_dyn_hash_entry *, PTR),
PTR info));
static const char *get_dyn_name
PARAMS ((asection *, struct elf_link_hash_entry *,
const Elf_Internal_Rela *, char **, size_t *));
#include "elf-hppa.h"
static bfd_boolean elf64_hppa_object_p
PARAMS ((bfd *));
static bfd_boolean elf64_hppa_section_from_shdr
PARAMS ((bfd *, Elf_Internal_Shdr *, const char *));
static void elf64_hppa_post_process_headers
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean elf64_hppa_create_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean elf64_hppa_adjust_dynamic_symbol
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
static bfd_boolean elf64_hppa_mark_milli_and_exported_functions
PARAMS ((struct elf_link_hash_entry *, PTR));
static bfd_boolean elf64_hppa_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean elf64_hppa_link_output_symbol_hook
PARAMS ((bfd *abfd, struct bfd_link_info *, const char *,
Elf_Internal_Sym *, asection *input_sec));
static bfd_boolean elf64_hppa_finish_dynamic_symbol
PARAMS ((bfd *, struct bfd_link_info *,
struct elf_link_hash_entry *, Elf_Internal_Sym *));
static int elf64_hppa_additional_program_headers
PARAMS ((bfd *));
static bfd_boolean elf64_hppa_modify_segment_map
PARAMS ((bfd *));
static enum elf_reloc_type_class elf64_hppa_reloc_type_class
PARAMS ((const Elf_Internal_Rela *));
static bfd_boolean elf64_hppa_finish_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean elf64_hppa_check_relocs
PARAMS ((bfd *, struct bfd_link_info *,
asection *, const Elf_Internal_Rela *));
static bfd_boolean elf64_hppa_dynamic_symbol_p
PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *));
static bfd_boolean elf64_hppa_mark_exported_functions
PARAMS ((struct elf_link_hash_entry *, PTR));
static bfd_boolean elf64_hppa_finalize_opd
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean elf64_hppa_finalize_dlt
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean allocate_global_data_dlt
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean allocate_global_data_plt
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean allocate_global_data_stub
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean allocate_global_data_opd
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean get_reloc_section
PARAMS ((bfd *, struct elf64_hppa_link_hash_table *, asection *));
static bfd_boolean count_dyn_reloc
PARAMS ((bfd *, struct elf64_hppa_dyn_hash_entry *,
int, asection *, int, bfd_vma, bfd_vma));
static bfd_boolean allocate_dynrel_entries
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean elf64_hppa_finalize_dynreloc
PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
static bfd_boolean get_opd
PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *));
static bfd_boolean get_plt
PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *));
static bfd_boolean get_dlt
PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *));
static bfd_boolean get_stub
PARAMS ((bfd *, struct bfd_link_info *, struct elf64_hppa_link_hash_table *));
static int elf64_hppa_elf_get_symbol_type
PARAMS ((Elf_Internal_Sym *, int));
static bfd_boolean
elf64_hppa_dyn_hash_table_init (ht, abfd, new)
struct elf64_hppa_dyn_hash_table *ht;
bfd *abfd ATTRIBUTE_UNUSED;
new_hash_entry_func new;
{
memset (ht, 0, sizeof (*ht));
return bfd_hash_table_init (&ht->root, new);
}
static struct bfd_hash_entry*
elf64_hppa_new_dyn_hash_entry (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct elf64_hppa_dyn_hash_entry *ret;
ret = (struct elf64_hppa_dyn_hash_entry *) entry;
if (!ret)
ret = bfd_hash_allocate (table, sizeof (*ret));
if (!ret)
return 0;
memset (ret, 0, sizeof (*ret));
ret = ((struct elf64_hppa_dyn_hash_entry *)
bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
return &ret->root;
}
static struct bfd_link_hash_table*
elf64_hppa_hash_table_create (abfd)
bfd *abfd;
{
struct elf64_hppa_link_hash_table *ret;
ret = bfd_zalloc (abfd, (bfd_size_type) sizeof (*ret));
if (!ret)
return 0;
if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
_bfd_elf_link_hash_newfunc))
{
bfd_release (abfd, ret);
return 0;
}
if (!elf64_hppa_dyn_hash_table_init (&ret->dyn_hash_table, abfd,
elf64_hppa_new_dyn_hash_entry))
return 0;
return &ret->root.root;
}
static struct elf64_hppa_dyn_hash_entry *
elf64_hppa_dyn_hash_lookup(table, string, create, copy)
struct elf64_hppa_dyn_hash_table *table;
const char *string;
bfd_boolean create, copy;
{
return ((struct elf64_hppa_dyn_hash_entry *)
bfd_hash_lookup (&table->root, string, create, copy));
}
static void
elf64_hppa_dyn_hash_traverse (table, func, info)
struct elf64_hppa_dyn_hash_table *table;
bfd_boolean (*func) PARAMS ((struct elf64_hppa_dyn_hash_entry *, PTR));
PTR info;
{
(bfd_hash_traverse
(&table->root,
(bfd_boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) func,
info));
}
static bfd_boolean
elf64_hppa_object_p (abfd)
bfd *abfd;
{
Elf_Internal_Ehdr * i_ehdrp;
unsigned int flags;
i_ehdrp = elf_elfheader (abfd);
if (strcmp (bfd_get_target (abfd), "elf64-hppa-linux") == 0)
{
if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_LINUX)
return FALSE;
}
else
{
if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_HPUX)
return FALSE;
}
flags = i_ehdrp->e_flags;
switch (flags & (EF_PARISC_ARCH | EF_PARISC_WIDE))
{
case EFA_PARISC_1_0:
return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 10);
case EFA_PARISC_1_1:
return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 11);
case EFA_PARISC_2_0:
return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 20);
case EFA_PARISC_2_0 | EF_PARISC_WIDE:
return bfd_default_set_arch_mach (abfd, bfd_arch_hppa, 25);
}
return TRUE;
}
static bfd_boolean
elf64_hppa_section_from_shdr (abfd, hdr, name)
bfd *abfd;
Elf_Internal_Shdr *hdr;
const char *name;
{
asection *newsect;
switch (hdr->sh_type)
{
case SHT_PARISC_EXT:
if (strcmp (name, ".PARISC.archext") != 0)
return FALSE;
break;
case SHT_PARISC_UNWIND:
if (strcmp (name, ".PARISC.unwind") != 0)
return FALSE;
break;
case SHT_PARISC_DOC:
case SHT_PARISC_ANNOT:
default:
return FALSE;
}
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
return FALSE;
newsect = hdr->bfd_section;
return TRUE;
}
static const char *
get_dyn_name (sec, h, rel, pbuf, plen)
asection *sec;
struct elf_link_hash_entry *h;
const Elf_Internal_Rela *rel;
char **pbuf;
size_t *plen;
{
size_t nlen, tlen;
char *buf;
size_t len;
if (h && rel->r_addend == 0)
return h->root.root.string;
if (h)
nlen = strlen (h->root.root.string);
else
nlen = 8 + 1 + sizeof (rel->r_info) * 2 - 8;
tlen = nlen + 1 + sizeof (rel->r_addend) * 2 + 1;
len = *plen;
buf = *pbuf;
if (len < tlen)
{
if (buf)
free (buf);
*pbuf = buf = malloc (tlen);
*plen = len = tlen;
if (!buf)
return NULL;
}
if (h)
{
memcpy (buf, h->root.root.string, nlen);
buf[nlen++] = '+';
sprintf_vma (buf + nlen, rel->r_addend);
}
else
{
nlen = sprintf (buf, "%x:%lx",
sec->id & 0xffffffff,
(long) ELF64_R_SYM (rel->r_info));
if (rel->r_addend)
{
buf[nlen++] = '+';
sprintf_vma (buf + nlen, rel->r_addend);
}
}
return buf;
}
static bfd_boolean
get_reloc_section (abfd, hppa_info, sec)
bfd *abfd;
struct elf64_hppa_link_hash_table *hppa_info;
asection *sec;
{
const char *srel_name;
asection *srel;
bfd *dynobj;
srel_name = (bfd_elf_string_from_elf_section
(abfd, elf_elfheader(abfd)->e_shstrndx,
elf_section_data(sec)->rel_hdr.sh_name));
if (srel_name == NULL)
return FALSE;
BFD_ASSERT ((strncmp (srel_name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (abfd, sec),
srel_name+5) == 0)
|| (strncmp (srel_name, ".rel", 4) == 0
&& strcmp (bfd_get_section_name (abfd, sec),
srel_name+4) == 0));
dynobj = hppa_info->root.dynobj;
if (!dynobj)
hppa_info->root.dynobj = dynobj = abfd;
srel = bfd_get_section_by_name (dynobj, srel_name);
if (srel == NULL)
{
srel = bfd_make_section (dynobj, srel_name);
if (srel == NULL
|| !bfd_set_section_flags (dynobj, srel,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED
| SEC_READONLY))
|| !bfd_set_section_alignment (dynobj, srel, 3))
return FALSE;
}
hppa_info->other_rel_sec = srel;
return TRUE;
}
static bfd_boolean
count_dyn_reloc (abfd, dyn_h, type, sec, sec_symndx, offset, addend)
bfd *abfd;
struct elf64_hppa_dyn_hash_entry *dyn_h;
int type;
asection *sec;
int sec_symndx;
bfd_vma offset;
bfd_vma addend;
{
struct elf64_hppa_dyn_reloc_entry *rent;
rent = (struct elf64_hppa_dyn_reloc_entry *)
bfd_alloc (abfd, (bfd_size_type) sizeof (*rent));
if (!rent)
return FALSE;
rent->next = dyn_h->reloc_entries;
rent->type = type;
rent->sec = sec;
rent->sec_symndx = sec_symndx;
rent->offset = offset;
rent->addend = addend;
dyn_h->reloc_entries = rent;
return TRUE;
}
static bfd_boolean
elf64_hppa_check_relocs (abfd, info, sec, relocs)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
const Elf_Internal_Rela *relocs;
{
struct elf64_hppa_link_hash_table *hppa_info;
const Elf_Internal_Rela *relend;
Elf_Internal_Shdr *symtab_hdr;
const Elf_Internal_Rela *rel;
asection *dlt, *plt, *stubs;
char *buf;
size_t buf_len;
int sec_symndx;
if (info->relocateable)
return TRUE;
if (! elf_hash_table (info)->dynamic_sections_created)
{
if (! bfd_elf64_link_create_dynamic_sections (abfd, info))
return FALSE;
}
hppa_info = elf64_hppa_hash_table (info);
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
if (info->shared && hppa_info->section_syms_bfd != abfd)
{
unsigned long i;
unsigned int highest_shndx;
Elf_Internal_Sym *local_syms = NULL;
Elf_Internal_Sym *isym, *isymend;
bfd_size_type amt;
if (hppa_info->section_syms)
free (hppa_info->section_syms);
if (symtab_hdr->sh_info != 0)
{
local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
if (local_syms == NULL)
local_syms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
if (local_syms == NULL)
return FALSE;
}
highest_shndx = 0;
isymend = local_syms + symtab_hdr->sh_info;
for (isym = local_syms; isym < isymend; isym++)
{
if (isym->st_shndx > highest_shndx)
highest_shndx = isym->st_shndx;
}
highest_shndx++;
amt = highest_shndx;
amt *= sizeof (int);
hppa_info->section_syms = (int *) bfd_malloc (amt);
for (i = 0, isym = local_syms; isym < isymend; i++, isym++)
{
if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
hppa_info->section_syms[isym->st_shndx] = i;
}
if (local_syms != NULL
&& symtab_hdr->contents != (unsigned char *) local_syms)
{
if (! info->keep_memory)
free (local_syms);
else
{
symtab_hdr->contents = (unsigned char *) local_syms;
}
}
hppa_info->section_syms_bfd = abfd;
}
if (info->shared)
{
sec_symndx = _bfd_elf_section_from_bfd_section (abfd, sec);
if (sec_symndx == -1)
return FALSE;
sec_symndx = hppa_info->section_syms[sec_symndx];
}
else
sec_symndx = 0;
dlt = plt = stubs = NULL;
buf = NULL;
buf_len = 0;
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; ++rel)
{
enum {
NEED_DLT = 1,
NEED_PLT = 2,
NEED_STUB = 4,
NEED_OPD = 8,
NEED_DYNREL = 16,
};
struct elf_link_hash_entry *h = NULL;
unsigned long r_symndx = ELF64_R_SYM (rel->r_info);
struct elf64_hppa_dyn_hash_entry *dyn_h;
int need_entry;
const char *addr_name;
bfd_boolean maybe_dynamic;
int dynrel_type = R_PARISC_NONE;
static reloc_howto_type *howto;
if (r_symndx >= symtab_hdr->sh_info)
{
long indx = r_symndx - symtab_hdr->sh_info;
h = elf_sym_hashes (abfd)[indx];
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;
h->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
}
maybe_dynamic = FALSE;
if (h && ((info->shared
&& (!info->symbolic || info->allow_shlib_undefined) )
|| ! (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
|| h->root.type == bfd_link_hash_defweak))
maybe_dynamic = TRUE;
howto = elf_hppa_howto_table + ELF64_R_TYPE (rel->r_info);
need_entry = 0;
switch (howto->type)
{
case R_PARISC_DLTIND21L:
case R_PARISC_DLTIND14R:
case R_PARISC_DLTIND14F:
case R_PARISC_DLTIND14WR:
case R_PARISC_DLTIND14DR:
need_entry = NEED_DLT;
break;
case R_PARISC_LTOFF_TP21L:
case R_PARISC_LTOFF_TP14R:
case R_PARISC_LTOFF_TP14F:
case R_PARISC_LTOFF_TP64:
case R_PARISC_LTOFF_TP14WR:
case R_PARISC_LTOFF_TP14DR:
case R_PARISC_LTOFF_TP16F:
case R_PARISC_LTOFF_TP16WF:
case R_PARISC_LTOFF_TP16DF:
need_entry = NEED_DLT;
break;
case R_PARISC_PCREL12F:
case R_PARISC_PCREL17F:
case R_PARISC_PCREL22F:
case R_PARISC_PCREL32:
case R_PARISC_PCREL64:
case R_PARISC_PCREL21L:
case R_PARISC_PCREL17R:
case R_PARISC_PCREL17C:
case R_PARISC_PCREL14R:
case R_PARISC_PCREL14F:
case R_PARISC_PCREL22C:
case R_PARISC_PCREL14WR:
case R_PARISC_PCREL14DR:
case R_PARISC_PCREL16F:
case R_PARISC_PCREL16WF:
case R_PARISC_PCREL16DF:
need_entry = (NEED_PLT | NEED_STUB);
break;
case R_PARISC_PLTOFF21L:
case R_PARISC_PLTOFF14R:
case R_PARISC_PLTOFF14F:
case R_PARISC_PLTOFF14WR:
case R_PARISC_PLTOFF14DR:
case R_PARISC_PLTOFF16F:
case R_PARISC_PLTOFF16WF:
case R_PARISC_PLTOFF16DF:
need_entry = (NEED_PLT);
break;
case R_PARISC_DIR64:
if (info->shared || maybe_dynamic)
need_entry = (NEED_DYNREL);
dynrel_type = R_PARISC_DIR64;
break;
case R_PARISC_LTOFF_FPTR21L:
case R_PARISC_LTOFF_FPTR14R:
case R_PARISC_LTOFF_FPTR14WR:
case R_PARISC_LTOFF_FPTR14DR:
case R_PARISC_LTOFF_FPTR32:
case R_PARISC_LTOFF_FPTR64:
case R_PARISC_LTOFF_FPTR16F:
case R_PARISC_LTOFF_FPTR16WF:
case R_PARISC_LTOFF_FPTR16DF:
if (info->shared || maybe_dynamic)
need_entry = (NEED_DLT | NEED_OPD);
else
need_entry = (NEED_DLT | NEED_OPD);
dynrel_type = R_PARISC_FPTR64;
break;
case R_PARISC_FPTR64:
if (info->shared || maybe_dynamic)
need_entry = (NEED_OPD | NEED_DYNREL);
else
need_entry = (NEED_OPD);
dynrel_type = R_PARISC_FPTR64;
break;
}
if (!need_entry)
continue;
addr_name = get_dyn_name (sec, h, rel, &buf, &buf_len);
dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table,
addr_name, TRUE, TRUE);
BFD_ASSERT (dyn_h);
dyn_h->h = h;
dyn_h->owner = abfd;
dyn_h->sym_indx = r_symndx;
if (need_entry & NEED_DLT)
{
if (! hppa_info->dlt_sec
&& ! get_dlt (abfd, info, hppa_info))
goto err_out;
dyn_h->want_dlt = 1;
}
if (need_entry & NEED_PLT)
{
if (! hppa_info->plt_sec
&& ! get_plt (abfd, info, hppa_info))
goto err_out;
dyn_h->want_plt = 1;
}
if (need_entry & NEED_STUB)
{
if (! hppa_info->stub_sec
&& ! get_stub (abfd, info, hppa_info))
goto err_out;
dyn_h->want_stub = 1;
}
if (need_entry & NEED_OPD)
{
if (! hppa_info->opd_sec
&& ! get_opd (abfd, info, hppa_info))
goto err_out;
dyn_h->want_opd = 1;
if (h)
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
}
if ((need_entry & NEED_DYNREL) && (sec->flags & SEC_ALLOC))
{
if (! hppa_info->other_rel_sec
&& ! get_reloc_section (abfd, hppa_info, sec))
goto err_out;
if (!count_dyn_reloc (abfd, dyn_h, dynrel_type, sec,
sec_symndx, rel->r_offset, rel->r_addend))
goto err_out;
if (info->shared && dynrel_type == R_PARISC_FPTR64
&& ! (_bfd_elf64_link_record_local_dynamic_symbol
(info, abfd, sec_symndx)))
return FALSE;
}
}
if (buf)
free (buf);
return TRUE;
err_out:
if (buf)
free (buf);
return FALSE;
}
struct elf64_hppa_allocate_data
{
struct bfd_link_info *info;
bfd_size_type ofs;
};
static bfd_boolean
elf64_hppa_dynamic_symbol_p (h, info)
struct elf_link_hash_entry *h;
struct bfd_link_info *info;
{
if (h == NULL)
return FALSE;
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 (h->dynindx == -1)
return FALSE;
if (h->root.type == bfd_link_hash_undefweak
|| h->root.type == bfd_link_hash_defweak)
return TRUE;
if (h->root.root.string[0] == '$' && h->root.root.string[1] == '$')
return FALSE;
if ((info->shared && (!info->symbolic || info->allow_shlib_undefined))
|| ((h->elf_link_hash_flags
& (ELF_LINK_HASH_DEF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR))
== (ELF_LINK_HASH_DEF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR)))
return TRUE;
return FALSE;
}
static bfd_boolean
elf64_hppa_mark_exported_functions (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct bfd_link_info *info = (struct bfd_link_info *)data;
struct elf64_hppa_link_hash_table *hppa_info;
hppa_info = elf64_hppa_hash_table (info);
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& h->root.u.def.section->output_section != NULL
&& h->type == STT_FUNC)
{
struct elf64_hppa_dyn_hash_entry *dyn_h;
dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table,
h->root.root.string, TRUE, TRUE);
BFD_ASSERT (dyn_h);
dyn_h->h = h;
if (! hppa_info->opd_sec
&& ! get_opd (hppa_info->root.dynobj, info, hppa_info))
return FALSE;
dyn_h->want_opd = 1;
dyn_h->st_shndx = -1;
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
}
return TRUE;
}
static bfd_boolean
allocate_global_data_dlt (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data;
if (dyn_h->want_dlt)
{
struct elf_link_hash_entry *h = dyn_h->h;
if (x->info->shared)
{
if (! h
|| (h->dynindx == -1 && h->type != STT_PARISC_MILLI))
{
bfd *owner;
owner = (h ? h->root.u.def.section->owner : dyn_h->owner);
if (! (_bfd_elf64_link_record_local_dynamic_symbol
(x->info, owner, dyn_h->sym_indx)))
return FALSE;
}
}
dyn_h->dlt_offset = x->ofs;
x->ofs += DLT_ENTRY_SIZE;
}
return TRUE;
}
static bfd_boolean
allocate_global_data_plt (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data;
if (dyn_h->want_plt
&& elf64_hppa_dynamic_symbol_p (dyn_h->h, x->info)
&& !((dyn_h->h->root.type == bfd_link_hash_defined
|| dyn_h->h->root.type == bfd_link_hash_defweak)
&& dyn_h->h->root.u.def.section->output_section != NULL))
{
dyn_h->plt_offset = x->ofs;
x->ofs += PLT_ENTRY_SIZE;
if (dyn_h->plt_offset < 0x2000)
elf64_hppa_hash_table (x->info)->gp_offset = dyn_h->plt_offset;
}
else
dyn_h->want_plt = 0;
return TRUE;
}
static bfd_boolean
allocate_global_data_stub (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data;
if (dyn_h->want_stub
&& elf64_hppa_dynamic_symbol_p (dyn_h->h, x->info)
&& !((dyn_h->h->root.type == bfd_link_hash_defined
|| dyn_h->h->root.type == bfd_link_hash_defweak)
&& dyn_h->h->root.u.def.section->output_section != NULL))
{
dyn_h->stub_offset = x->ofs;
x->ofs += sizeof (plt_stub);
}
else
dyn_h->want_stub = 0;
return TRUE;
}
static bfd_boolean
allocate_global_data_opd (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data;
if (dyn_h->want_opd)
{
struct elf_link_hash_entry *h = dyn_h->h;
if (h)
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 (h && (h->root.type == bfd_link_hash_undefined
|| h->root.u.def.section->output_section == NULL))
dyn_h->want_opd = 0;
else if (x->info->shared
|| h == NULL
|| (h->dynindx == -1 && h->type != STT_PARISC_MILLI)
|| (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
if (x->info->shared
&& (h == NULL || (h->dynindx == -1)))
{
bfd *owner;
owner = (h ? h->root.u.def.section->owner : dyn_h->owner);
if (!_bfd_elf64_link_record_local_dynamic_symbol
(x->info, owner, dyn_h->sym_indx))
return FALSE;
}
if (x->info->shared && h)
{
char *new_name;
struct elf_link_hash_entry *nh;
new_name = alloca (strlen (h->root.root.string) + 2);
new_name[0] = '.';
strcpy (new_name + 1, h->root.root.string);
nh = elf_link_hash_lookup (elf_hash_table (x->info),
new_name, TRUE, TRUE, TRUE);
nh->root.type = h->root.type;
nh->root.u.def.value = h->root.u.def.value;
nh->root.u.def.section = h->root.u.def.section;
if (! bfd_elf64_link_record_dynamic_symbol (x->info, nh))
return FALSE;
}
dyn_h->opd_offset = x->ofs;
x->ofs += OPD_ENTRY_SIZE;
}
else
dyn_h->want_opd = 0;
}
return TRUE;
}
static void
elf64_hppa_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);
if (strcmp (bfd_get_target (abfd), "elf64-hppa-linux") == 0)
{
i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_LINUX;
}
else
{
i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_HPUX;
i_ehdrp->e_ident[EI_ABIVERSION] = 1;
}
}
static bfd_boolean
get_opd (abfd, info, hppa_info)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
struct elf64_hppa_link_hash_table *hppa_info;
{
asection *opd;
bfd *dynobj;
opd = hppa_info->opd_sec;
if (!opd)
{
dynobj = hppa_info->root.dynobj;
if (!dynobj)
hppa_info->root.dynobj = dynobj = abfd;
opd = bfd_make_section (dynobj, ".opd");
if (!opd
|| !bfd_set_section_flags (dynobj, opd,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, opd, 3))
{
BFD_ASSERT (0);
return FALSE;
}
hppa_info->opd_sec = opd;
}
return TRUE;
}
static bfd_boolean
get_plt (abfd, info, hppa_info)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
struct elf64_hppa_link_hash_table *hppa_info;
{
asection *plt;
bfd *dynobj;
plt = hppa_info->plt_sec;
if (!plt)
{
dynobj = hppa_info->root.dynobj;
if (!dynobj)
hppa_info->root.dynobj = dynobj = abfd;
plt = bfd_make_section (dynobj, ".plt");
if (!plt
|| !bfd_set_section_flags (dynobj, plt,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, plt, 3))
{
BFD_ASSERT (0);
return FALSE;
}
hppa_info->plt_sec = plt;
}
return TRUE;
}
static bfd_boolean
get_dlt (abfd, info, hppa_info)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
struct elf64_hppa_link_hash_table *hppa_info;
{
asection *dlt;
bfd *dynobj;
dlt = hppa_info->dlt_sec;
if (!dlt)
{
dynobj = hppa_info->root.dynobj;
if (!dynobj)
hppa_info->root.dynobj = dynobj = abfd;
dlt = bfd_make_section (dynobj, ".dlt");
if (!dlt
|| !bfd_set_section_flags (dynobj, dlt,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, dlt, 3))
{
BFD_ASSERT (0);
return FALSE;
}
hppa_info->dlt_sec = dlt;
}
return TRUE;
}
static bfd_boolean
get_stub (abfd, info, hppa_info)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
struct elf64_hppa_link_hash_table *hppa_info;
{
asection *stub;
bfd *dynobj;
stub = hppa_info->stub_sec;
if (!stub)
{
dynobj = hppa_info->root.dynobj;
if (!dynobj)
hppa_info->root.dynobj = dynobj = abfd;
stub = bfd_make_section (dynobj, ".stub");
if (!stub
|| !bfd_set_section_flags (dynobj, stub,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_READONLY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, stub, 3))
{
BFD_ASSERT (0);
return FALSE;
}
hppa_info->stub_sec = stub;
}
return TRUE;
}
static bfd_boolean
elf64_hppa_create_dynamic_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
asection *s;
if (! get_stub (abfd, info, elf64_hppa_hash_table (info)))
return FALSE;
if (! get_dlt (abfd, info, elf64_hppa_hash_table (info)))
return FALSE;
if (! get_plt (abfd, info, elf64_hppa_hash_table (info)))
return FALSE;
if (! get_opd (abfd, info, elf64_hppa_hash_table (info)))
return FALSE;
s = bfd_make_section(abfd, ".rela.dlt");
if (s == NULL
|| !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_READONLY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, s, 3))
return FALSE;
elf64_hppa_hash_table (info)->dlt_rel_sec = s;
s = bfd_make_section(abfd, ".rela.plt");
if (s == NULL
|| !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_READONLY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, s, 3))
return FALSE;
elf64_hppa_hash_table (info)->plt_rel_sec = s;
s = bfd_make_section(abfd, ".rela.data");
if (s == NULL
|| !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_READONLY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, s, 3))
return FALSE;
elf64_hppa_hash_table (info)->other_rel_sec = s;
s = bfd_make_section(abfd, ".rela.opd");
if (s == NULL
|| !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_READONLY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (abfd, s, 3))
return FALSE;
elf64_hppa_hash_table (info)->opd_rel_sec = s;
return TRUE;
}
static bfd_boolean
allocate_dynrel_entries (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct elf64_hppa_allocate_data *x = (struct elf64_hppa_allocate_data *)data;
struct elf64_hppa_link_hash_table *hppa_info;
struct elf64_hppa_dyn_reloc_entry *rent;
bfd_boolean dynamic_symbol, shared;
hppa_info = elf64_hppa_hash_table (x->info);
dynamic_symbol = elf64_hppa_dynamic_symbol_p (dyn_h->h, x->info);
shared = x->info->shared;
if (!dynamic_symbol && !shared)
return TRUE;
for (rent = dyn_h->reloc_entries; rent; rent = rent->next)
{
if (!shared && rent->type == R_PARISC_FPTR64 && dyn_h->want_opd)
continue;
hppa_info->other_rel_sec->_raw_size += sizeof (Elf64_External_Rela);
if (dyn_h->h == 0
|| (dyn_h->h->dynindx == -1 && dyn_h->h->type != STT_PARISC_MILLI))
if (!_bfd_elf64_link_record_local_dynamic_symbol
(x->info, rent->sec->owner, dyn_h->sym_indx))
return FALSE;
}
if ((dynamic_symbol || shared) && dyn_h->want_dlt)
hppa_info->dlt_rel_sec->_raw_size += sizeof (Elf64_External_Rela);
if (shared && dyn_h->want_opd)
hppa_info->opd_rel_sec->_raw_size += sizeof (Elf64_External_Rela);
if (dyn_h->want_plt && dynamic_symbol)
{
bfd_size_type t = 0;
if (dynamic_symbol)
t = sizeof (Elf64_External_Rela);
else if (shared)
t = 2 * sizeof (Elf64_External_Rela);
hppa_info->plt_rel_sec->_raw_size += t;
}
return TRUE;
}
static bfd_boolean
elf64_hppa_adjust_dynamic_symbol (info, h)
struct bfd_link_info *info ATTRIBUTE_UNUSED;
struct elf_link_hash_entry *h;
{
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;
}
return TRUE;
}
static bfd_boolean
elf64_hppa_mark_milli_and_exported_functions (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct bfd_link_info *info = (struct bfd_link_info *)data;
struct elf_link_hash_entry *elf = h;
if (elf->root.type == bfd_link_hash_warning)
elf = (struct elf_link_hash_entry *) elf->root.u.i.link;
if (elf->type == STT_PARISC_MILLI)
{
if (elf->dynindx != -1)
{
elf->dynindx = -1;
_bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
elf->dynstr_index);
}
return TRUE;
}
return elf64_hppa_mark_exported_functions (h, data);
}
static bfd_boolean
elf64_hppa_size_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *s;
bfd_boolean plt;
bfd_boolean relocs;
bfd_boolean reltext;
struct elf64_hppa_allocate_data data;
struct elf64_hppa_link_hash_table *hppa_info;
hppa_info = elf64_hppa_hash_table (info);
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
elf_link_hash_traverse (elf_hash_table (info),
(elf_hash_table (info)->dynamic_sections_created
? elf64_hppa_mark_milli_and_exported_functions
: elf64_hppa_mark_exported_functions),
info);
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
{
s = bfd_get_section_by_name (dynobj, ".rela.dlt");
if (s != NULL)
s->_raw_size = 0;
}
data.info = info;
if (elf64_hppa_hash_table (info)->dlt_sec)
{
data.ofs = 0x0;
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
allocate_global_data_dlt, &data);
hppa_info->dlt_sec->_raw_size = data.ofs;
data.ofs = 0x0;
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
allocate_global_data_plt, &data);
hppa_info->plt_sec->_raw_size = data.ofs;
data.ofs = 0x0;
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
allocate_global_data_stub, &data);
hppa_info->stub_sec->_raw_size = data.ofs;
}
if (elf64_hppa_hash_table (info)->opd_sec)
{
data.ofs = 0;
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
allocate_global_data_opd, &data);
hppa_info->opd_sec->_raw_size = data.ofs;
}
if (hppa_info->root.dynamic_sections_created)
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
allocate_dynrel_entries, &data);
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;
name = bfd_get_section_name (dynobj, s);
strip = 0;
if (strcmp (name, ".plt") == 0)
{
if (s->_raw_size == 0)
{
strip = TRUE;
}
else
{
plt = TRUE;
}
}
else if (strcmp (name, ".dlt") == 0)
{
if (s->_raw_size == 0)
{
strip = TRUE;
}
}
else if (strcmp (name, ".opd") == 0)
{
if (s->_raw_size == 0)
{
strip = TRUE;
}
}
else if (strncmp (name, ".rela", 5) == 0)
{
if (s->_raw_size == 0)
{
strip = TRUE;
}
else
{
asection *target;
if (strcmp (name, ".rela.plt") != 0)
{
const char *outname;
relocs = TRUE;
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;
}
s->reloc_count = 0;
}
}
else if (strncmp (name, ".dlt", 4) != 0
&& strcmp (name, ".stub") != 0
&& strcmp (name, ".got") != 0)
{
continue;
}
if (strip)
{
_bfd_strip_section_from_output (info, s);
continue;
}
if (s->contents == NULL)
{
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_elf64_add_dynamic_entry (info, (bfd_vma) (TAG), (bfd_vma) (VAL))
if (!add_dynamic_entry (DT_HP_DLD_FLAGS, 0)
|| !add_dynamic_entry (DT_PLTGOT, 0))
return FALSE;
if (! info->shared)
{
if (!add_dynamic_entry (DT_DEBUG, 0)
|| !add_dynamic_entry (DT_HP_DLD_HOOK, 0)
|| !add_dynamic_entry (DT_HP_LOAD_MAP, 0))
return FALSE;
}
if (!add_dynamic_entry (DT_FLAGS, (info)->flags))
return FALSE;
if (plt)
{
if (!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 (Elf64_External_Rela)))
return FALSE;
}
if (reltext)
{
if (!add_dynamic_entry (DT_TEXTREL, 0))
return FALSE;
info->flags |= DF_TEXTREL;
}
}
#undef add_dynamic_entry
return TRUE;
}
static bfd_boolean
elf64_hppa_link_output_symbol_hook (abfd, info, name, sym, input_sec)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
const char *name;
Elf_Internal_Sym *sym;
asection *input_sec ATTRIBUTE_UNUSED;
{
struct elf64_hppa_link_hash_table *hppa_info;
struct elf64_hppa_dyn_hash_entry *dyn_h;
if (!name)
return TRUE;
hppa_info = elf64_hppa_hash_table (info);
dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table,
name, FALSE, FALSE);
if (dyn_h && dyn_h->want_opd && dyn_h->st_shndx != -1)
{
sym->st_value = dyn_h->st_value;
sym->st_shndx = dyn_h->st_shndx;
}
return TRUE;
}
static bfd_boolean
elf64_hppa_finish_dynamic_symbol (output_bfd, info, h, sym)
bfd *output_bfd;
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
asection *stub, *splt, *sdlt, *sopd, *spltrel, *sdltrel;
struct elf64_hppa_link_hash_table *hppa_info;
struct elf64_hppa_dyn_hash_entry *dyn_h;
hppa_info = elf64_hppa_hash_table (info);
dyn_h = elf64_hppa_dyn_hash_lookup (&hppa_info->dyn_hash_table,
h->root.root.string, FALSE, FALSE);
stub = hppa_info->stub_sec;
splt = hppa_info->plt_sec;
sdlt = hppa_info->dlt_sec;
sopd = hppa_info->opd_sec;
spltrel = hppa_info->plt_rel_sec;
sdltrel = hppa_info->dlt_rel_sec;
if (dyn_h && dyn_h->want_opd)
{
BFD_ASSERT (sopd != NULL)
dyn_h->st_value = sym->st_value;
dyn_h->st_shndx = sym->st_shndx;
sym->st_value = (dyn_h->opd_offset
+ sopd->output_offset
+ sopd->output_section->vma);
sym->st_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
sopd->output_section);
}
if (dyn_h && dyn_h->want_plt
&& elf64_hppa_dynamic_symbol_p (dyn_h->h, info))
{
bfd_vma value;
Elf_Internal_Rela rel;
bfd_byte *loc;
BFD_ASSERT (splt != NULL && spltrel != NULL)
if (info->shared && h->root.type == bfd_link_hash_undefined)
value = 0;
else
value = (h->root.u.def.value + h->root.u.def.section->vma);
bfd_put_64 (splt->owner, value, splt->contents + dyn_h->plt_offset);
value = _bfd_get_gp_value (splt->output_section->owner);
bfd_put_64 (splt->owner, value, splt->contents + dyn_h->plt_offset + 0x8);
rel.r_offset = (dyn_h->plt_offset + splt->output_offset
+ splt->output_section->vma);
rel.r_info = ELF64_R_INFO (h->dynindx, R_PARISC_IPLT);
rel.r_addend = 0;
loc = spltrel->contents;
loc += spltrel->reloc_count++ * sizeof (Elf64_External_Rela);
bfd_elf64_swap_reloca_out (splt->output_section->owner, &rel, loc);
}
if (dyn_h && dyn_h->want_stub
&& elf64_hppa_dynamic_symbol_p (dyn_h->h, info))
{
bfd_vma value;
int insn;
unsigned int max_offset;
BFD_ASSERT (stub != NULL)
memcpy (stub->contents + dyn_h->stub_offset, plt_stub, sizeof (plt_stub));
value = dyn_h->plt_offset - hppa_info->gp_offset;
insn = bfd_get_32 (stub->owner, stub->contents + dyn_h->stub_offset);
if (output_bfd->arch_info->mach >= 25)
{
max_offset = 32768;
insn &= ~ 0xfff1;
insn |= re_assemble_16 ((int) value);
}
else
{
max_offset = 8192;
insn &= ~ 0x3ff1;
insn |= re_assemble_14 ((int) value);
}
if ((value & 7) || value + max_offset >= 2*max_offset - 8)
{
(*_bfd_error_handler) (_("stub entry for %s cannot load .plt, dp offset = %ld"),
dyn_h->root.string,
(long) value);
return FALSE;
}
bfd_put_32 (stub->owner, (bfd_vma) insn,
stub->contents + dyn_h->stub_offset);
value += 8;
insn = bfd_get_32 (stub->owner, stub->contents + dyn_h->stub_offset + 8);
if (output_bfd->arch_info->mach >= 25)
{
insn &= ~ 0xfff1;
insn |= re_assemble_16 ((int) value);
}
else
{
insn &= ~ 0x3ff1;
insn |= re_assemble_14 ((int) value);
}
bfd_put_32 (stub->owner, (bfd_vma) insn,
stub->contents + dyn_h->stub_offset + 8);
}
return TRUE;
}
static bfd_boolean
elf64_hppa_finalize_opd (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct bfd_link_info *info = (struct bfd_link_info *)data;
struct elf64_hppa_link_hash_table *hppa_info;
struct elf_link_hash_entry *h = dyn_h ? dyn_h->h : NULL;
asection *sopd;
asection *sopdrel;
hppa_info = elf64_hppa_hash_table (info);
sopd = hppa_info->opd_sec;
sopdrel = hppa_info->opd_rel_sec;
if (h && dyn_h->want_opd)
{
bfd_vma value;
memset (sopd->contents + dyn_h->opd_offset, 0, 16);
value = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
bfd_put_64 (sopd->owner, value, sopd->contents + dyn_h->opd_offset + 16);
value = _bfd_get_gp_value (sopd->output_section->owner);
bfd_put_64 (sopd->owner, value, sopd->contents + dyn_h->opd_offset + 24);
}
if (info->shared && dyn_h && dyn_h->want_opd)
{
Elf_Internal_Rela rel;
bfd_byte *loc;
int dynindx;
if (h && h->dynindx != -1)
dynindx = h->dynindx;
else
dynindx
= _bfd_elf_link_lookup_local_dynindx (info, dyn_h->owner,
dyn_h->sym_indx);
rel.r_offset = (dyn_h->opd_offset + sopd->output_offset
+ sopd->output_section->vma);
if (h)
{
char *new_name;
struct elf_link_hash_entry *nh;
new_name = alloca (strlen (h->root.root.string) + 2);
new_name[0] = '.';
strcpy (new_name + 1, h->root.root.string);
nh = elf_link_hash_lookup (elf_hash_table (info),
new_name, FALSE, FALSE, FALSE);
dynindx = nh->dynindx;
}
rel.r_addend = 0;
rel.r_info = ELF64_R_INFO (dynindx, R_PARISC_EPLT);
loc = sopdrel->contents;
loc += sopdrel->reloc_count++ * sizeof (Elf64_External_Rela);
bfd_elf64_swap_reloca_out (sopd->output_section->owner, &rel, loc);
}
return TRUE;
}
static bfd_boolean
elf64_hppa_finalize_dlt (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct bfd_link_info *info = (struct bfd_link_info *)data;
struct elf64_hppa_link_hash_table *hppa_info;
asection *sdlt, *sdltrel;
struct elf_link_hash_entry *h = dyn_h ? dyn_h->h : NULL;
hppa_info = elf64_hppa_hash_table (info);
sdlt = hppa_info->dlt_sec;
sdltrel = hppa_info->dlt_rel_sec;
if (! info->shared && h && dyn_h->want_dlt)
{
bfd_vma value;
if (dyn_h->want_opd)
{
value = (dyn_h->opd_offset
+ hppa_info->opd_sec->output_offset
+ hppa_info->opd_sec->output_section->vma);
}
else if (h->root.u.def.section)
{
value = h->root.u.def.value + h->root.u.def.section->output_offset;
if (h->root.u.def.section->output_section)
value += h->root.u.def.section->output_section->vma;
else
value += h->root.u.def.section->vma;
}
else
value = 0;
bfd_put_64 (sdlt->owner, value, sdlt->contents + dyn_h->dlt_offset);
}
if (dyn_h->want_dlt
&& (elf64_hppa_dynamic_symbol_p (dyn_h->h, info) || info->shared))
{
Elf_Internal_Rela rel;
bfd_byte *loc;
int dynindx;
if (h && h->dynindx != -1)
dynindx = h->dynindx;
else
dynindx
= _bfd_elf_link_lookup_local_dynindx (info, dyn_h->owner,
dyn_h->sym_indx);
rel.r_offset = (dyn_h->dlt_offset + sdlt->output_offset
+ sdlt->output_section->vma);
if (h && h->type == STT_FUNC)
rel.r_info = ELF64_R_INFO (dynindx, R_PARISC_FPTR64);
else
rel.r_info = ELF64_R_INFO (dynindx, R_PARISC_DIR64);
rel.r_addend = 0;
loc = sdltrel->contents;
loc += sdltrel->reloc_count++ * sizeof (Elf64_External_Rela);
bfd_elf64_swap_reloca_out (sdlt->output_section->owner, &rel, loc);
}
return TRUE;
}
static bfd_boolean
elf64_hppa_finalize_dynreloc (dyn_h, data)
struct elf64_hppa_dyn_hash_entry *dyn_h;
PTR data;
{
struct bfd_link_info *info = (struct bfd_link_info *)data;
struct elf64_hppa_link_hash_table *hppa_info;
struct elf_link_hash_entry *h;
int dynamic_symbol;
dynamic_symbol = elf64_hppa_dynamic_symbol_p (dyn_h->h, info);
if (!dynamic_symbol && !info->shared)
return TRUE;
if (dyn_h->reloc_entries)
{
struct elf64_hppa_dyn_reloc_entry *rent;
int dynindx;
hppa_info = elf64_hppa_hash_table (info);
h = dyn_h->h;
if (h && h->dynindx != -1)
dynindx = h->dynindx;
else
dynindx
= _bfd_elf_link_lookup_local_dynindx (info, dyn_h->owner,
dyn_h->sym_indx);
for (rent = dyn_h->reloc_entries; rent; rent = rent->next)
{
Elf_Internal_Rela rel;
bfd_byte *loc;
if (!info->shared && rent->type == R_PARISC_FPTR64 && dyn_h->want_opd)
continue;
rel.r_offset = (rent->offset + rent->sec->output_offset
+ rent->sec->output_section->vma);
if (info->shared && rent->type == R_PARISC_FPTR64 && dyn_h->want_opd)
{
bfd_vma value, value2;
value = (dyn_h->opd_offset
+ hppa_info->opd_sec->output_section->vma
+ hppa_info->opd_sec->output_offset);
value2 = (rent->sec->output_section->vma
+ rent->sec->output_offset);
value -= value2;
rel.r_addend = value;
dynindx
= _bfd_elf_link_lookup_local_dynindx (info,
rent->sec->owner,
rent->sec_symndx);
}
else
rel.r_addend = rent->addend;
rel.r_info = ELF64_R_INFO (dynindx, rent->type);
loc = hppa_info->other_rel_sec->contents;
loc += (hppa_info->other_rel_sec->reloc_count++
* sizeof (Elf64_External_Rela));
bfd_elf64_swap_reloca_out (hppa_info->other_rel_sec->output_section->owner,
&rel, loc);
}
}
return TRUE;
}
static enum elf_reloc_type_class
elf64_hppa_reloc_type_class (rela)
const Elf_Internal_Rela *rela;
{
if (ELF64_R_SYM (rela->r_info) == 0)
return reloc_class_relative;
switch ((int) ELF64_R_TYPE (rela->r_info))
{
case R_PARISC_IPLT:
return reloc_class_plt;
case R_PARISC_COPY:
return reloc_class_copy;
default:
return reloc_class_normal;
}
}
static bfd_boolean
elf64_hppa_finish_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *sdyn;
struct elf64_hppa_link_hash_table *hppa_info;
hppa_info = elf64_hppa_hash_table (info);
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
elf64_hppa_finalize_opd,
info);
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
elf64_hppa_finalize_dynreloc,
info);
dynobj = elf_hash_table (info)->dynobj;
elf64_hppa_dyn_hash_traverse (&hppa_info->dyn_hash_table,
elf64_hppa_finalize_dlt,
info);
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (elf_hash_table (info)->dynamic_sections_created)
{
Elf64_External_Dyn *dyncon, *dynconend;
BFD_ASSERT (sdyn != NULL);
dyncon = (Elf64_External_Dyn *) sdyn->contents;
dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
asection *s;
bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
default:
break;
case DT_HP_LOAD_MAP:
s = bfd_get_section_by_name (output_bfd, ".data");
dyn.d_un.d_ptr = s->vma;
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_PLTGOT:
dyn.d_un.d_ptr = _bfd_get_gp_value (output_bfd);
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_JMPREL:
s = hppa_info->plt_rel_sec;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_PLTRELSZ:
s = hppa_info->plt_rel_sec;
dyn.d_un.d_val = s->_raw_size;
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_RELA:
s = hppa_info->other_rel_sec;
if (! s || ! s->_raw_size)
s = hppa_info->dlt_rel_sec;
if (! s || ! s->_raw_size)
s = hppa_info->opd_rel_sec;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_RELASZ:
s = hppa_info->other_rel_sec;
dyn.d_un.d_val = s->_raw_size;
s = hppa_info->dlt_rel_sec;
dyn.d_un.d_val += s->_raw_size;
s = hppa_info->opd_rel_sec;
dyn.d_un.d_val += s->_raw_size;
s = hppa_info->plt_rel_sec;
dyn.d_un.d_val += s->_raw_size;
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
}
}
}
return TRUE;
}
static int
elf64_hppa_additional_program_headers (abfd)
bfd *abfd;
{
asection *s;
s = bfd_get_section_by_name (abfd, ".interp");
if (! s)
return 1;
return 0;
}
static bfd_boolean
elf64_hppa_modify_segment_map (abfd)
bfd *abfd;
{
struct elf_segment_map *m;
asection *s;
s = bfd_get_section_by_name (abfd, ".interp");
if (! s)
{
for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
if (m->p_type == PT_PHDR)
break;
if (m == NULL)
{
m = ((struct elf_segment_map *)
bfd_zalloc (abfd, (bfd_size_type) sizeof *m));
if (m == NULL)
return FALSE;
m->p_type = PT_PHDR;
m->p_flags = PF_R | PF_X;
m->p_flags_valid = 1;
m->p_paddr_valid = 1;
m->includes_phdrs = 1;
m->next = elf_tdata (abfd)->segment_map;
elf_tdata (abfd)->segment_map = m;
}
}
for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
if (m->p_type == PT_LOAD)
{
unsigned int i;
for (i = 0; i < m->count; i++)
{
if (m->sections[i]->flags & SEC_CODE
|| (strcmp (m->sections[i]->name, ".hash") == 0))
m->p_flags |= (PF_X | PF_HP_CODE);
}
}
return TRUE;
}
static int
elf64_hppa_elf_get_symbol_type (elf_sym, type)
Elf_Internal_Sym *elf_sym;
int type;
{
if (ELF_ST_TYPE (elf_sym->st_info) == STT_PARISC_MILLI)
return STT_PARISC_MILLI;
else
return type;
}
const struct elf_size_info hppa64_elf_size_info =
{
sizeof (Elf64_External_Ehdr),
sizeof (Elf64_External_Phdr),
sizeof (Elf64_External_Shdr),
sizeof (Elf64_External_Rel),
sizeof (Elf64_External_Rela),
sizeof (Elf64_External_Sym),
sizeof (Elf64_External_Dyn),
sizeof (Elf_External_Note),
4,
1,
64, 8,
ELFCLASS64, EV_CURRENT,
bfd_elf64_write_out_phdrs,
bfd_elf64_write_shdrs_and_ehdr,
bfd_elf64_write_relocs,
bfd_elf64_swap_symbol_in,
bfd_elf64_swap_symbol_out,
bfd_elf64_slurp_reloc_table,
bfd_elf64_slurp_symbol_table,
bfd_elf64_swap_dyn_in,
bfd_elf64_swap_dyn_out,
bfd_elf64_swap_reloc_in,
bfd_elf64_swap_reloc_out,
bfd_elf64_swap_reloca_in,
bfd_elf64_swap_reloca_out
};
#define TARGET_BIG_SYM bfd_elf64_hppa_vec
#define TARGET_BIG_NAME "elf64-hppa"
#define ELF_ARCH bfd_arch_hppa
#define ELF_MACHINE_CODE EM_PARISC
#define ELF_MAXPAGESIZE 0x1000
#define bfd_elf64_bfd_reloc_type_lookup elf_hppa_reloc_type_lookup
#define bfd_elf64_bfd_is_local_label_name elf_hppa_is_local_label_name
#define elf_info_to_howto elf_hppa_info_to_howto
#define elf_info_to_howto_rel elf_hppa_info_to_howto_rel
#define elf_backend_section_from_shdr elf64_hppa_section_from_shdr
#define elf_backend_object_p elf64_hppa_object_p
#define elf_backend_final_write_processing \
elf_hppa_final_write_processing
#define elf_backend_fake_sections elf_hppa_fake_sections
#define elf_backend_add_symbol_hook elf_hppa_add_symbol_hook
#define elf_backend_relocate_section elf_hppa_relocate_section
#define bfd_elf64_bfd_final_link elf_hppa_final_link
#define elf_backend_create_dynamic_sections \
elf64_hppa_create_dynamic_sections
#define elf_backend_post_process_headers elf64_hppa_post_process_headers
#define elf_backend_adjust_dynamic_symbol \
elf64_hppa_adjust_dynamic_symbol
#define elf_backend_size_dynamic_sections \
elf64_hppa_size_dynamic_sections
#define elf_backend_finish_dynamic_symbol \
elf64_hppa_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections \
elf64_hppa_finish_dynamic_sections
#define bfd_elf64_bfd_link_hash_table_create \
elf64_hppa_hash_table_create
#define elf_backend_check_relocs \
elf64_hppa_check_relocs
#define elf_backend_size_info \
hppa64_elf_size_info
#define elf_backend_additional_program_headers \
elf64_hppa_additional_program_headers
#define elf_backend_modify_segment_map \
elf64_hppa_modify_segment_map
#define elf_backend_link_output_symbol_hook \
elf64_hppa_link_output_symbol_hook
#define elf_backend_want_got_plt 0
#define elf_backend_plt_readonly 0
#define elf_backend_want_plt_sym 0
#define elf_backend_got_header_size 0
#define elf_backend_plt_header_size 0
#define elf_backend_type_change_ok TRUE
#define elf_backend_get_symbol_type elf64_hppa_elf_get_symbol_type
#define elf_backend_reloc_type_class elf64_hppa_reloc_type_class
#define elf_backend_rela_normal 1
#include "elf64-target.h"
#undef TARGET_BIG_SYM
#define TARGET_BIG_SYM bfd_elf64_hppa_linux_vec
#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf64-hppa-linux"
#define INCLUDED_TARGET_FILE 1
#include "elf64-target.h"