11573Srgrimes/* ARC-specific support for 32-bit ELF
21573Srgrimes   Copyright (C) 1994-2020 Free Software Foundation, Inc.
31573Srgrimes   Contributed by Cupertino Miranda (cmiranda@synopsys.com).
41573Srgrimes
5227753Stheraven   This file is part of BFD, the Binary File Descriptor library.
6227753Stheraven
7227753Stheraven   This program is free software; you can redistribute it and/or modify
8227753Stheraven   it under the terms of the GNU General Public License as published by
9227753Stheraven   the Free Software Foundation; either version 3 of the License, or
101573Srgrimes   (at your option) any later version.
111573Srgrimes
121573Srgrimes   This program is distributed in the hope that it will be useful,
131573Srgrimes   but WITHOUT ANY WARRANTY; without even the implied warranty of
141573Srgrimes   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
151573Srgrimes   GNU General Public License for more details.
161573Srgrimes
171573Srgrimes   You should have received a copy of the GNU General Public License
181573Srgrimes   along with this program; if not, write to the Free Software
191573Srgrimes   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
201573Srgrimes   MA 02110-1301, USA.  */
211573Srgrimes
221573Srgrimes#ifndef ARC_GOT_H
231573Srgrimes#define ARC_GOT_H
241573Srgrimes
251573Srgrimes#define TCB_SIZE (8)
261573Srgrimes
271573Srgrimes#define	align_power(addr, align)	\
281573Srgrimes  (((addr) + ((bfd_vma) 1 << (align)) - 1) & (-((bfd_vma) 1 << (align))))
291573Srgrimes
301573Srgrimesenum tls_type_e
311573Srgrimes{
321573Srgrimes  GOT_UNKNOWN = 0,
331573Srgrimes  GOT_NORMAL,
341573Srgrimes  GOT_TLS_GD,
351573Srgrimes  GOT_TLS_IE,
361573Srgrimes  GOT_TLS_LE
371573Srgrimes};
3892986Sobrien
3992986Sobrienenum tls_got_entries
401573Srgrimes{
411573Srgrimes  TLS_GOT_NONE = 0,
421573Srgrimes  TLS_GOT_MOD,
431573Srgrimes  TLS_GOT_OFF,
441573Srgrimes  TLS_GOT_MOD_AND_OFF
45227753Stheraven};
461573Srgrimes
471573Srgrimesstruct got_entry
481573Srgrimes{
491573Srgrimes  struct got_entry *next;
5082975Sache  enum tls_type_e type;
511573Srgrimes  bfd_vma offset;
521573Srgrimes  bfd_boolean processed;
531573Srgrimes  bfd_boolean created_dyn_relocation;
54227753Stheraven  enum tls_got_entries existing_entries;
551573Srgrimes};
5687016Sache
5787016Sache/* Return the local got list, if not defined, create an empty one.  */
5887494Sache
5987016Sachestatic struct got_entry **
6087494Sachearc_get_local_got_ents (bfd * abfd)
61227753Stheraven{
621573Srgrimes  if (elf_local_got_ents (abfd) == NULL)
631573Srgrimes    {
641573Srgrimes      bfd_size_type amt = (elf_tdata (abfd)->symtab_hdr.sh_info
651573Srgrimes			   * sizeof (*elf_local_got_ents (abfd)));
6682975Sache      elf_local_got_ents (abfd) = bfd_zmalloc (amt);
671573Srgrimes      if (elf_local_got_ents (abfd) == NULL)
681573Srgrimes	{
69227753Stheraven	  _bfd_error_handler (_("%pB: cannot allocate memory for local "
701573Srgrimes				"GOT entries"), abfd);
711573Srgrimes	  bfd_set_error (bfd_error_bad_value);
721573Srgrimes	  return NULL;
7382975Sache	}
7482975Sache    }
7582975Sache
7682975Sache  return elf_local_got_ents (abfd);
7782975Sache}
781573Srgrimes
79140536Sachestatic struct got_entry *
80140577Sachegot_entry_for_type (struct got_entry **list,
81140577Sache		    enum tls_type_e type)
82140577Sache{
831573Srgrimes  struct got_entry **p = list;
841573Srgrimes
851573Srgrimes  while (*p != NULL)
861573Srgrimes    {
871573Srgrimes      if ((*p)->type == type)
881573Srgrimes	return *p;
8982982Sache      p = &((*p)->next);
9087023Sfenner    }
9182975Sache  return NULL;
9282975Sache}
9382975Sache
9482975Sachestatic void
9582982Sachenew_got_entry_to_list (struct got_entry **list,
9687494Sache		       enum tls_type_e type,
9787494Sache		       bfd_vma offset,
9887494Sache		       enum tls_got_entries existing_entries)
9987494Sache{
10087494Sache  /* Find list end.  Avoid having multiple entries of the same
10187494Sache     type.  */
1021573Srgrimes  struct got_entry **p = list;
1031573Srgrimes  struct got_entry *entry;
10487494Sache
1051573Srgrimes  while (*p != NULL)
10687494Sache    {
1071573Srgrimes      if ((*p)->type == type)
1081573Srgrimes	return;
1091573Srgrimes      p = &((*p)->next);
1101573Srgrimes    }
11187494Sache
1121573Srgrimes  entry = (struct got_entry *) xmalloc (sizeof (struct got_entry));
1131573Srgrimes
1141573Srgrimes  entry->type = type;
1151573Srgrimes  entry->offset = offset;
1161573Srgrimes  entry->next = NULL;
11782975Sache  entry->processed = FALSE;
11882975Sache  entry->created_dyn_relocation = FALSE;
11982975Sache  entry->existing_entries = existing_entries;
1201573Srgrimes
1211573Srgrimes  ARC_DEBUG ("New GOT got entry added to list: "
12282975Sache	     "type: %d, offset: %ld, existing_entries: %d\n",
1231573Srgrimes	     type, (long) offset, existing_entries);
1241573Srgrimes
1251573Srgrimes  /* Add the entry to the end of the list.  */
126227753Stheraven  *p = entry;
127227753Stheraven}
128227753Stheraven
129227753Stheravenstatic enum tls_type_e
130227753Stheraventls_type_for_reloc (reloc_howto_type *howto)
131{
132  enum tls_type_e ret = GOT_UNKNOWN;
133
134  if (is_reloc_for_GOT (howto))
135    return GOT_NORMAL;
136
137  switch (howto->type)
138    {
139    case R_ARC_TLS_GD_GOT:
140      ret = GOT_TLS_GD;
141      break;
142    case R_ARC_TLS_IE_GOT:
143      ret = GOT_TLS_IE;
144      break;
145    case R_ARC_TLS_LE_32:
146      ret = GOT_TLS_LE;
147      break;
148    default:
149      ret = GOT_UNKNOWN;
150      break;
151    }
152
153  return ret;
154};
155
156static struct got_entry **
157get_got_entry_list_for_symbol (bfd *abfd,
158			       unsigned long r_symndx,
159			       struct elf_link_hash_entry *h)
160{
161  struct elf_arc_link_hash_entry *h1 =
162    ((struct elf_arc_link_hash_entry *) h);
163  if (h1 != NULL)
164    {
165      return &h1->got_ents;
166    }
167  else
168    {
169      return arc_get_local_got_ents (abfd) + r_symndx;
170    }
171}
172
173
174static enum tls_type_e
175arc_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
217static bfd_boolean
218arc_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
266static bfd_vma
267relocate_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      || h->forced_local == TRUE
288      || (! elf_hash_table (info)->dynamic_sections_created
289	  || (bfd_link_pic (info)
290	      && SYMBOL_REFERENCES_LOCAL (info, h))))
291    {
292      const char ATTRIBUTE_UNUSED *symbol_name;
293      static const char local_name[] = "(local)";
294      asection *tls_sec = NULL;
295      bfd_vma sym_value = 0;
296
297      if (h != NULL)
298	{
299	  /* TODO: This should not be here.  */
300	  reloc_data->sym_value = h->root.u.def.value;
301	  reloc_data->sym_section = h->root.u.def.section;
302
303	  sym_value = h->root.u.def.value
304	    + h->root.u.def.section->output_section->vma
305	    + h->root.u.def.section->output_offset;
306
307	  tls_sec = elf_hash_table (info)->tls_sec;
308
309	  symbol_name = h->root.root.string;
310	}
311      else
312	{
313	  Elf_Internal_Sym *sym = local_syms + r_symndx;
314	  asection *sec = local_sections[r_symndx];
315
316	  sym_value = sym->st_value
317	    + sec->output_section->vma
318	    + sec->output_offset;
319
320	  tls_sec = elf_hash_table (info)->tls_sec;
321
322	  symbol_name = local_name;
323	}
324
325
326      if (entry && !entry->processed)
327	{
328	  switch (entry->type)
329	    {
330	    case GOT_TLS_GD:
331	      {
332		BFD_ASSERT (tls_sec && tls_sec->output_section);
333		bfd_vma sec_vma = tls_sec->output_section->vma;
334
335		if (h == NULL || h->forced_local
336		   || !elf_hash_table (info)->dynamic_sections_created)
337		  {
338		    bfd_put_32 (output_bfd,
339			    sym_value - sec_vma
340			    + (elf_hash_table (info)->dynamic_sections_created
341			       ? 0
342			       : (align_power (0,
343					       tls_sec->alignment_power))),
344			    htab->sgot->contents + entry->offset
345			    + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
346			       ? 4 : 0));
347
348		    ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx "
349			  "@ %lx, for symbol %s\n",
350			  (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
351			   "GOT_TLS_IE"),
352			  (long) (sym_value - sec_vma),
353			  (long) (htab->sgot->output_section->vma
354			     + htab->sgot->output_offset
355			     + entry->offset
356			     + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
357				? 4 : 0)),
358			  symbol_name);
359		  }
360	      }
361	      break;
362
363	    case GOT_TLS_IE:
364	      {
365		BFD_ASSERT (tls_sec && tls_sec->output_section);
366		bfd_vma ATTRIBUTE_UNUSED sec_vma
367		  = tls_sec->output_section->vma;
368
369		bfd_put_32 (output_bfd,
370			    sym_value - sec_vma
371			    + (elf_hash_table (info)->dynamic_sections_created
372			       ? 0
373			       : (align_power (TCB_SIZE,
374					       tls_sec->alignment_power))),
375			    htab->sgot->contents + entry->offset
376			    + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
377			       ? 4 : 0));
378
379		ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx "
380			   "@ %p, for symbol %s\n",
381			   (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
382			    "GOT_TLS_IE"),
383			   (long) (sym_value - sec_vma),
384			   (long) (htab->sgot->output_section->vma
385			      + htab->sgot->output_offset
386			      + entry->offset
387			      + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
388				 ? 4 : 0)),
389			   symbol_name);
390	      }
391	      break;
392
393	    case GOT_NORMAL:
394	      {
395		bfd_vma sec_vma
396		  = reloc_data->sym_section->output_section->vma
397		  + reloc_data->sym_section->output_offset;
398
399		if (h != NULL
400		    && h->root.type == bfd_link_hash_undefweak)
401		  ARC_DEBUG ("arc_info: PATCHED: NOT_PATCHED "
402			     "@ %#08lx for sym %s in got offset %#lx "
403			     "(is undefweak)\n",
404			     (long) (htab->sgot->output_section->vma
405				     + htab->sgot->output_offset
406				     + entry->offset),
407			     symbol_name,
408			     (long) entry->offset);
409		else
410		  {
411		    bfd_put_32 (output_bfd,
412				reloc_data->sym_value + sec_vma,
413				htab->sgot->contents + entry->offset);
414		    ARC_DEBUG ("arc_info: PATCHED: %#08lx "
415			       "@ %#08lx for sym %s in got offset %#lx\n",
416			       (long) (reloc_data->sym_value + sec_vma),
417			       (long) (htab->sgot->output_section->vma
418				       + htab->sgot->output_offset + entry->offset),
419			       symbol_name,
420			       (long) entry->offset);
421		  }
422	      }
423	      break;
424	    default:
425	      BFD_ASSERT (0);
426	      break;
427	    }
428	  entry->processed = TRUE;
429	}
430    }
431
432  return entry->offset;
433}
434
435static void
436create_got_dynrelocs_for_single_entry (struct got_entry *list,
437				       bfd *output_bfd,
438				       struct bfd_link_info *  info,
439				       struct elf_link_hash_entry *h)
440{
441  if (list == NULL)
442    return;
443
444  bfd_vma got_offset = list->offset;
445
446  if (list->type == GOT_NORMAL
447      && !list->created_dyn_relocation)
448    {
449      if (bfd_link_pic (info)
450	  && h != NULL
451	      && (info->symbolic || h->dynindx == -1)
452	      && h->def_regular)
453	{
454	  ADD_RELA (output_bfd, got, got_offset, 0, R_ARC_RELATIVE, 0);
455	}
456      /* Do not fully understand the side effects of this condition.
457	 The relocation space might still being reserved.  Perhaps
458	 I should clear its value.  */
459      else if (h != NULL && h->dynindx != -1)
460	{
461	  ADD_RELA (output_bfd, got, got_offset, h->dynindx, R_ARC_GLOB_DAT, 0);
462	}
463      list->created_dyn_relocation = TRUE;
464    }
465  else if (list->existing_entries != TLS_GOT_NONE
466	   && !list->created_dyn_relocation)
467    {
468       /* TODO TLS: This is not called for local symbols.
469	  In order to correctly implement TLS, this should also
470	  be called for all local symbols with tls got entries.
471	  Should be moved to relocate_section in order to make it
472	  work for local symbols.  */
473      struct elf_link_hash_table *htab = elf_hash_table (info);
474      enum tls_got_entries e = list->existing_entries;
475
476      BFD_ASSERT (list->type != GOT_TLS_GD
477		  || list->existing_entries == TLS_GOT_MOD_AND_OFF);
478
479      bfd_vma dynindx = (h == NULL || h->dynindx == -1) ? 0 : h->dynindx;
480
481      if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_MOD)
482	{
483	      ADD_RELA (output_bfd, got, got_offset, dynindx,
484			R_ARC_TLS_DTPMOD, 0);
485	      ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
486GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = 0x0\n",
487			 list->type,
488			 (long) got_offset,
489			 (long) (htab->sgot->output_section->vma
490				 + htab->sgot->output_offset + got_offset),
491			 (long) dynindx);
492	}
493
494      if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_OFF)
495	{
496	  bfd_vma addend = 0;
497	  if (list->type == GOT_TLS_IE)
498	  {
499	    addend = bfd_get_32 (output_bfd,
500				 htab->sgot->contents + got_offset);
501	  }
502
503	  ADD_RELA (output_bfd, got,
504		    got_offset + (e == TLS_GOT_MOD_AND_OFF ? 4 : 0),
505		    dynindx,
506		    (list->type == GOT_TLS_IE ? R_ARC_TLS_TPOFF
507					      : R_ARC_TLS_DTPOFF),
508		    addend);
509
510	  ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
511GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = %#lx\n",
512		     list->type,
513		     (long) got_offset,
514		     (long) (htab->sgot->output_section->vma
515			     + htab->sgot->output_offset + got_offset),
516		     (long) dynindx, (long) addend);
517	}
518      list->created_dyn_relocation = TRUE;
519    }
520}
521
522static void
523create_got_dynrelocs_for_got_info (struct got_entry **list_p,
524				   bfd *output_bfd,
525				   struct bfd_link_info *  info,
526				   struct elf_link_hash_entry *h)
527{
528  if (list_p == NULL)
529    return;
530
531  struct got_entry *list = *list_p;
532  /* Traverse the list of got entries for this symbol.  */
533  while (list)
534    {
535      create_got_dynrelocs_for_single_entry (list, output_bfd, info, h);
536      list = list->next;
537    }
538}
539
540#undef ADD_SYMBOL_REF_SEC_AND_RELOC
541
542#endif /* ARC_GOT_H */
543