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