1 /* Renesas / SuperH specific support for Symbian 32-bit ELF files
2    Copyright (C) 2004-2016 Free Software Foundation, Inc.
3    Contributed by Red Hat
4 
5    This file is part of BFD, the Binary File Descriptor library.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20    MA 02110-1301, USA.  */
21 
22 
23 /* Stop elf32-sh.c from defining any target vectors.  */
24 #define SH_TARGET_ALREADY_DEFINED
25 #define sh_find_elf_flags           sh_symbian_find_elf_flags
26 #define sh_elf_get_flags_from_mach  sh_symbian_elf_get_flags_from_mach
27 #include "elf32-sh.c"
28 
29 
30 //#define SYMBIAN_DEBUG 1
31 #define SYMBIAN_DEBUG 0
32 
33 #define DIRECTIVE_HEADER	"#<SYMEDIT>#\n"
34 #define DIRECTIVE_IMPORT	"IMPORT "
35 #define DIRECTIVE_EXPORT	"EXPORT "
36 #define DIRECTIVE_AS		"AS "
37 
38 /* Macro to advance 's' until either it reaches 'e' or the
39    character pointed to by 's' is equal to 'c'.  If 'e' is
40    reached and SYMBIAN_DEBUG is enabled then the error message 'm'
41    is displayed.  */
42 #define SKIP_UNTIL(s,e,c,m)					\
43   do								\
44     {								\
45       while (s < e && *s != c)					\
46 	++ s;							\
47       if (s >= e)						\
48 	{							\
49           if (SYMBIAN_DEBUG)					\
50 	    fprintf (stderr, "Corrupt directive: %s\n", m);	\
51 	  result = FALSE;					\
52 	}							\
53     }								\
54   while (0);							\
55   if (!result)							\
56      break;
57 
58 /* Like SKIP_UNTIL except there are two terminator characters
59    c1 and c2.  */
60 #define SKIP_UNTIL2(s,e,c1,c2,m)				\
61   do								\
62     {								\
63       while (s < e && *s != c1 && *s != c2)			\
64 	++ s;							\
65       if (s >= e)						\
66 	{							\
67           if (SYMBIAN_DEBUG)					\
68 	    fprintf (stderr, "Corrupt directive: %s\n", m);	\
69 	  result = FALSE;					\
70 	}							\
71     }								\
72   while (0);							\
73   if (!result)							\
74      break;
75 
76 /* Macro to advance 's' until either it reaches 'e' or the
77    character pointed to by 's' is not equal to 'c'.  If 'e'
78    is reached and SYMBIAN_DEBUG is enabled then the error message
79    'm' is displayed.  */
80 #define SKIP_WHILE(s,e,c,m)					\
81   do								\
82     {								\
83       while (s < e && *s == c)					\
84 	++ s;							\
85       if (s >= e)						\
86 	{							\
87           if (SYMBIAN_DEBUG)					\
88 	    fprintf (stderr, "Corrupt directive: %s\n", m);	\
89 	  result = FALSE;					\
90 	}							\
91     }								\
92   while (0);							\
93   if (!result)							\
94      break;
95 
96 
97 typedef struct symbol_rename
98 {
99   struct symbol_rename *       next;
100   char *                       current_name;
101   char *                       new_name;
102   struct elf_link_hash_entry * current_hash;
103   unsigned long                new_symndx;
104 }
105 symbol_rename;
106 
107 static symbol_rename * rename_list = NULL;
108 
109 /* Accumulate a list of symbols to be renamed.  */
110 
111 static bfd_boolean
sh_symbian_import_as(struct bfd_link_info * info,bfd * abfd,char * current_name,char * new_name)112 sh_symbian_import_as (struct bfd_link_info *info, bfd * abfd,
113 		      char * current_name, char * new_name)
114 {
115   struct elf_link_hash_entry * new_hash;
116   symbol_rename * node;
117 
118   if (SYMBIAN_DEBUG)
119     fprintf (stderr, "IMPORT '%s' AS '%s'\n", current_name, new_name);
120 
121   for (node = rename_list; node; node = node->next)
122     if (strcmp (node->current_name, current_name) == 0)
123       {
124 	if (strcmp (node->new_name, new_name) == 0)
125 	  /* Already added to rename list.  */
126 	  return TRUE;
127 
128 	bfd_set_error (bfd_error_invalid_operation);
129 	_bfd_error_handler (_("%B: IMPORT AS directive for %s conceals previous IMPORT AS"),
130 			    abfd, current_name);
131 	return FALSE;
132       }
133 
134   if ((node = bfd_malloc (sizeof * node)) == NULL)
135     {
136       if (SYMBIAN_DEBUG)
137 	fprintf (stderr, "IMPORT AS: No mem for new rename node\n");
138       return FALSE;
139     }
140 
141   if ((node->current_name = bfd_malloc (strlen (current_name) + 1)) == NULL)
142     {
143       if (SYMBIAN_DEBUG)
144 	fprintf (stderr, "IMPORT AS: No mem for current name field in rename node\n");
145       free (node);
146       return FALSE;
147     }
148   else
149     strcpy (node->current_name, current_name);
150 
151   if ((node->new_name = bfd_malloc (strlen (new_name) + 1)) == NULL)
152     {
153       if (SYMBIAN_DEBUG)
154 	fprintf (stderr, "IMPORT AS: No mem for new name field in rename node\n");
155       free (node->current_name);
156       free (node);
157       return FALSE;
158     }
159   else
160     strcpy (node->new_name, new_name);
161 
162   node->next = rename_list;
163   node->current_hash = NULL;
164   node->new_symndx = 0;
165   rename_list = node;
166 
167   new_hash = elf_link_hash_lookup (elf_hash_table (info), node->new_name, TRUE, FALSE, TRUE);
168   bfd_elf_link_record_dynamic_symbol (info, new_hash);
169   if (new_hash->root.type == bfd_link_hash_new)
170     new_hash->root.type = bfd_link_hash_undefined;
171 
172   return TRUE;
173 }
174 
175 
176 static bfd_boolean
sh_symbian_import(bfd * abfd ATTRIBUTE_UNUSED,char * name)177 sh_symbian_import (bfd * abfd ATTRIBUTE_UNUSED, char * name)
178 {
179   if (SYMBIAN_DEBUG)
180     fprintf (stderr, "IMPORT '%s'\n", name);
181 
182   /* XXX: Generate an import somehow ?  */
183 
184   return TRUE;
185 }
186 
187 static bfd_boolean
sh_symbian_export(bfd * abfd ATTRIBUTE_UNUSED,char * name)188 sh_symbian_export (bfd * abfd ATTRIBUTE_UNUSED, char * name)
189 {
190   if (SYMBIAN_DEBUG)
191     fprintf (stderr, "EXPORT '%s'\n", name);
192 
193   /* XXX: Generate an export somehow ?  */
194 
195   return TRUE;
196 }
197 
198 /* Process any magic embedded commands in the .directive. section.
199    Returns TRUE upon sucecss, but if it fails it sets bfd_error and
200    returns FALSE.  */
201 
202 static bfd_boolean
sh_symbian_process_embedded_commands(struct bfd_link_info * info,bfd * abfd,asection * sec,bfd_byte * contents)203 sh_symbian_process_embedded_commands (struct bfd_link_info *info, bfd * abfd,
204 				      asection * sec, bfd_byte * contents)
205 {
206   char *s;
207   char *e;
208   bfd_boolean result = TRUE;
209   bfd_size_type sz = sec->rawsize ? sec->rawsize : sec->size;
210 
211   for (s = (char *) contents, e = s + sz; s < e;)
212     {
213       char * directive = s;
214 
215       switch (*s)
216 	{
217 	  /* I want to use "case DIRECTIVE_HEADER [0]:" here but gcc won't let me :-(  */
218 	case '#':
219 	  if (strcmp (s, DIRECTIVE_HEADER))
220 	    result = FALSE;
221 	  else
222 	    /* Just ignore the header.
223 	       XXX: Strictly speaking we ought to check that the header
224 	       is present and that it is the first thing in the file.  */
225 	    s += strlen (DIRECTIVE_HEADER) + 1;
226 	  break;
227 
228 	case 'I':
229 	  if (! CONST_STRNEQ (s, DIRECTIVE_IMPORT))
230 	    result = FALSE;
231 	  else
232 	    {
233 	      char * new_name;
234 	      char * new_name_end;
235 	      char   name_end_char;
236 
237 	      /* Skip the IMPORT directive.  */
238 	      s += strlen (DIRECTIVE_IMPORT);
239 
240 	      new_name = s;
241 	      /* Find the end of the new name.  */
242 	      while (s < e && *s != ' ' && *s != '\n')
243 		++ s;
244 	      if (s >= e)
245 		{
246 		  /* We have reached the end of the .directive section
247 		     without encountering a string terminator.  This is
248 		     allowed for IMPORT directives.  */
249 		  new_name_end   = e - 1;
250 		  name_end_char  = * new_name_end;
251 		  * new_name_end = 0;
252 		  result = sh_symbian_import (abfd, new_name);
253 		  * new_name_end = name_end_char;
254 		  break;
255 		}
256 
257 	      /* Remember where the name ends.  */
258 	      new_name_end = s;
259 	      /* Skip any whitespace before the 'AS'.  */
260 	      SKIP_WHILE (s, e, ' ', "IMPORT: Name just followed by spaces");
261 	      /* Terminate the new name.  (Do this after skiping...)  */
262 	      name_end_char = * new_name_end;
263 	      * new_name_end = 0;
264 
265 	      /* Check to see if 'AS '... is present.  If so we have an
266 		 IMPORT AS directive, otherwise we have an IMPORT directive.  */
267 	      if (! CONST_STRNEQ (s, DIRECTIVE_AS))
268 		{
269 		  /* Skip the new-line at the end of the name.  */
270 		  if (SYMBIAN_DEBUG && name_end_char != '\n')
271 		    fprintf (stderr, "IMPORT: No newline at end of directive\n");
272 		  else
273 		    s ++;
274 
275 		  result = sh_symbian_import (abfd, new_name);
276 
277 		  /* Skip past the NUL character.  */
278 		  if (* s ++ != 0)
279 		    {
280 		      if (SYMBIAN_DEBUG)
281 			fprintf (stderr, "IMPORT: No NUL at end of directive\n");
282 		    }
283 		}
284 	      else
285 		{
286 		  char * current_name;
287 		  char * current_name_end;
288 		  char   current_name_end_char;
289 
290 		  /* Skip the 'AS '.  */
291 		  s += strlen (DIRECTIVE_AS);
292 		  /* Skip any white space after the 'AS '.  */
293 		  SKIP_WHILE (s, e, ' ', "IMPORT AS: Nothing after AS");
294 		  current_name = s;
295 		  /* Find the end of the current name.  */
296 		  SKIP_UNTIL2 (s, e, ' ', '\n', "IMPORT AS: No newline at the end of the current name");
297 		  /* Skip (backwards) over spaces at the end of the current name.  */
298 		  current_name_end = s;
299 		  current_name_end_char = * current_name_end;
300 
301 		  SKIP_WHILE (s, e, ' ', "IMPORT AS: Current name just followed by spaces");
302 		  /* Skip past the newline character.  */
303 		  if (* s ++ != '\n')
304 		    if (SYMBIAN_DEBUG)
305 		      fprintf (stderr, "IMPORT AS: No newline at end of directive\n");
306 
307 		  /* Terminate the current name after having performed the skips.  */
308 		  * current_name_end = 0;
309 
310 		  result = sh_symbian_import_as (info, abfd, current_name, new_name);
311 
312 		  /* The next character should be a NUL.  */
313 		  if (* s != 0)
314 		    {
315 		      if (SYMBIAN_DEBUG)
316 			fprintf (stderr, "IMPORT AS: Junk at end of directive\n");
317 		      result = FALSE;
318 		    }
319 		  s ++;
320 
321 		  * current_name_end = current_name_end_char;
322 		}
323 
324 	      /* Restore the characters we overwrote, since
325 		 the .directive section will be emitted.  */
326 	      * new_name_end = name_end_char;
327 	    }
328 	  break;
329 
330 	case 'E':
331 	  if (! CONST_STRNEQ (s, DIRECTIVE_EXPORT))
332 	    result = FALSE;
333 	  else
334 	    {
335 	      char * name;
336 	      char * name_end;
337 	      char   name_end_char;
338 
339 	      /* Skip the directive.  */
340 	      s += strlen (DIRECTIVE_EXPORT);
341 	      name = s;
342 	      /* Find the end of the name to be exported.  */
343 	      SKIP_UNTIL (s, e, '\n', "EXPORT: no newline at end of directive");
344 	      /* Skip (backwards) over spaces at end of exported name.  */
345 	      for (name_end = s; name_end[-1] == ' '; name_end --)
346 		;
347 	      /* name_end now points at the first character after the
348 		 end of the exported name, so we can termiante it  */
349 	      name_end_char = * name_end;
350 	      * name_end = 0;
351 	      /* Skip passed the newline character.  */
352 	      s ++;
353 
354 	      result = sh_symbian_export (abfd, name);
355 
356 	      /* The next character should be a NUL.  */
357 	      if (* s != 0)
358 		{
359 		  if (SYMBIAN_DEBUG)
360 		    fprintf (stderr, "EXPORT: Junk at end of directive\n");
361 		  result = FALSE;
362 		}
363 	      s++;
364 
365 	      /* Restore the character we deleted.  */
366 	      * name_end = name_end_char;
367 	    }
368 	  break;
369 
370 	default:
371 	  result = FALSE;
372 	  break;
373 	}
374 
375       if (! result)
376 	{
377 	  if (SYMBIAN_DEBUG)
378 	    fprintf (stderr, "offset into .directive section: %ld\n",
379 		     (long) (directive - (char *) contents));
380 
381 	  bfd_set_error (bfd_error_invalid_operation);
382 	  _bfd_error_handler (_("%B: Unrecognised .directive command: %s"),
383 			      abfd, directive);
384 	  break;
385 	}
386     }
387 
388   return result;
389 }
390 
391 
392 /* Scan a bfd for a .directive section, and if found process it.
393    Returns TRUE upon success, FALSE otherwise.  */
394 
395 static bfd_boolean
sh_symbian_process_directives(bfd * abfd,struct bfd_link_info * info)396 sh_symbian_process_directives (bfd *abfd, struct bfd_link_info *info)
397 {
398   bfd_boolean result = FALSE;
399   bfd_byte *  contents;
400   asection *  sec = bfd_get_section_by_name (abfd, ".directive");
401   bfd_size_type sz;
402 
403   if (!sec)
404     return TRUE;
405 
406   sz = sec->rawsize ? sec->rawsize : sec->size;
407   contents = bfd_malloc (sz);
408 
409   if (!contents)
410     bfd_set_error (bfd_error_no_memory);
411   else
412     {
413       if (bfd_get_section_contents (abfd, sec, contents, 0, sz))
414 	result = sh_symbian_process_embedded_commands (info, abfd, sec, contents);
415       free (contents);
416     }
417 
418   return result;
419 }
420 
421 /* Intercept the normal sh_relocate_section() function
422    and magle the relocs to allow for symbol renaming.  */
423 
424 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)425 sh_symbian_relocate_section (bfd *                  output_bfd,
426 			     struct bfd_link_info * info,
427 			     bfd *                  input_bfd,
428 			     asection *             input_section,
429 			     bfd_byte *             contents,
430 			     Elf_Internal_Rela *    relocs,
431 			     Elf_Internal_Sym *     local_syms,
432 			     asection **            local_sections)
433 {
434   /* When performing a final link we implement the IMPORT AS directives.  */
435   if (!bfd_link_relocatable (info))
436     {
437       Elf_Internal_Rela *            rel;
438       Elf_Internal_Rela *            relend;
439       Elf_Internal_Shdr *            symtab_hdr;
440       struct elf_link_hash_entry **  sym_hashes;
441       struct elf_link_hash_entry **  sym_hashes_end;
442       struct elf_link_hash_table *   hash_table;
443       symbol_rename *                ptr;
444       bfd_size_type                  num_global_syms;
445       unsigned long		     num_local_syms;
446 
447       BFD_ASSERT (! elf_bad_symtab (input_bfd));
448 
449       symtab_hdr       = & elf_tdata (input_bfd)->symtab_hdr;
450       hash_table       = elf_hash_table (info);
451       num_local_syms   = symtab_hdr->sh_info;
452       num_global_syms  = symtab_hdr->sh_size / sizeof (Elf32_External_Sym);
453       num_global_syms -= num_local_syms;
454       sym_hashes       = elf_sym_hashes (input_bfd);
455       sym_hashes_end   = sym_hashes + num_global_syms;
456 
457       /* First scan the rename table, caching the hash entry and the new index.  */
458       for (ptr = rename_list; ptr; ptr = ptr->next)
459 	{
460 	  struct elf_link_hash_entry *   new_hash;
461 	  struct elf_link_hash_entry **  h;
462 
463 	  ptr->current_hash = elf_link_hash_lookup (hash_table, ptr->current_name, FALSE, FALSE, TRUE);
464 
465 	  if (ptr->current_hash == NULL)
466 	    {
467 	      if (SYMBIAN_DEBUG)
468 		fprintf (stderr, "IMPORT AS: current symbol '%s' does not exist\n", ptr->current_name);
469 	      continue;
470 	    }
471 
472 	  new_hash = elf_link_hash_lookup (hash_table, ptr->new_name,
473 					   FALSE, FALSE, TRUE);
474 	  /* If we could not find the symbol then it is a new, undefined symbol.
475 	     Symbian want this behaviour - ie they want to be able to rename the
476 	     reference in a reloc from one undefined symbol to another, new and
477 	     undefined symbol.  So we create that symbol here.  */
478 	  if (new_hash == NULL)
479 	    {
480 	      struct bfd_link_hash_entry *bh = NULL;
481 	      bfd_boolean collect = get_elf_backend_data (input_bfd)->collect;
482 	      if (_bfd_generic_link_add_one_symbol (info, input_bfd,
483 						    ptr->new_name, BSF_GLOBAL,
484 						    bfd_und_section_ptr, 0,
485 						    NULL, FALSE, collect,
486 						    &bh))
487 		{
488 		  new_hash = (struct elf_link_hash_entry *) bh;
489 		  new_hash->type = STT_FUNC;
490 		  new_hash->non_elf = 0;
491 
492 		  if (SYMBIAN_DEBUG)
493 		    fprintf (stderr, "Created new symbol %s\n", ptr->new_name);
494 		}
495 	    }
496 
497 	  if (new_hash == NULL)
498 	    {
499 	      _bfd_error_handler (_("%B: Failed to add renamed symbol %s"),
500 				  input_bfd, ptr->new_name);
501 	      continue;
502 	    }
503 
504 	  /* Convert the new_hash value into a index into the table of symbol hashes.  */
505 	  for (h = sym_hashes; h < sym_hashes_end; h ++)
506 	    {
507 	      if (* h == new_hash)
508 		{
509 		  ptr->new_symndx = h - sym_hashes + num_local_syms;
510 		  if (SYMBIAN_DEBUG)
511 		    fprintf (stderr, "Converted new hash to index of %ld\n", ptr->new_symndx);
512 		  break;
513 		}
514 	    }
515 	  /* If the new symbol is not in the hash table then it must be
516 	     because it is one of the newly created undefined symbols
517 	     manufactured above.  So we extend the sym has table here to
518 	     include this extra symbol.  */
519 	  if (h == sym_hashes_end)
520 	    {
521 	      struct elf_link_hash_entry **  new_sym_hashes;
522 
523 	      /* This is not very efficient, but it works.  */
524 	      ++ num_global_syms;
525 	      new_sym_hashes = bfd_alloc (input_bfd, num_global_syms * sizeof * sym_hashes);
526 	      if (new_sym_hashes == NULL)
527 		{
528 		  if (SYMBIAN_DEBUG)
529 		    fprintf (stderr, "Out of memory extending hash table\n");
530 		  continue;
531 		}
532 	      memcpy (new_sym_hashes, sym_hashes, (num_global_syms - 1) * sizeof * sym_hashes);
533 	      new_sym_hashes[num_global_syms - 1] = new_hash;
534 	      elf_sym_hashes (input_bfd) = sym_hashes = new_sym_hashes;
535 	      sym_hashes_end = sym_hashes + num_global_syms;
536 	      symtab_hdr->sh_size  = (num_global_syms + num_local_syms) * sizeof (Elf32_External_Sym);
537 
538 	      ptr->new_symndx = num_global_syms - 1 + num_local_syms;
539 
540 	      if (SYMBIAN_DEBUG)
541 		fprintf (stderr, "Extended symbol hash table to insert new symbol as index %ld\n",
542 			 ptr->new_symndx);
543 	    }
544 	}
545 
546       /* Walk the reloc list looking for references to renamed symbols.
547 	 When we find one, we alter the index in the reloc to point to the new symbol.  */
548       for (rel = relocs, relend = relocs + input_section->reloc_count;
549 	   rel < relend;
550 	   rel ++)
551 	{
552 	  int                          r_type;
553 	  unsigned long                r_symndx;
554 	  struct elf_link_hash_entry * h;
555 
556 	  r_symndx = ELF32_R_SYM (rel->r_info);
557 	  r_type = ELF32_R_TYPE (rel->r_info);
558 
559 	  /* Ignore unused relocs.  */
560 	  if ((r_type >= (int) R_SH_GNU_VTINHERIT
561 	       && r_type <= (int) R_SH_LABEL)
562 	      || r_type == (int) R_SH_NONE
563 	      || r_type < 0
564 	      || r_type >= R_SH_max)
565 	    continue;
566 
567 	  /* Ignore relocs against local symbols.  */
568 	  if (r_symndx < num_local_syms)
569 	    continue;
570 
571 	  BFD_ASSERT (r_symndx < (num_global_syms + num_local_syms));
572 	  h = sym_hashes[r_symndx - num_local_syms];
573 	  BFD_ASSERT (h != NULL);
574 
575 	  while (   h->root.type == bfd_link_hash_indirect
576 		 || h->root.type == bfd_link_hash_warning)
577 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
578 
579 	  /* If the symbol is defined there is no need to rename it.
580 	     XXX - is this true ?  */
581 	  if (   h->root.type == bfd_link_hash_defined
582 	      || h->root.type == bfd_link_hash_defweak
583 	      || h->root.type == bfd_link_hash_undefweak)
584 	    continue;
585 
586 	  for (ptr = rename_list; ptr; ptr = ptr->next)
587 	    if (h == ptr->current_hash)
588 	      {
589 		BFD_ASSERT (ptr->new_symndx);
590 		if (SYMBIAN_DEBUG)
591 		  fprintf (stderr, "convert reloc %lx from using index %ld to using index %ld\n",
592 			   (unsigned long) rel->r_info,
593 			   (long) ELF32_R_SYM (rel->r_info), ptr->new_symndx);
594 		rel->r_info = ELF32_R_INFO (ptr->new_symndx, r_type);
595 		break;
596 	      }
597 	}
598     }
599 
600   return sh_elf_relocate_section (output_bfd, info, input_bfd, input_section,
601 				  contents, relocs, local_syms, local_sections);
602 }
603 
604 #define TARGET_LITTLE_SYM	sh_elf32_symbian_le_vec
605 #define TARGET_LITTLE_NAME      "elf32-shl-symbian"
606 
607 #undef  elf_backend_relocate_section
608 #define elf_backend_relocate_section	sh_symbian_relocate_section
609 #undef  elf_backend_check_directives
610 #define elf_backend_check_directives    sh_symbian_process_directives
611 
612 #include "elf32-target.h"
613