diff options
author | DJ Delorie <dj@redhat.com> | 2014-05-27 17:14:49 -0400 |
---|---|---|
committer | DJ Delorie <dj@redhat.com> | 2014-05-27 17:14:49 -0400 |
commit | 7a2f2d82fd0e757dfd6eb79637cb790959f52295 (patch) | |
tree | f83505a11ac59b3a0b866919d285d7c902647929 /bfd/elf32-rx.c | |
parent | c23dd3426c75fbf7fa9f30a4082be034818967c1 (diff) | |
download | binutils-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.c | 397 |
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" |