/* ELF linker support.
- Copyright 1995 Free Software Foundation, Inc.
+ Copyright 1995, 1996 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
return false;
}
}
+\f
/* Add symbols from an ELF archive file to the linker hash table. We
don't use _bfd_generic_link_add_archive_symbols because of a
definition = true;
size_change_ok = false;
- type_change_ok = false;
+ type_change_ok = get_elf_backend_data (abfd)->type_change_ok;
if (info->hash->creator->flavour == bfd_target_elf_flavour)
{
/* We need to look up the symbol now in order to get some of
/* It's OK to change the type if it used to be a weak
definition. */
- type_change_ok = (h->root.type == bfd_link_hash_defweak
- || h->root.type == bfd_link_hash_undefweak);
+ if (h->root.type == bfd_link_hash_defweak
+ || h->root.type == bfd_link_hash_undefweak)
+ type_change_ok = true;
/* It's OK to change the size if it used to be a weak
definition, or if it used to be undefined, or if we will
- be overriding an old definition.
- */
- size_change_ok = (type_change_ok
- || h->root.type == bfd_link_hash_undefined);
+ be overriding an old definition. */
+ if (type_change_ok
+ || h->root.type == bfd_link_hash_undefined)
+ size_change_ok = true;
/* If we are looking at a dynamic object, and this is a
definition, we need to see if it has already been defined
h = *hpp;
if (h != NULL && h != hlook
- && (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
+ && h->root.type == bfd_link_hash_defined
&& h->root.u.def.section == slook
&& h->root.u.def.value == vlook)
{
return true;
}
+\f
/* Read and swap the relocs for a section. They may have been cached.
If the EXTERNAL_RELOCS and INTERNAL_RELOCS arguments are not NULL,
free (alloc2);
return NULL;
}
+\f
/* Record an assignment to a symbol made by a linker script. We need
this in case some dynamic object refers to this symbol. */
return true;
}
+\f
/* Array used to determine the number of hash table buckets to use
based on the number of symbols there are. If there are fewer than
return true;
}
+\f
/* This routine is used to export all defined symbols into the dynamic
symbol table. It is called via elf_link_hash_traverse. */
return true;
}
+\f
/* Make the backend pick a good value for a dynamic symbol. This is
called via elf_link_hash_traverse, and also calls itself
reloc_howto_type *howto;
long indx;
bfd_vma offset;
+ bfd_vma addend;
struct elf_link_hash_entry **rel_hash_ptr;
Elf_Internal_Shdr *rel_hdr;
return false;
}
+ addend = link_order->u.reloc.p->addend;
+
+ /* Figure out the symbol index. */
+ rel_hash_ptr = (elf_section_data (output_section)->rel_hashes
+ + output_section->reloc_count);
+ if (link_order->type == bfd_section_reloc_link_order)
+ {
+ indx = link_order->u.reloc.p->u.section->target_index;
+ BFD_ASSERT (indx != 0);
+ *rel_hash_ptr = NULL;
+ }
+ else
+ {
+ struct elf_link_hash_entry *h;
+
+ /* Treat a reloc against a defined symbol as though it were
+ actually against the section. */
+ h = elf_link_hash_lookup (elf_hash_table (info),
+ link_order->u.reloc.p->u.name,
+ false, false, true);
+ if (h != NULL
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak))
+ {
+ asection *section;
+
+ section = h->root.u.def.section;
+ indx = section->output_section->target_index;
+ *rel_hash_ptr = NULL;
+ /* It seems that we ought to add the symbol value to the
+ addend here, but in practice it has already been added
+ because it was passed to constructor_callback. */
+ addend += section->output_section->vma + section->output_offset;
+ }
+ else if (h != NULL)
+ {
+ /* Setting the index to -2 tells elf_link_output_extsym that
+ this symbol is used by a reloc. */
+ h->indx = -2;
+ *rel_hash_ptr = h;
+ indx = 0;
+ }
+ else
+ {
+ if (! ((*info->callbacks->unattached_reloc)
+ (info, link_order->u.reloc.p->u.name, (bfd *) NULL,
+ (asection *) NULL, (bfd_vma) 0)))
+ return false;
+ indx = 0;
+ }
+ }
+
/* If this is an inplace reloc, we must write the addend into the
object file. */
- if (howto->partial_inplace
- && link_order->u.reloc.p->addend != 0)
+ if (howto->partial_inplace && addend != 0)
{
bfd_size_type size;
bfd_reloc_status_type rstat;
buf = (bfd_byte *) bfd_zmalloc (size);
if (buf == (bfd_byte *) NULL)
return false;
- rstat = _bfd_relocate_contents (howto, output_bfd,
- link_order->u.reloc.p->addend, buf);
+ rstat = _bfd_relocate_contents (howto, output_bfd, addend, buf);
switch (rstat)
{
case bfd_reloc_ok:
? bfd_section_name (output_bfd,
link_order->u.reloc.p->u.section)
: link_order->u.reloc.p->u.name),
- howto->name, link_order->u.reloc.p->addend,
- (bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
+ howto->name, addend, (bfd *) NULL, (asection *) NULL,
+ (bfd_vma) 0)))
{
free (buf);
return false;
return false;
}
- /* Figure out the symbol index. */
- rel_hash_ptr = (elf_section_data (output_section)->rel_hashes
- + output_section->reloc_count);
- if (link_order->type == bfd_section_reloc_link_order)
- {
- indx = link_order->u.reloc.p->u.section->target_index;
- BFD_ASSERT (indx != 0);
- *rel_hash_ptr = NULL;
- }
- else
- {
- struct elf_link_hash_entry *h;
-
- h = elf_link_hash_lookup (elf_hash_table (info),
- link_order->u.reloc.p->u.name,
- false, false, true);
- if (h != NULL)
- {
- /* Setting the index to -2 tells elf_link_output_extsym that
- this symbol is used by a reloc. */
- h->indx = -2;
- *rel_hash_ptr = h;
- indx = 0;
- }
- else
- {
- if (! ((*info->callbacks->unattached_reloc)
- (info, link_order->u.reloc.p->u.name, (bfd *) NULL,
- (asection *) NULL, (bfd_vma) 0)))
- return false;
- indx = 0;
- }
- }
-
/* The address of a reloc is relative to the section in a
relocateable file, and is a virtual address in an executable
file. */
irela.r_offset = offset;
irela.r_info = ELF_R_INFO (indx, howto->type);
- irela.r_addend = link_order->u.reloc.p->addend;
+ irela.r_addend = addend;
erela = ((Elf_External_Rela *) rel_hdr->contents
+ output_section->reloc_count);
elf_swap_reloca_out (output_bfd, &irela, erela);
return true;
}
+\f
+/* Allocate a pointer to live in a linker created section. */
+
+boolean
+elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ elf_linker_section_t *lsect;
+ struct elf_link_hash_entry *h;
+ const Elf_Internal_Rela *rel;
+{
+ elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
+ elf_linker_section_pointers_t *linker_section_ptr;
+ unsigned long r_symndx = ELF_R_SYM (rel->r_info);;
+
+ BFD_ASSERT (lsect != NULL);
+
+ /* Is this a global symbol? */
+ if (h != NULL)
+ {
+ /* Has this symbol already been allocated, if so, our work is done */
+ if (_bfd_elf_find_pointer_linker_section (h->linker_section_pointer,
+ rel->r_addend,
+ lsect->which))
+ return true;
+
+ ptr_linker_section_ptr = &h->linker_section_pointer;
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (h->dynindx == -1)
+ {
+ if (! elf_link_record_dynamic_symbol (info, h))
+ return false;
+ }
+
+ if (lsect->rel_section)
+ lsect->rel_section->_raw_size += sizeof (Elf_External_Rela);
+ }
+
+ else /* Allocation of a pointer to a local symbol */
+ {
+ elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd);
+
+ /* Allocate a table to hold the local symbols if first time */
+ if (!ptr)
+ {
+ int num_symbols = elf_tdata (abfd)->symtab_hdr.sh_info;
+ register unsigned int i;
+
+ ptr = (elf_linker_section_pointers_t **)
+ bfd_alloc (abfd, num_symbols * sizeof (elf_linker_section_pointers_t *));
+
+ if (!ptr)
+ return false;
+
+ elf_local_ptr_offsets (abfd) = ptr;
+ for (i = 0; i < num_symbols; i++)
+ ptr[i] = (elf_linker_section_pointers_t *)0;
+ }
+
+ /* Has this symbol already been allocated, if so, our work is done */
+ if (_bfd_elf_find_pointer_linker_section (ptr[r_symndx],
+ rel->r_addend,
+ lsect->which))
+ return true;
+
+ ptr_linker_section_ptr = &ptr[r_symndx];
+
+ if (info->shared)
+ {
+ /* If we are generating a shared object, we need to
+ output a R_<xxx>_RELATIVE reloc so that the
+ dynamic linker can adjust this GOT entry. */
+ BFD_ASSERT (lsect->rel_section != NULL);
+ lsect->rel_section->_raw_size += sizeof (Elf_External_Rela);
+ }
+ }
+
+ /* Allocate space for a pointer in the linker section, and allocate a new pointer record
+ from internal memory. */
+ BFD_ASSERT (ptr_linker_section_ptr != NULL);
+ linker_section_ptr = (elf_linker_section_pointers_t *)
+ bfd_alloc (abfd, sizeof (elf_linker_section_pointers_t));
+
+ if (!linker_section_ptr)
+ return false;
+
+ linker_section_ptr->next = *ptr_linker_section_ptr;
+ linker_section_ptr->addend = rel->r_addend;
+ linker_section_ptr->which = lsect->which;
+ linker_section_ptr->written_address_p = false;
+ *ptr_linker_section_ptr = linker_section_ptr;
+
+ if (lsect->hole_size && lsect->hole_offset < lsect->max_hole_offset)
+ {
+ linker_section_ptr->offset = lsect->section->_raw_size - lsect->hole_size;
+ lsect->hole_offset += ARCH_SIZE / 8;
+ lsect->sym_offset += ARCH_SIZE / 8;
+ if (lsect->sym_hash) /* Bump up symbol value if needed */
+ lsect->sym_hash->root.u.def.value += ARCH_SIZE / 8;
+ }
+ else
+ linker_section_ptr->offset = lsect->section->_raw_size;
+
+ lsect->section->_raw_size += ARCH_SIZE / 8;
+
+#ifdef DEBUG
+ fprintf (stderr, "Create pointer in linker section %s, offset = %ld, section size = %ld\n",
+ lsect->name, (long)linker_section_ptr->offset, (long)lsect->section->_raw_size);
+#endif
+
+ return true;
+}
+
+\f
+#if ARCH_SIZE==64
+#define bfd_put_ptr(BFD,VAL,ADDR) bfd_put_64 (BFD, VAL, ADDR)
+#endif
+#if ARCH_SIZE==32
+#define bfd_put_ptr(BFD,VAL,ADDR) bfd_put_32 (BFD, VAL, ADDR)
+#endif
+
+/* Fill in the address for a pointer generated in alinker section. */
+
+bfd_vma
+elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h, relocation, rel, relative_reloc)
+ bfd *output_bfd;
+ bfd *input_bfd;
+ struct bfd_link_info *info;
+ elf_linker_section_t *lsect;
+ struct elf_link_hash_entry *h;
+ bfd_vma relocation;
+ const Elf_Internal_Rela *rel;
+ int relative_reloc;
+{
+ elf_linker_section_pointers_t *linker_section_ptr;
+
+ BFD_ASSERT (lsect != NULL);
+
+ if (h != NULL) /* global symbol */
+ {
+ linker_section_ptr = _bfd_elf_find_pointer_linker_section (h->linker_section_pointer,
+ rel->r_addend,
+ lsect->which);
+
+ BFD_ASSERT (linker_section_ptr != NULL);
+
+ if (! elf_hash_table (info)->dynamic_sections_created
+ || (info->shared
+ && info->symbolic
+ && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+ {
+ /* This is actually a static link, or it is a
+ -Bsymbolic link and the symbol is defined
+ locally. We must initialize this entry in the
+ global section.
+
+ When doing a dynamic link, we create a .rela.<xxx>
+ relocation entry to initialize the value. This
+ is done in the finish_dynamic_symbol routine. */
+ if (!linker_section_ptr->written_address_p)
+ {
+ linker_section_ptr->written_address_p = true;
+ bfd_put_ptr (output_bfd, relocation + linker_section_ptr->addend,
+ lsect->section->contents + linker_section_ptr->offset);
+ }
+ }
+ }
+ else /* local symbol */
+ {
+ unsigned long r_symndx = ELF_R_SYM (rel->r_info);
+ BFD_ASSERT (elf_local_ptr_offsets (input_bfd) != NULL);
+ BFD_ASSERT (elf_local_ptr_offsets (input_bfd)[r_symndx] != NULL);
+ linker_section_ptr = _bfd_elf_find_pointer_linker_section (elf_local_ptr_offsets (input_bfd)[r_symndx],
+ rel->r_addend,
+ lsect->which);
+
+ BFD_ASSERT (linker_section_ptr != NULL);
+
+ /* Write out pointer if it hasn't been rewritten out before */
+ if (!linker_section_ptr->written_address_p)
+ {
+ linker_section_ptr->written_address_p = true;
+ bfd_put_ptr (output_bfd, relocation + linker_section_ptr->addend,
+ lsect->section->contents + linker_section_ptr->offset);
+
+ if (info->shared)
+ {
+ asection *srel = lsect->rel_section;
+ Elf_Internal_Rela outrel;
+
+ /* We need to generate a relative reloc for the dynamic linker. */
+ if (!srel)
+ lsect->rel_section = srel = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
+ lsect->rel_name);
+
+ BFD_ASSERT (srel != NULL);
+
+ outrel.r_offset = (lsect->section->output_section->vma
+ + lsect->section->output_offset
+ + linker_section_ptr->offset);
+ outrel.r_info = ELF_R_INFO (0, relative_reloc);
+ outrel.r_addend = 0;
+ elf_swap_reloca_out (output_bfd, &outrel,
+ (((Elf_External_Rela *)
+ lsect->section->contents)
+ + lsect->section->reloc_count));
+ ++lsect->section->reloc_count;
+ }
+ }
+ }
+
+ relocation = (lsect->section->output_offset
+ + linker_section_ptr->offset
+ - lsect->hole_offset
+ - lsect->sym_offset);
+
+#ifdef DEBUG
+ fprintf (stderr, "Finish pointer in linker section %s, offset = %ld (0x%lx)\n",
+ lsect->name, (long)relocation, (long)relocation);
+#endif
+
+ /* Subtract out the addend, because it will get added back in by the normal
+ processing. */
+ return relocation - linker_section_ptr->addend;
+}