[ARC] Refactored location where GOT information is collected.
[external/binutils.git] / bfd / arc-got.h
1 /* ARC-specific support for 32-bit ELF
2    Copyright (C) 1994-2018 Free Software Foundation, Inc.
3    Contributed by Cupertino Miranda (cmiranda@synopsys.com).
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 #ifndef ARC_GOT_H
23 #define ARC_GOT_H
24
25 #define TCB_SIZE (8)
26
27 enum tls_type_e
28 {
29   GOT_UNKNOWN = 0,
30   GOT_NORMAL,
31   GOT_TLS_GD,
32   GOT_TLS_IE,
33   GOT_TLS_LE
34 };
35
36 enum tls_got_entries
37 {
38   TLS_GOT_NONE = 0,
39   TLS_GOT_MOD,
40   TLS_GOT_OFF,
41   TLS_GOT_MOD_AND_OFF
42 };
43
44 struct got_entry
45 {
46   struct got_entry *next;
47   enum tls_type_e type;
48   bfd_vma offset;
49   bfd_boolean processed;
50   bfd_boolean created_dyn_relocation;
51   enum tls_got_entries existing_entries;
52 };
53
54 static struct got_entry **
55 arc_get_local_got_ents (bfd * abfd)
56 {
57   static struct got_entry **local_got_ents = NULL;
58
59   if (local_got_ents == NULL)
60     {
61       size_t       size;
62       Elf_Internal_Shdr *symtab_hdr = &((elf_tdata (abfd))->symtab_hdr);
63
64       size = symtab_hdr->sh_info * sizeof (bfd_vma);
65       local_got_ents = (struct got_entry **)
66         bfd_alloc (abfd, sizeof (struct got_entry *) * size);
67       if (local_got_ents == NULL)
68         return FALSE;
69
70       memset (local_got_ents, 0, sizeof (struct got_entry *) * size);
71       elf_local_got_ents (abfd) = local_got_ents;
72     }
73
74   return local_got_ents;
75 }
76
77 static struct got_entry *
78 got_entry_for_type (struct got_entry **list,
79                     enum tls_type_e type)
80 {
81   struct got_entry **p = list;
82
83   while (*p != NULL)
84     {
85       if ((*p)->type == type)
86         return *p;
87       p = &((*p)->next);
88     }
89   return NULL;
90 }
91
92 static void
93 new_got_entry_to_list (struct got_entry **list,
94                        enum tls_type_e type,
95                        bfd_vma offset,
96                        enum tls_got_entries existing_entries)
97 {
98   /* Find list end.  Avoid having multiple entries of the same
99      type.  */
100   struct got_entry **p = list;
101   struct got_entry *entry;
102
103   while (*p != NULL)
104     {
105       if ((*p)->type == type)
106         return;
107       p = &((*p)->next);
108     }
109
110   entry = (struct got_entry *) xmalloc (sizeof (struct got_entry));
111
112   entry->type = type;
113   entry->offset = offset;
114   entry->next = NULL;
115   entry->processed = FALSE;
116   entry->created_dyn_relocation = FALSE;
117   entry->existing_entries = existing_entries;
118
119   ARC_DEBUG ("New GOT got entry added to list: "
120              "type: %d, offset: %ld, existing_entries: %d\n",
121              type, (long) offset, existing_entries);
122
123   /* Add the entry to the end of the list.  */
124   *p = entry;
125 }
126
127 static enum tls_type_e
128 tls_type_for_reloc (reloc_howto_type *howto)
129 {
130   enum tls_type_e ret = GOT_UNKNOWN;
131
132   if (is_reloc_for_GOT (howto))
133     return GOT_NORMAL;
134
135   switch (howto->type)
136     {
137     case R_ARC_TLS_GD_GOT:
138       ret = GOT_TLS_GD;
139       break;
140     case R_ARC_TLS_IE_GOT:
141       ret = GOT_TLS_IE;
142       break;
143     case R_ARC_TLS_LE_32:
144       ret = GOT_TLS_LE;
145       break;
146     default:
147       ret = GOT_UNKNOWN;
148       break;
149     }
150
151   return ret;
152 };
153
154 static struct got_entry **
155 get_got_entry_list_for_symbol (bfd *abfd,
156                                unsigned long r_symndx,
157                                struct elf_link_hash_entry *h)
158 {
159   struct elf_arc_link_hash_entry *h1 =
160     ((struct elf_arc_link_hash_entry *) h);
161   if (h1 != NULL)
162     {
163       return &h1->got_ents;
164     }
165   else
166     {
167       struct got_entry **local_got_ents
168         = arc_get_local_got_ents (abfd);
169       return &local_got_ents[r_symndx];
170     }
171 }
172
173
174 static enum tls_type_e
175 arc_got_entry_type_for_reloc (reloc_howto_type *howto)
176 {
177   enum tls_type_e type = GOT_UNKNOWN;
178
179   if (is_reloc_for_GOT (howto))
180     return  GOT_NORMAL;
181
182   if (is_reloc_for_TLS (howto))
183     {
184       switch (howto->type)
185         {
186           case R_ARC_TLS_GD_GOT:
187             type = GOT_TLS_GD;
188             break;
189           case R_ARC_TLS_IE_GOT:
190             type = GOT_TLS_IE;
191             break;
192           default:
193             break;
194         }
195     }
196   return type;
197 }
198
199 #define ADD_SYMBOL_REF_SEC_AND_RELOC(SECNAME, COND_FOR_RELOC, H)        \
200   htab->s##SECNAME->size;                                               \
201   {                                                                     \
202     if (COND_FOR_RELOC)                                                 \
203       {                                                                 \
204         htab->srel##SECNAME->size += sizeof (Elf32_External_Rela);      \
205           ARC_DEBUG ("arc_info: Added reloc space in "                  \
206                      #SECNAME " section at " __FILE__                   \
207                      ":%d for symbol %s\n",                             \
208                      __LINE__, name_for_global_symbol (H));             \
209       }                                                                 \
210     if (H)                                                              \
211       if (h->dynindx == -1 && !h->forced_local)                         \
212         if (! bfd_elf_link_record_dynamic_symbol (info, H))             \
213           return FALSE;                                                 \
214      htab->s##SECNAME->size += 4;                                       \
215    }                                                                    \
216
217 static bfd_boolean
218 arc_fill_got_info_for_reloc (enum tls_type_e type,
219                              struct got_entry **list,
220                              struct bfd_link_info *  info,
221                              struct elf_link_hash_entry *h)
222 {
223   struct elf_link_hash_table *htab = elf_hash_table (info);
224
225   if (got_entry_for_type (list, type) != NULL)
226     return TRUE;
227
228   switch (type)
229     {
230       case GOT_NORMAL:
231         {
232           bfd_vma offset
233             = ADD_SYMBOL_REF_SEC_AND_RELOC (got, bfd_link_pic (info)
234                                                  || h != NULL, h);
235           new_got_entry_to_list (list, type, offset, TLS_GOT_NONE);
236         }
237         break;
238
239
240       case GOT_TLS_GD:
241         {
242           bfd_vma offset
243             = ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h);
244           bfd_vma ATTRIBUTE_UNUSED notneeded
245             = ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h);
246           new_got_entry_to_list (list, type, offset, TLS_GOT_MOD_AND_OFF);
247         }
248         break;
249       case GOT_TLS_IE:
250       case GOT_TLS_LE:
251         {
252           bfd_vma offset
253             = ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h);
254           new_got_entry_to_list (list, type, offset, TLS_GOT_OFF);
255         }
256         break;
257
258       default:
259         return FALSE;
260         break;
261     }
262   return TRUE;
263 }
264
265
266 static bfd_vma
267 relocate_fix_got_relocs_for_got_info (struct got_entry **          list_p,
268                                       enum tls_type_e              type,
269                                       struct bfd_link_info *       info,
270                                       bfd *                        output_bfd,
271                                       unsigned long                r_symndx,
272                                       Elf_Internal_Sym *           local_syms,
273                                       asection **                  local_sections,
274                                       struct elf_link_hash_entry * h,
275                                       struct arc_relocation_data * reloc_data)
276 {
277   struct elf_link_hash_table *htab = elf_hash_table (info);
278   struct got_entry *entry = NULL;
279
280   if (list_p == NULL || type == GOT_UNKNOWN || type == GOT_TLS_LE)
281     return 0;
282
283   entry = got_entry_for_type (list_p, type);
284   BFD_ASSERT (entry);
285
286   if (h == NULL
287       || (! elf_hash_table (info)->dynamic_sections_created
288           || (bfd_link_pic (info)
289               && SYMBOL_REFERENCES_LOCAL (info, h))))
290     {
291       const char ATTRIBUTE_UNUSED *symbol_name;
292       static const char local_name[] = "(local)";
293       asection *tls_sec = NULL;
294       bfd_vma sym_value = 0;
295
296       if (h != NULL)
297         {
298           // TODO: This should not be here.
299           reloc_data->sym_value = h->root.u.def.value;
300           reloc_data->sym_section = h->root.u.def.section;
301
302           sym_value = h->root.u.def.value
303             + h->root.u.def.section->output_section->vma
304             + h->root.u.def.section->output_offset;
305
306           tls_sec = elf_hash_table (info)->tls_sec;
307
308           symbol_name = h->root.root.string;
309         }
310       else
311         {
312           Elf_Internal_Sym *sym = local_syms + r_symndx;
313           asection *sec = local_sections[r_symndx];
314
315           sym_value = sym->st_value
316             + sec->output_section->vma
317             + sec->output_offset;
318
319           tls_sec = elf_hash_table (info)->tls_sec;
320
321           symbol_name = local_name;
322         }
323
324
325       if (entry && !entry->processed)
326         {
327           switch (entry->type)
328             {
329             case GOT_TLS_GD:
330               {
331                 BFD_ASSERT (tls_sec && tls_sec->output_section);
332                 bfd_vma sec_vma = tls_sec->output_section->vma;
333
334                 bfd_put_32 (output_bfd,
335                             sym_value - sec_vma,
336                             htab->sgot->contents + entry->offset
337                             + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
338                                ? 4 : 0));
339
340                 ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx "
341                            "@ %lx, for symbol %s\n",
342                            (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
343                             "GOT_TLS_IE"),
344                            (long) (sym_value - sec_vma),
345                            (long) (htab->sgot->output_section->vma
346                               + htab->sgot->output_offset->vma
347                               + entry->offset
348                               + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
349                                  ? 4 : 0)),
350                            symbol_name);
351               }
352               break;
353
354             case GOT_TLS_IE:
355               {
356                 BFD_ASSERT (tls_sec && tls_sec->output_section);
357                 bfd_vma ATTRIBUTE_UNUSED sec_vma
358                   = tls_sec->output_section->vma;
359
360                 bfd_put_32 (output_bfd,
361                             sym_value - sec_vma
362                             + (elf_hash_table (info)->dynamic_sections_created ? 0 : TCB_SIZE),
363                             htab->sgot->contents + entry->offset
364                             + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
365                                ? 4 : 0));
366
367                 ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx "
368                            "@ %p, for symbol %s\n",
369                            (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
370                             "GOT_TLS_IE"),
371                            (long) (sym_value - sec_vma),
372                            (long) (htab->sgot->output_section->vma
373                               + htab->sgot->output_offset->vma
374                               + entry->offset
375                               + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
376                                  ? 4 : 0)),
377                            symbol_name);
378               }
379               break;
380
381             case GOT_NORMAL:
382               {
383                 bfd_vma sec_vma
384                   = reloc_data->sym_section->output_section->vma
385                   + reloc_data->sym_section->output_offset;
386
387                 if (h != NULL
388                     && h->root.type == bfd_link_hash_undefweak)
389                   ARC_DEBUG ("arc_info: PATCHED: NOT_PATCHED "
390                              "@ %#08lx for sym %s in got offset %#lx "
391                              "(is undefweak)\n",
392                              (long) (htab->sgot->output_section->vma
393                                      + htab->sgot->output_offset
394                                      + entry->offset),
395                              symbol_name,
396                              (long) entry->offset);
397                 else
398                   {
399                     bfd_put_32 (output_bfd,
400                                 reloc_data->sym_value + sec_vma,
401                                 htab->sgot->contents + entry->offset);
402                     ARC_DEBUG ("arc_info: PATCHED: %#08lx "
403                                "@ %#08lx for sym %s in got offset %#lx\n",
404                                (long) (reloc_data->sym_value + sec_vma),
405                                (long) (htab->sgot->output_section->vma
406                                        + htab->sgot->output_offset + entry->offset),
407                                symbol_name,
408                                (long) entry->offset);
409                   }
410               }
411               break;
412             default:
413               BFD_ASSERT (0);
414               break;
415             }
416           entry->processed = TRUE;
417         }
418     }
419
420   return entry->offset;
421 }
422
423 static void
424 create_got_dynrelocs_for_single_entry (struct got_entry *list,
425                                        bfd *output_bfd,
426                                        struct bfd_link_info *  info,
427                                        struct elf_link_hash_entry *h)
428 {
429   if (list == NULL)
430     return;
431
432   bfd_vma got_offset = list->offset;
433
434   if (list->type == GOT_NORMAL
435       && !list->created_dyn_relocation)
436     {
437       if (bfd_link_pic (info)
438           && h != NULL
439               && (info->symbolic || h->dynindx == -1)
440               && h->def_regular)
441         {
442           ADD_RELA (output_bfd, got, got_offset, 0, R_ARC_RELATIVE, 0);
443         }
444       /* Do not fully understand the side effects of this condition.
445          The relocation space might still being reserved.  Perhaps
446          I should clear its value.  */
447       else if (h != NULL && h->dynindx != -1)
448         {
449           ADD_RELA (output_bfd, got, got_offset, h->dynindx, R_ARC_GLOB_DAT, 0);
450         }
451       list->created_dyn_relocation = TRUE;
452     }
453   else if (list->existing_entries != TLS_GOT_NONE
454            && !list->created_dyn_relocation)
455     {
456        /* TODO TLS: This is not called for local symbols.
457           In order to correctly implement TLS, this should also
458           be called for all local symbols with tls got entries.
459           Should be moved to relocate_section in order to make it
460           work for local symbols.  */
461       struct elf_link_hash_table *htab = elf_hash_table (info);
462       enum tls_got_entries e = list->existing_entries;
463
464       BFD_ASSERT (list->type != GOT_TLS_GD
465                   || list->existing_entries == TLS_GOT_MOD_AND_OFF);
466
467       bfd_vma dynindx = (h == NULL || h->dynindx == -1) ? 0 : h->dynindx;
468
469       if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_MOD)
470         {
471               ADD_RELA (output_bfd, got, got_offset, dynindx,
472                         R_ARC_TLS_DTPMOD, 0);
473               ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
474 GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = 0x0\n",
475                          list->type,
476                          (long) got_offset,
477                          (long) (htab->sgot->output_section->vma
478                                  + htab->sgot->output_offset + got_offset),
479                          (long) dynindx);
480         }
481
482       if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_OFF)
483         {
484           bfd_vma addend = 0;
485           if (list->type == GOT_TLS_IE)
486           {
487             addend = bfd_get_32 (output_bfd,
488                                  htab->sgot->contents + got_offset);
489           }
490
491           ADD_RELA (output_bfd, got,
492                     got_offset + (e == TLS_GOT_MOD_AND_OFF ? 4 : 0),
493                     dynindx,
494                     (list->type == GOT_TLS_IE ? R_ARC_TLS_TPOFF
495                                               : R_ARC_TLS_DTPOFF),
496                     addend);
497
498           ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
499 GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = %#lx\n",
500                      list->type,
501                      (long) got_offset,
502                      (long) (htab->sgot->output_section->vma
503                              + htab->sgot->output_offset + got_offset),
504                      (long) dynindx, (long) addend);
505         }
506       list->created_dyn_relocation = TRUE;
507     }
508 }
509
510 static void
511 create_got_dynrelocs_for_got_info (struct got_entry **list_p,
512                                    bfd *output_bfd,
513                                    struct bfd_link_info *  info,
514                                    struct elf_link_hash_entry *h)
515 {
516   if (list_p == NULL)
517     return;
518
519   struct got_entry *list = *list_p;
520   /* Traverse the list of got entries for this symbol.  */
521   while (list)
522     {
523       create_got_dynrelocs_for_single_entry (list, output_bfd, info, h);
524       list = list->next;
525     }
526 }
527
528 #undef ADD_SYMBOL_REF_SEC_AND_RELOC
529
530 #endif /* ARC_GOT_H */