|
|
|
@ -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); |
|
|
|
@ -3543,6 +3647,298 @@ static const struct bfd_elf_special_section elf32_rx_special_sections[] = |
|
|
|
{ STRING_COMMA_LEN (".preinit_array"), 0, SHT_PREINIT_ARRAY, SHF_ALLOC + SHF_EXECINSTR }, |
|
|
|
{ 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 |
|
|
|
@ -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" |
|
|
|
|
|
|
|
|