elf32-sh-symbian.c [plain text]
#define SH_TARGET_ALREADY_DEFINED
#define sh_find_elf_flags sh_symbian_find_elf_flags
#define sh_elf_get_flags_from_mach sh_symbian_elf_get_flags_from_mach
#include "elf32-sh.c"
#define DEBUG 0
#define DIRECTIVE_HEADER "#<SYMEDIT>#\n"
#define DIRECTIVE_IMPORT "IMPORT "
#define DIRECTIVE_EXPORT "EXPORT "
#define DIRECTIVE_AS "AS "
#define SKIP_UNTIL(s,e,c,m) \
do \
{ \
while (s < e && *s != c) \
++ s; \
if (s >= e) \
{ \
if (DEBUG) \
fprintf (stderr, "Corrupt directive: %s\n", m); \
result = FALSE; \
} \
} \
while (0); \
if (!result) \
break;
#define SKIP_UNTIL2(s,e,c1,c2,m) \
do \
{ \
while (s < e && *s != c1 && *s != c2) \
++ s; \
if (s >= e) \
{ \
if (DEBUG) \
fprintf (stderr, "Corrupt directive: %s\n", m); \
result = FALSE; \
} \
} \
while (0); \
if (!result) \
break;
#define SKIP_WHILE(s,e,c,m) \
do \
{ \
while (s < e && *s == c) \
++ s; \
if (s >= e) \
{ \
if (DEBUG) \
fprintf (stderr, "Corrupt directive: %s\n", m); \
result = FALSE; \
} \
} \
while (0); \
if (!result) \
break;
typedef struct symbol_rename
{
struct symbol_rename * next;
char * current_name;
char * new_name;
struct elf_link_hash_entry * current_hash;
unsigned long new_symndx;
}
symbol_rename;
static symbol_rename * rename_list = NULL;
static bfd_boolean
sh_symbian_import_as (struct bfd_link_info *info, bfd * abfd,
char * current_name, char * new_name)
{
struct elf_link_hash_entry * new_hash;
symbol_rename * node;
if (DEBUG)
fprintf (stderr, "IMPORT '%s' AS '%s'\n", current_name, new_name);
for (node = rename_list; node; node = node->next)
if (strcmp (node->current_name, current_name) == 0)
{
if (strcmp (node->new_name, new_name) == 0)
return TRUE;
bfd_set_error (bfd_error_invalid_operation);
_bfd_error_handler (_("%B: IMPORT AS directive for %s conceals previous IMPORT AS"),
abfd, current_name);
return FALSE;
}
if ((node = bfd_malloc (sizeof * node)) == NULL)
{
if (DEBUG)
fprintf (stderr, "IMPORT AS: No mem for new rename node\n");
return FALSE;
}
if ((node->current_name = bfd_malloc (strlen (current_name) + 1)) == NULL)
{
if (DEBUG)
fprintf (stderr, "IMPORT AS: No mem for current name field in rename node\n");
free (node);
return FALSE;
}
else
strcpy (node->current_name, current_name);
if ((node->new_name = bfd_malloc (strlen (new_name) + 1)) == NULL)
{
if (DEBUG)
fprintf (stderr, "IMPORT AS: No mem for new name field in rename node\n");
free (node->current_name);
free (node);
return FALSE;
}
else
strcpy (node->new_name, new_name);
node->next = rename_list;
node->current_hash = NULL;
node->new_symndx = 0;
rename_list = node;
new_hash = elf_link_hash_lookup (elf_hash_table (info), node->new_name, TRUE, FALSE, TRUE);
bfd_elf_link_record_dynamic_symbol (info, new_hash);
if (new_hash->root.type == bfd_link_hash_new)
new_hash->root.type = bfd_link_hash_undefined;
return TRUE;
}
static bfd_boolean
sh_symbian_import (bfd * abfd ATTRIBUTE_UNUSED, char * name)
{
if (DEBUG)
fprintf (stderr, "IMPORT '%s'\n", name);
return TRUE;
}
static bfd_boolean
sh_symbian_export (bfd * abfd ATTRIBUTE_UNUSED, char * name)
{
if (DEBUG)
fprintf (stderr, "EXPORT '%s'\n", name);
return TRUE;
}
static bfd_boolean
sh_symbian_process_embedded_commands (struct bfd_link_info *info, bfd * abfd,
asection * sec, bfd_byte * contents)
{
char *s;
char *e;
bfd_boolean result = TRUE;
bfd_size_type sz = sec->rawsize ? sec->rawsize : sec->size;
for (s = (char *) contents, e = s + sz; s < e;)
{
char * directive = s;
switch (*s)
{
case '#':
if (strcmp (s, DIRECTIVE_HEADER))
result = FALSE;
else
s += strlen (DIRECTIVE_HEADER) + 1;
break;
case 'I':
if (strncmp (s, DIRECTIVE_IMPORT, strlen (DIRECTIVE_IMPORT)))
result = FALSE;
else
{
char * new_name;
char * new_name_end;
char name_end_char;
s += strlen (DIRECTIVE_IMPORT);
new_name = s;
while (s < e && *s != ' ' && *s != '\n')
++ s;
if (s >= e)
{
new_name_end = e - 1;
name_end_char = * new_name_end;
* new_name_end = 0;
result = sh_symbian_import (abfd, new_name);
* new_name_end = name_end_char;
break;
}
new_name_end = s;
SKIP_WHILE (s, e, ' ', "IMPORT: Name just followed by spaces");
name_end_char = * new_name_end;
* new_name_end = 0;
if (strncmp (s, DIRECTIVE_AS, strlen (DIRECTIVE_AS)))
{
if (DEBUG && name_end_char != '\n')
fprintf (stderr, "IMPORT: No newline at end of directive\n");
else
s ++;
result = sh_symbian_import (abfd, new_name);
if (* s ++ != 0)
{
if (DEBUG)
fprintf (stderr, "IMPORT: No NUL at end of directive\n");
}
}
else
{
char * current_name;
char * current_name_end;
char current_name_end_char;
s += strlen (DIRECTIVE_AS);
SKIP_WHILE (s, e, ' ', "IMPORT AS: Nothing after AS");
current_name = s;
SKIP_UNTIL2 (s, e, ' ', '\n', "IMPORT AS: No newline at the end of the current name");
current_name_end = s;
current_name_end_char = * current_name_end;
SKIP_WHILE (s, e, ' ', "IMPORT AS: Current name just followed by spaces");
if (* s ++ != '\n')
if (DEBUG)
fprintf (stderr, "IMPORT AS: No newline at end of directive\n");
* current_name_end = 0;
result = sh_symbian_import_as (info, abfd, current_name, new_name);
if (* s != 0)
{
if (DEBUG)
fprintf (stderr, "IMPORT AS: Junk at end of directive\n");
result = FALSE;
}
s ++;
* current_name_end = current_name_end_char;
}
* new_name_end = name_end_char;
}
break;
case 'E':
if (strncmp (s, DIRECTIVE_EXPORT, strlen (DIRECTIVE_EXPORT)))
result = FALSE;
else
{
char * name;
char * name_end;
char name_end_char;
s += strlen (DIRECTIVE_EXPORT);
name = s;
SKIP_UNTIL (s, e, '\n', "EXPORT: no newline at end of directive");
for (name_end = s; name_end[-1] == ' '; name_end --)
;
name_end_char = * name_end;
* name_end = 0;
s ++;
result = sh_symbian_export (abfd, name);
if (* s != 0)
{
if (DEBUG)
fprintf (stderr, "EXPORT: Junk at end of directive\n");
result = FALSE;
}
s++;
* name_end = name_end_char;
}
break;
default:
result = FALSE;
break;
}
if (! result)
{
if (DEBUG)
fprintf (stderr, "offset into .directive section: %ld\n",
(long) (directive - (char *) contents));
bfd_set_error (bfd_error_invalid_operation);
_bfd_error_handler (_("%B: Unrecognised .directive command: %s"),
abfd, directive);
break;
}
}
return result;
}
bfd_boolean bfd_elf32_sh_symbian_process_directives (struct bfd_link_info *info, bfd * abfd);
bfd_boolean
bfd_elf32_sh_symbian_process_directives (struct bfd_link_info *info, bfd * abfd)
{
bfd_boolean result = FALSE;
bfd_byte * contents;
asection * sec = bfd_get_section_by_name (abfd, ".directive");
bfd_size_type sz;
if (!sec)
return TRUE;
sz = sec->rawsize ? sec->rawsize : sec->size;
contents = bfd_malloc (sz);
if (!contents)
bfd_set_error (bfd_error_no_memory);
else
{
if (bfd_get_section_contents (abfd, sec, contents, 0, sz))
result = sh_symbian_process_embedded_commands (info, abfd, sec, contents);
free (contents);
}
return result;
}
static bfd_boolean
sh_symbian_relocate_section (bfd * output_bfd,
struct bfd_link_info * info,
bfd * input_bfd,
asection * input_section,
bfd_byte * contents,
Elf_Internal_Rela * relocs,
Elf_Internal_Sym * local_syms,
asection ** local_sections)
{
if (!info->relocatable)
{
Elf_Internal_Rela * rel;
Elf_Internal_Rela * relend;
Elf_Internal_Shdr * symtab_hdr;
struct elf_link_hash_entry ** sym_hashes;
struct elf_link_hash_entry ** sym_hashes_end;
struct elf_link_hash_table * hash_table;
symbol_rename * ptr;
bfd_size_type num_global_syms;
unsigned long num_local_syms;
BFD_ASSERT (! elf_bad_symtab (input_bfd));
symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
hash_table = elf_hash_table (info);
num_local_syms = symtab_hdr->sh_info;
num_global_syms = symtab_hdr->sh_size / sizeof (Elf32_External_Sym);
num_global_syms -= num_local_syms;
sym_hashes = elf_sym_hashes (input_bfd);
sym_hashes_end = sym_hashes + num_global_syms;
for (ptr = rename_list; ptr; ptr = ptr->next)
{
struct elf_link_hash_entry * new_hash;
struct elf_link_hash_entry ** h;
ptr->current_hash = elf_link_hash_lookup (hash_table, ptr->current_name, FALSE, FALSE, TRUE);
if (ptr->current_hash == NULL)
{
if (DEBUG)
fprintf (stderr, "IMPORT AS: current symbol '%s' does not exist\n", ptr->current_name);
continue;
}
new_hash = elf_link_hash_lookup (hash_table, ptr->new_name, FALSE, FALSE, TRUE);
if (new_hash == NULL)
{
asection * psec = bfd_und_section_ptr;
Elf_Internal_Sym new_sym;
bfd_vma new_value = 0;
bfd_boolean skip;
bfd_boolean override;
bfd_boolean type_change_ok;
bfd_boolean size_change_ok;
new_sym.st_value = 0;
new_sym.st_size = 0;
new_sym.st_name = -1;
new_sym.st_info = ELF_ST_INFO (STB_GLOBAL, STT_FUNC);
new_sym.st_other = ELF_ST_VISIBILITY (STV_DEFAULT);
new_sym.st_shndx = SHN_UNDEF;
if (! _bfd_elf_merge_symbol (input_bfd, info,
ptr->new_name, & new_sym,
& psec, & new_value, NULL,
& new_hash, & skip,
& override, & type_change_ok,
& size_change_ok))
{
_bfd_error_handler (_("%B: Failed to add renamed symbol %s"),
input_bfd, ptr->new_name);
continue;
}
new_hash->root.type = bfd_link_hash_undefined;
if (new_hash->dynindx == -1)
new_hash->def_regular = 1;
if (DEBUG)
fprintf (stderr, "Created new symbol %s\n", ptr->new_name);
}
for (h = sym_hashes; h < sym_hashes_end; h ++)
{
if (* h == new_hash)
{
ptr->new_symndx = h - sym_hashes + num_local_syms;
if (DEBUG)
fprintf (stderr, "Converted new hash to index of %ld\n", ptr->new_symndx);
break;
}
}
if (h == sym_hashes_end)
{
struct elf_link_hash_entry ** new_sym_hashes;
++ num_global_syms;
new_sym_hashes = bfd_alloc (input_bfd, num_global_syms * sizeof * sym_hashes);
if (new_sym_hashes == NULL)
{
if (DEBUG)
fprintf (stderr, "Out of memory extending hash table\n");
continue;
}
memcpy (new_sym_hashes, sym_hashes, (num_global_syms - 1) * sizeof * sym_hashes);
new_sym_hashes[num_global_syms - 1] = new_hash;
elf_sym_hashes (input_bfd) = sym_hashes = new_sym_hashes;
sym_hashes_end = sym_hashes + num_global_syms;
symtab_hdr->sh_size = (num_global_syms + num_local_syms) * sizeof (Elf32_External_Sym);
ptr->new_symndx = num_global_syms - 1 + num_local_syms;
if (DEBUG)
fprintf (stderr, "Extended symbol hash table to insert new symbol as index %ld\n",
ptr->new_symndx);
}
}
for (rel = relocs, relend = relocs + input_section->reloc_count;
rel < relend;
rel ++)
{
int r_type;
unsigned long r_symndx;
struct elf_link_hash_entry * h;
r_symndx = ELF32_R_SYM (rel->r_info);
r_type = ELF32_R_TYPE (rel->r_info);
if ((r_type >= (int) R_SH_GNU_VTINHERIT
&& r_type <= (int) R_SH_LABEL)
|| r_type == (int) R_SH_NONE
|| r_type < 0
|| r_type >= R_SH_max)
continue;
if (r_symndx < num_local_syms)
continue;
BFD_ASSERT (r_symndx < (num_global_syms + num_local_syms));
h = sym_hashes[r_symndx - num_local_syms];
BFD_ASSERT (h != NULL);
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->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak
|| h->root.type == bfd_link_hash_undefweak)
continue;
for (ptr = rename_list; ptr; ptr = ptr->next)
if (h == ptr->current_hash)
{
BFD_ASSERT (ptr->new_symndx);
if (DEBUG)
fprintf (stderr, "convert reloc %lx from using index %ld to using index %ld\n",
(long) rel->r_info, (long) ELF32_R_SYM (rel->r_info), ptr->new_symndx);
rel->r_info = ELF32_R_INFO (ptr->new_symndx, r_type);
break;
}
}
}
return sh_elf_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, local_syms, local_sections);
}
static bfd_boolean
sh_symbian_check_directives (bfd *abfd, struct bfd_link_info *info)
{
return bfd_elf32_sh_symbian_process_directives (info, abfd);
}
#define TARGET_LITTLE_SYM bfd_elf32_shl_symbian_vec
#define TARGET_LITTLE_NAME "elf32-shl-symbian"
#undef elf_backend_relocate_section
#define elf_backend_relocate_section sh_symbian_relocate_section
#undef elf_backend_check_directives
#define elf_backend_check_directives sh_symbian_check_directives
#include "elf32-target.h"