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