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