1 /* ARC-specific support for 32-bit ELF
2    Copyright (C) 1994-2021 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 #define	align_power(addr, align)	\
28   (((addr) + ((bfd_vma) 1 << (align)) - 1) & (-((bfd_vma) 1 << (align))))
29 
30 enum tls_type_e
31 {
32   GOT_UNKNOWN = 0,
33   GOT_NORMAL,
34   GOT_TLS_GD,
35   GOT_TLS_IE,
36   GOT_TLS_LE
37 };
38 
39 enum tls_got_entries
40 {
41   TLS_GOT_NONE = 0,
42   TLS_GOT_MOD,
43   TLS_GOT_OFF,
44   TLS_GOT_MOD_AND_OFF
45 };
46 
47 struct got_entry
48 {
49   struct got_entry *next;
50   enum tls_type_e type;
51   bfd_vma offset;
52   bool processed;
53   bool created_dyn_relocation;
54   enum tls_got_entries existing_entries;
55 };
56 
57 /* Return the local got list, if not defined, create an empty one.  */
58 
59 static struct got_entry **
arc_get_local_got_ents(bfd * abfd)60 arc_get_local_got_ents (bfd * abfd)
61 {
62   if (elf_local_got_ents (abfd) == NULL)
63     {
64       bfd_size_type amt = (elf_tdata (abfd)->symtab_hdr.sh_info
65 			   * sizeof (*elf_local_got_ents (abfd)));
66       elf_local_got_ents (abfd) = bfd_zmalloc (amt);
67       if (elf_local_got_ents (abfd) == NULL)
68 	{
69 	  _bfd_error_handler (_("%pB: cannot allocate memory for local "
70 				"GOT entries"), abfd);
71 	  bfd_set_error (bfd_error_bad_value);
72 	  return NULL;
73 	}
74     }
75 
76   return elf_local_got_ents (abfd);
77 }
78 
79 static struct got_entry *
got_entry_for_type(struct got_entry ** list,enum tls_type_e type)80 got_entry_for_type (struct got_entry **list,
81 		    enum tls_type_e type)
82 {
83   struct got_entry **p = list;
84 
85   while (*p != NULL)
86     {
87       if ((*p)->type == type)
88 	return *p;
89       p = &((*p)->next);
90     }
91   return NULL;
92 }
93 
94 static void
new_got_entry_to_list(struct got_entry ** list,enum tls_type_e type,bfd_vma offset,enum tls_got_entries existing_entries)95 new_got_entry_to_list (struct got_entry **list,
96 		       enum tls_type_e type,
97 		       bfd_vma offset,
98 		       enum tls_got_entries existing_entries)
99 {
100   /* Find list end.  Avoid having multiple entries of the same
101      type.  */
102   struct got_entry **p = list;
103   struct got_entry *entry;
104 
105   while (*p != NULL)
106     {
107       if ((*p)->type == type)
108 	return;
109       p = &((*p)->next);
110     }
111 
112   entry = (struct got_entry *) xmalloc (sizeof (struct got_entry));
113 
114   entry->type = type;
115   entry->offset = offset;
116   entry->next = NULL;
117   entry->processed = false;
118   entry->created_dyn_relocation = false;
119   entry->existing_entries = existing_entries;
120 
121   ARC_DEBUG ("New GOT got entry added to list: "
122 	     "type: %d, offset: %ld, existing_entries: %d\n",
123 	     type, (long) offset, existing_entries);
124 
125   /* Add the entry to the end of the list.  */
126   *p = entry;
127 }
128 
129 static enum tls_type_e
tls_type_for_reloc(reloc_howto_type * howto)130 tls_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 
156 static struct got_entry **
get_got_entry_list_for_symbol(bfd * abfd,unsigned long r_symndx,struct elf_link_hash_entry * h)157 get_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 
174 static enum tls_type_e
arc_got_entry_type_for_reloc(reloc_howto_type * howto)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 bool
arc_fill_got_info_for_reloc(enum tls_type_e type,struct got_entry ** list,struct bfd_link_info * info,struct elf_link_hash_entry * h)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
relocate_fix_got_relocs_for_got_info(struct got_entry ** list_p,enum tls_type_e type,struct bfd_link_info * info,bfd * output_bfd,unsigned long r_symndx,Elf_Internal_Sym * local_syms,asection ** local_sections,struct elf_link_hash_entry * h,struct arc_relocation_data * reloc_data)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       || 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 
435 static void
create_got_dynrelocs_for_single_entry(struct got_entry * list,bfd * output_bfd,struct bfd_link_info * info,struct elf_link_hash_entry * h)436 create_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, \
486 GOT_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, \
511 GOT_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 
522 static void
create_got_dynrelocs_for_got_info(struct got_entry ** list_p,bfd * output_bfd,struct bfd_link_info * info,struct elf_link_hash_entry * h)523 create_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