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