aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-rx.c
diff options
context:
space:
mode:
authorDJ Delorie <dj@redhat.com>2014-05-27 17:14:49 -0400
committerDJ Delorie <dj@redhat.com>2014-05-27 17:14:49 -0400
commit7a2f2d82fd0e757dfd6eb79637cb790959f52295 (patch)
treef83505a11ac59b3a0b866919d285d7c902647929 /bfd/elf32-rx.c
parentc23dd3426c75fbf7fa9f30a4082be034818967c1 (diff)
downloadbinutils-7a2f2d82fd0e757dfd6eb79637cb790959f52295.zip
binutils-7a2f2d82fd0e757dfd6eb79637cb790959f52295.tar.gz
binutils-7a2f2d82fd0e757dfd6eb79637cb790959f52295.tar.bz2
Add new link-map-text hook and RX auto-vector support.
* bfd/elf32-rx.c (get_symbol_value_maybe): New. (rx_elf_relocate_section): If we find a reloc against $tableentry$default$<name>, redirect it to the appropriate $tableentry$<n>$. (RX_Table_Info): New. (rx_table_find): New. Check all tables and SEC_KEEP all sections with table parts in them. (rx_check_directives): New. (rx_table_map_2): New. (rx_table_map): New. (rx_additional_link_map_text): New. Called to dump tables to the map file. * bfd/elf32-rx.h: New. * ld/ldemul.h (extra_map_file_text): New field. (ldemul_extra_map_file_text): Declare. * ld/ldemul.c (ldemul_extra_map_file_text): Define. * ld/ldlang.c (lang_map): Call it. * ld/emultempl/rxelf.em: Add extra_map_file_text hook. * ld/emultempl/aix.em: Add NULL extra_map_file_text hook. * ld/emultempl/armcoff.em: Likewise. * ld/emultempl/beos.em: Likewise. * ld/emultempl/elf32.em: Likewise. * ld/emultempl/generic.em: Likewise. * ld/emultempl/gld960.em: Likewise. * ld/emultempl/gld960c.em: Likewise. * ld/emultempl/linux.em: Likewise. * ld/emultempl/lnk960.em: Likewise. * ld/emultempl/m68kcoff.em: Likewise. * ld/emultempl/pe.em: Likewise. * ld/emultempl/pep.em: Likewise. * ld/emultempl/sunos.em: Likewise. * ld/emultempl/ticoff.em: Likewise. * ld/emultempl/vanilla.em: Likewise.
Diffstat (limited to 'bfd/elf32-rx.c')
-rw-r--r--bfd/elf32-rx.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/bfd/elf32-rx.c b/bfd/elf32-rx.c
index 2045cb7..e1856a9 100644
--- a/bfd/elf32-rx.c
+++ b/bfd/elf32-rx.c
@@ -24,6 +24,7 @@
#include "elf-bfd.h"
#include "elf/rx.h"
#include "libiberty.h"
+#include "elf32-rx.h"
#define RX_OPCODE_BIG_ENDIAN 0
@@ -335,6 +336,26 @@ get_symbol_value (const char * name,
return value;
}
+static bfd_vma
+get_symbol_value_maybe (const char * name,
+ struct bfd_link_info * info)
+{
+ bfd_vma value = 0;
+ struct bfd_link_hash_entry * h;
+
+ h = bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, TRUE);
+
+ if (h == NULL
+ || (h->type != bfd_link_hash_defined
+ && h->type != bfd_link_hash_defweak))
+ return 0;
+ else
+ value = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+
+ return value;
+}
static bfd_vma
get_gp (bfd_reloc_status_type * status,
@@ -464,6 +485,9 @@ rx_elf_relocate_section
Elf_Internal_Rela * relend;
bfd_boolean pid_mode;
bfd_boolean saw_subtract = FALSE;
+ const char * table_default_cache = NULL;
+ bfd_vma table_start_cache = 0;
+ bfd_vma table_end_cache = 0;
if (elf_elfheader (output_bfd)->e_flags & E_FLAG_RX_PID)
pid_mode = TRUE;
@@ -520,6 +544,86 @@ rx_elf_relocate_section
name = h->root.root.string;
}
+ if (strncmp (name, "$tableentry$default$", 20) == 0)
+ {
+ bfd_vma entry_vma;
+ int idx;
+ char *buf;
+ bfd_reloc_status_type tstat = 0;
+
+ if (table_default_cache != name)
+ {
+
+ /* All relocs for a given table should be to the same
+ (weak) default symbol) so we can use it to detect a
+ cache miss. We use the offset into the table to find
+ the "real" symbol. Calculate and store the table's
+ offset here. */
+
+ table_default_cache = name;
+
+ /* We have already done error checking in rx_table_find(). */
+
+ buf = (char *) malloc (13 + strlen (name + 20));
+
+ sprintf (buf, "$tablestart$%s", name + 20);
+ tstat = 0;
+ table_start_cache = get_symbol_value (buf,
+ &tstat,
+ info,
+ input_bfd,
+ input_section,
+ rel->r_offset);
+
+ sprintf (buf, "$tableend$%s", name + 20);
+ tstat = 0;
+ table_end_cache = get_symbol_value (buf,
+ &tstat,
+ info,
+ input_bfd,
+ input_section,
+ rel->r_offset);
+
+ free (buf);
+ }
+
+ entry_vma = (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+
+ if (table_end_cache <= entry_vma || entry_vma < table_start_cache)
+ {
+ _bfd_error_handler (_("%B:%A: table entry %s outside table"),
+ input_bfd, input_section,
+ name);
+ }
+ else if ((int) (entry_vma - table_start_cache) % 4)
+ {
+ _bfd_error_handler (_("%B:%A: table entry %s not word-aligned within table"),
+ input_bfd, input_section,
+ name);
+ }
+ else
+ {
+ idx = (int) (entry_vma - table_start_cache) / 4;
+
+ /* This will look like $tableentry$<N>$<name> */
+ buf = (char *) malloc (12 + 20 + strlen (name + 20));
+ sprintf (buf, "$tableentry$%d$%s", idx, name + 20);
+
+ h = (struct elf_link_hash_entry *) bfd_link_hash_lookup (info->hash, buf, FALSE, FALSE, TRUE);
+
+ if (h)
+ {
+ relocation = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);;
+ }
+
+ free (buf);
+ }
+ }
+
if (sec != NULL && discarded_section (sec))
RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
rel, 1, relend, howto, 0, contents);
@@ -3544,6 +3648,298 @@ static const struct bfd_elf_special_section elf32_rx_special_sections[] =
{ NULL, 0, 0, 0, 0 }
};
+typedef struct {
+ bfd *abfd;
+ struct bfd_link_info *info;
+ bfd_vma table_start;
+ int table_size;
+ bfd_vma *table_handlers;
+ bfd_vma table_default_handler;
+ struct bfd_link_hash_entry **table_entries;
+ struct bfd_link_hash_entry *table_default_entry;
+ FILE *mapfile;
+} RX_Table_Info;
+
+static bfd_boolean
+rx_table_find (struct bfd_hash_entry *vent, void *vinfo)
+{
+ RX_Table_Info *info = (RX_Table_Info *)vinfo;
+ struct bfd_link_hash_entry *ent = (struct bfd_link_hash_entry *)vent;
+ const char *name; /* of the symbol we've found */
+ asection *sec;
+ struct bfd *abfd;
+ int idx;
+ const char *tname; /* name of the table */
+ bfd_vma start_addr, end_addr;
+ char *buf;
+ struct bfd_link_hash_entry * h;
+
+ /* We're looking for globally defined symbols of the form
+ $tablestart$<NAME>. */
+ if (ent->type != bfd_link_hash_defined
+ && ent->type != bfd_link_hash_defweak)
+ return TRUE;
+
+ name = ent->root.string;
+ sec = ent->u.def.section;
+ abfd = sec->owner;
+
+ if (strncmp (name, "$tablestart$", 12))
+ return TRUE;
+
+ sec->flags |= SEC_KEEP;
+
+ tname = name + 12;
+
+ start_addr = ent->u.def.value;
+
+ /* At this point, we can't build the table but we can (and must)
+ find all the related symbols and mark their sections as SEC_KEEP
+ so we don't garbage collect them. */
+
+ buf = (char *) malloc (12 + 10 + strlen (tname));
+
+ sprintf (buf, "$tableend$%s", tname);
+ h = bfd_link_hash_lookup (info->info->hash, buf, FALSE, FALSE, TRUE);
+ if (!h || (h->type != bfd_link_hash_defined
+ && h->type != bfd_link_hash_defweak))
+ {
+ _bfd_error_handler (_("%B:%A: table %s missing corresponding %s"),
+ abfd, sec, name, buf);
+ return TRUE;
+ }
+
+ if (h->u.def.section != ent->u.def.section)
+ {
+ _bfd_error_handler (_("%B:%A: %s and %s must be in the same input section"),
+ h->u.def.section->owner, h->u.def.section,
+ name, buf);
+ return TRUE;
+ }
+
+ end_addr = h->u.def.value;
+
+ sprintf (buf, "$tableentry$default$%s", tname);
+ h = bfd_link_hash_lookup (info->info->hash, buf, FALSE, FALSE, TRUE);
+ if (h && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak))
+ {
+ h->u.def.section->flags |= SEC_KEEP;
+ }
+
+ for (idx = 0; idx < (int) (end_addr - start_addr) / 4; idx ++)
+ {
+ sprintf (buf, "$tableentry$%d$%s", idx, tname);
+ h = bfd_link_hash_lookup (info->info->hash, buf, FALSE, FALSE, TRUE);
+ if (h && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak))
+ {
+ h->u.def.section->flags |= SEC_KEEP;
+ }
+ }
+
+ /* Return TRUE to keep scanning, FALSE to end the traversal. */
+ return TRUE;
+}
+
+/* We need to check for table entry symbols and build the tables, and
+ we need to do it before the linker does garbage collection. This function is
+ called once per input object file. */
+static bfd_boolean
+rx_check_directives
+ (bfd * abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info * info ATTRIBUTE_UNUSED)
+{
+ RX_Table_Info stuff;
+
+ stuff.abfd = abfd;
+ stuff.info = info;
+ bfd_hash_traverse (&(info->hash->table), rx_table_find, &stuff);
+
+ return TRUE;
+}
+
+
+static bfd_boolean
+rx_table_map_2 (struct bfd_hash_entry *vent, void *vinfo)
+{
+ RX_Table_Info *info = (RX_Table_Info *)vinfo;
+ struct bfd_link_hash_entry *ent = (struct bfd_link_hash_entry *)vent;
+ int idx;
+ const char *name;
+ bfd_vma addr;
+
+ /* See if the symbol ENT has an address listed in the table, and
+ isn't a debug/special symbol. If so, put it in the table. */
+
+ if (ent->type != bfd_link_hash_defined
+ && ent->type != bfd_link_hash_defweak)
+ return TRUE;
+
+ name = ent->root.string;
+
+ if (name[0] == '$' || name[0] == '.' || name[0] < ' ')
+ return TRUE;
+
+ addr = (ent->u.def.value
+ + ent->u.def.section->output_section->vma
+ + ent->u.def.section->output_offset);
+
+ for (idx = 0; idx < info->table_size; idx ++)
+ if (addr == info->table_handlers[idx])
+ info->table_entries[idx] = ent;
+
+ if (addr == info->table_default_handler)
+ info->table_default_entry = ent;
+
+ return TRUE;
+}
+
+static bfd_boolean
+rx_table_map (struct bfd_hash_entry *vent, void *vinfo)
+{
+ RX_Table_Info *info = (RX_Table_Info *)vinfo;
+ struct bfd_link_hash_entry *ent = (struct bfd_link_hash_entry *)vent;
+ const char *name; /* of the symbol we've found */
+ asection *sec;
+ struct bfd *abfd;
+ int idx;
+ const char *tname; /* name of the table */
+ bfd_vma start_addr, end_addr;
+ char *buf;
+ struct bfd_link_hash_entry * h;
+ int need_elipses;
+
+ /* We're looking for globally defined symbols of the form
+ $tablestart$<NAME>. */
+ if (ent->type != bfd_link_hash_defined
+ && ent->type != bfd_link_hash_defweak)
+ return TRUE;
+
+ name = ent->root.string;
+ sec = ent->u.def.section;
+ abfd = sec->owner;
+
+ if (strncmp (name, "$tablestart$", 12))
+ return TRUE;
+
+ tname = name + 12;
+ start_addr = (ent->u.def.value
+ + ent->u.def.section->output_section->vma
+ + ent->u.def.section->output_offset);
+
+ buf = (char *) malloc (12 + 10 + strlen (tname));
+
+ sprintf (buf, "$tableend$%s", tname);
+ end_addr = get_symbol_value_maybe (buf, info->info);
+
+ sprintf (buf, "$tableentry$default$%s", tname);
+ h = bfd_link_hash_lookup (info->info->hash, buf, FALSE, FALSE, TRUE);
+ if (h)
+ {
+ info->table_default_handler = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+ }
+ else
+ /* Zero is a valid handler address! */
+ info->table_default_handler = (bfd_vma) (-1);
+ info->table_default_entry = NULL;
+
+ info->table_start = start_addr;
+ info->table_size = (int) (end_addr - start_addr) / 4;
+ info->table_handlers = (bfd_vma *) malloc (info->table_size * sizeof (bfd_vma));
+ info->table_entries = (struct bfd_link_hash_entry **) malloc (info->table_size * sizeof (struct bfd_link_hash_entry));
+
+ for (idx = 0; idx < (int) (end_addr - start_addr) / 4; idx ++)
+ {
+ sprintf (buf, "$tableentry$%d$%s", idx, tname);
+ h = bfd_link_hash_lookup (info->info->hash, buf, FALSE, FALSE, TRUE);
+ if (h && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak))
+ {
+ info->table_handlers[idx] = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+ }
+ else
+ info->table_handlers[idx] = info->table_default_handler;
+ info->table_entries[idx] = NULL;
+ }
+
+ free (buf);
+
+ bfd_hash_traverse (&(info->info->hash->table), rx_table_map_2, info);
+
+ fprintf (info->mapfile, "\nRX Vector Table: %s has %d entries at 0x%08lx\n\n",
+ tname, info->table_size, start_addr);
+
+ if (info->table_default_entry)
+ fprintf (info->mapfile, " default handler is: %s at 0x%08lx\n",
+ info->table_default_entry->root.string,
+ info->table_default_handler);
+ else if (info->table_default_handler != (bfd_vma)(-1))
+ fprintf (info->mapfile, " default handler is at 0x%08lx\n",
+ info->table_default_handler);
+ else
+ fprintf (info->mapfile, " no default handler\n");
+
+ need_elipses = 1;
+ for (idx = 0; idx < info->table_size; idx ++)
+ {
+ if (info->table_handlers[idx] == info->table_default_handler)
+ {
+ if (need_elipses)
+ fprintf (info->mapfile, " . . .\n");
+ need_elipses = 0;
+ continue;
+ }
+ need_elipses = 1;
+
+ fprintf (info->mapfile, " 0x%08lx [%3d] ", start_addr + 4 * idx, idx);
+
+ if (info->table_handlers[idx] == (bfd_vma) (-1))
+ fprintf (info->mapfile, "(no handler found)\n");
+
+ else if (info->table_handlers[idx] == info->table_default_handler)
+ {
+ if (info->table_default_entry)
+ fprintf (info->mapfile, "(default)\n");
+ else
+ fprintf (info->mapfile, "(default)\n");
+ }
+
+ else if (info->table_entries[idx])
+ {
+ fprintf (info->mapfile, "0x%08lx %s\n", info->table_handlers[idx], info->table_entries[idx]->root.string);
+ }
+
+ else
+ {
+ fprintf (info->mapfile, "0x%08lx ???\n", info->table_handlers[idx]);
+ }
+ }
+ if (need_elipses)
+ fprintf (info->mapfile, " . . .\n");
+
+ return TRUE;
+}
+
+void
+rx_additional_link_map_text (bfd *obfd, struct bfd_link_info *info, FILE *mapfile)
+{
+ /* We scan the symbol table looking for $tableentry$'s, and for
+ each, try to deduce which handlers go with which entries. */
+
+ RX_Table_Info stuff;
+
+ stuff.abfd = obfd;
+ stuff.info = info;
+ stuff.mapfile = mapfile;
+ bfd_hash_traverse (&(info->hash->table), rx_table_map, &stuff);
+}
+
+
#define ELF_ARCH bfd_arch_rx
#define ELF_MACHINE_CODE EM_RX
#define ELF_MAXPAGESIZE 0x1000
@@ -3572,6 +3968,7 @@ static const struct bfd_elf_special_section elf32_rx_special_sections[] =
#define bfd_elf32_bfd_final_link rx_final_link
#define bfd_elf32_bfd_relax_section elf32_rx_relax_section_wrapper
#define elf_backend_special_sections elf32_rx_special_sections
+#define elf_backend_check_directives rx_check_directives
#include "elf32-target.h"