Home | History | Annotate | Line # | Download | only in bfd
arc-got.h revision 1.1.1.5
      1 /* ARC-specific support for 32-bit ELF
      2    Copyright (C) 1994-2022 Free Software Foundation, Inc.
      3    Contributed by Cupertino Miranda (cmiranda (at) 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 **
     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 *
     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
     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
    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 **
    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
    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
    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 struct arc_static_sym_data {
    266   bfd_vma sym_value;
    267   const char *symbol_name;
    268 };
    269 
    270 static struct arc_static_sym_data
    271 get_static_sym_data (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   static const char local_name[] = "(local)";
    278   struct arc_static_sym_data ret = { 0, NULL };
    279 
    280   if (h != NULL)
    281     {
    282       BFD_ASSERT (h->root.type != bfd_link_hash_undefweak
    283 		  && h->root.type != bfd_link_hash_undefined);
    284       /* TODO: This should not be here.  */
    285       reloc_data->sym_value = h->root.u.def.value;
    286       reloc_data->sym_section = h->root.u.def.section;
    287 
    288       ret.sym_value = h->root.u.def.value
    289 	+ h->root.u.def.section->output_section->vma
    290 	+ h->root.u.def.section->output_offset;
    291 
    292       ret.symbol_name = h->root.root.string;
    293     }
    294   else
    295   {
    296     Elf_Internal_Sym *sym = local_syms + r_symndx;
    297     asection *sec = local_sections[r_symndx];
    298 
    299     ret.sym_value = sym->st_value
    300       + sec->output_section->vma
    301       + sec->output_offset;
    302 
    303     ret.symbol_name = local_name;
    304   }
    305   return ret;
    306 }
    307 
    308 static bfd_vma
    309 relocate_fix_got_relocs_for_got_info (struct got_entry **	   list_p,
    310 				      enum tls_type_e		   type,
    311 				      struct bfd_link_info *	   info,
    312 				      bfd *			   output_bfd,
    313 				      unsigned long		   r_symndx,
    314 				      Elf_Internal_Sym *	   local_syms,
    315 				      asection **		   local_sections,
    316 				      struct elf_link_hash_entry * h,
    317 				      struct arc_relocation_data * reloc_data)
    318 {
    319   struct elf_link_hash_table *htab = elf_hash_table (info);
    320   struct got_entry *entry = NULL;
    321 
    322   if (list_p == NULL || type == GOT_UNKNOWN || type == GOT_TLS_LE)
    323     return 0;
    324 
    325   entry = got_entry_for_type (list_p, type);
    326   BFD_ASSERT (entry);
    327 
    328   if (h == NULL
    329       || h->forced_local == true
    330       || (! elf_hash_table (info)->dynamic_sections_created
    331 	  || (bfd_link_pic (info)
    332 	      && SYMBOL_REFERENCES_LOCAL (info, h))))
    333     {
    334       const char ATTRIBUTE_UNUSED *symbol_name;
    335       asection *tls_sec = elf_hash_table (info)->tls_sec;
    336 
    337       if (entry && !entry->processed)
    338 	{
    339 	  switch (entry->type)
    340 	    {
    341 	    case GOT_TLS_GD:
    342 	      {
    343 		BFD_ASSERT (tls_sec && tls_sec->output_section);
    344 		bfd_vma sec_vma = tls_sec->output_section->vma;
    345 
    346 		if (h == NULL || h->forced_local
    347 		   || !elf_hash_table (info)->dynamic_sections_created)
    348 		  {
    349 		    struct arc_static_sym_data tmp =
    350 		      get_static_sym_data (r_symndx, local_syms, local_sections,
    351 					   h, reloc_data);
    352 
    353 		    bfd_put_32 (output_bfd,
    354 			    tmp.sym_value - sec_vma
    355 			    + (elf_hash_table (info)->dynamic_sections_created
    356 			       ? 0
    357 			       : (align_power (0,
    358 					       tls_sec->alignment_power))),
    359 			    htab->sgot->contents + entry->offset
    360 			    + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
    361 			       ? 4 : 0));
    362 
    363 		    ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx "
    364 			  "@ %lx, for symbol %s\n",
    365 			  (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
    366 			   "GOT_TLS_IE"),
    367 			  (long) (sym_value - sec_vma),
    368 			  (long) (htab->sgot->output_section->vma
    369 			     + htab->sgot->output_offset
    370 			     + entry->offset
    371 			     + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
    372 				? 4 : 0)),
    373 			  tmp.symbol_name);
    374 		  }
    375 	      }
    376 	      break;
    377 
    378 	    case GOT_TLS_IE:
    379 	      {
    380 		BFD_ASSERT (tls_sec && tls_sec->output_section);
    381 		bfd_vma ATTRIBUTE_UNUSED sec_vma
    382 		  = tls_sec->output_section->vma;
    383 
    384 		struct arc_static_sym_data tmp =
    385 		  get_static_sym_data (r_symndx, local_syms, local_sections,
    386 				       h, reloc_data);
    387 
    388 		bfd_put_32 (output_bfd,
    389 			    tmp.sym_value - sec_vma
    390 			    + (elf_hash_table (info)->dynamic_sections_created
    391 			       ? 0
    392 			       : (align_power (TCB_SIZE,
    393 					       tls_sec->alignment_power))),
    394 			    htab->sgot->contents + entry->offset
    395 			    + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
    396 			       ? 4 : 0));
    397 
    398 		ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx "
    399 			   "@ %p, for symbol %s\n",
    400 			   (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
    401 			    "GOT_TLS_IE"),
    402 			   (long) (sym_value - sec_vma),
    403 			   (long) (htab->sgot->output_section->vma
    404 			      + htab->sgot->output_offset
    405 			      + entry->offset
    406 			      + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
    407 				 ? 4 : 0)),
    408 			   tmp.symbol_name);
    409 	      }
    410 	      break;
    411 
    412 	    case GOT_NORMAL:
    413 	      {
    414 		bfd_vma sec_vma
    415 		  = reloc_data->sym_section->output_section->vma
    416 		  + reloc_data->sym_section->output_offset;
    417 
    418 		if (h != NULL
    419 		    && h->root.type == bfd_link_hash_undefweak)
    420 		  ARC_DEBUG ("arc_info: PATCHED: NOT_PATCHED "
    421 			     "@ %#08lx for sym %s in got offset %#lx "
    422 			     "(is undefweak)\n",
    423 			     (long) (htab->sgot->output_section->vma
    424 				     + htab->sgot->output_offset
    425 				     + entry->offset),
    426 			     symbol_name,
    427 			     (long) entry->offset);
    428 		else
    429 		  {
    430 		    bfd_put_32 (output_bfd,
    431 				reloc_data->sym_value + sec_vma,
    432 				htab->sgot->contents + entry->offset);
    433 		    ARC_DEBUG ("arc_info: PATCHED: %#08lx "
    434 			       "@ %#08lx for sym %s in got offset %#lx\n",
    435 			       (long) (reloc_data->sym_value + sec_vma),
    436 			       (long) (htab->sgot->output_section->vma
    437 				       + htab->sgot->output_offset
    438 				       + entry->offset),
    439 			       symbol_name,
    440 			       (long) entry->offset);
    441 		  }
    442 	      }
    443 	      break;
    444 	    default:
    445 	      BFD_ASSERT (0);
    446 	      break;
    447 	    }
    448 	  entry->processed = true;
    449 	}
    450     }
    451 
    452   return entry->offset;
    453 }
    454 
    455 static void
    456 create_got_dynrelocs_for_single_entry (struct got_entry *list,
    457 				       bfd *output_bfd,
    458 				       struct bfd_link_info *  info,
    459 				       struct elf_link_hash_entry *h)
    460 {
    461   if (list == NULL)
    462     return;
    463 
    464   bfd_vma got_offset = list->offset;
    465 
    466   if (list->type == GOT_NORMAL
    467       && !list->created_dyn_relocation)
    468     {
    469       if (bfd_link_pic (info)
    470 	  && h != NULL
    471 	      && (info->symbolic || h->dynindx == -1)
    472 	      && h->def_regular)
    473 	{
    474 	  ADD_RELA (output_bfd, got, got_offset, 0, R_ARC_RELATIVE, 0);
    475 	}
    476       /* Do not fully understand the side effects of this condition.
    477 	 The relocation space might still being reserved.  Perhaps
    478 	 I should clear its value.  */
    479       else if (h != NULL && h->dynindx != -1)
    480 	{
    481 	  ADD_RELA (output_bfd, got, got_offset, h->dynindx, R_ARC_GLOB_DAT, 0);
    482 	}
    483       list->created_dyn_relocation = true;
    484     }
    485   else if (list->existing_entries != TLS_GOT_NONE
    486 	   && !list->created_dyn_relocation)
    487     {
    488        /* TODO TLS: This is not called for local symbols.
    489 	  In order to correctly implement TLS, this should also
    490 	  be called for all local symbols with tls got entries.
    491 	  Should be moved to relocate_section in order to make it
    492 	  work for local symbols.  */
    493       struct elf_link_hash_table *htab = elf_hash_table (info);
    494       enum tls_got_entries e = list->existing_entries;
    495 
    496       BFD_ASSERT (list->type != GOT_TLS_GD
    497 		  || list->existing_entries == TLS_GOT_MOD_AND_OFF);
    498 
    499       bfd_vma dynindx = (h == NULL || h->dynindx == -1) ? 0 : h->dynindx;
    500 
    501       if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_MOD)
    502 	{
    503 	      ADD_RELA (output_bfd, got, got_offset, dynindx,
    504 			R_ARC_TLS_DTPMOD, 0);
    505 	      ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
    506 GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = 0x0\n",
    507 			 list->type,
    508 			 (long) got_offset,
    509 			 (long) (htab->sgot->output_section->vma
    510 				 + htab->sgot->output_offset + got_offset),
    511 			 (long) dynindx);
    512 	}
    513 
    514       if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_OFF)
    515 	{
    516 	  bfd_vma addend = 0;
    517 	  if (list->type == GOT_TLS_IE)
    518 	  {
    519 	    addend = bfd_get_32 (output_bfd,
    520 				 htab->sgot->contents + got_offset);
    521 	  }
    522 
    523 	  ADD_RELA (output_bfd, got,
    524 		    got_offset + (e == TLS_GOT_MOD_AND_OFF ? 4 : 0),
    525 		    dynindx,
    526 		    (list->type == GOT_TLS_IE ? R_ARC_TLS_TPOFF
    527 					      : R_ARC_TLS_DTPOFF),
    528 		    addend);
    529 
    530 	  ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
    531 GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = %#lx\n",
    532 		     list->type,
    533 		     (long) got_offset,
    534 		     (long) (htab->sgot->output_section->vma
    535 			     + htab->sgot->output_offset + got_offset),
    536 		     (long) dynindx, (long) addend);
    537 	}
    538       list->created_dyn_relocation = true;
    539     }
    540 }
    541 
    542 static void
    543 create_got_dynrelocs_for_got_info (struct got_entry **list_p,
    544 				   bfd *output_bfd,
    545 				   struct bfd_link_info *  info,
    546 				   struct elf_link_hash_entry *h)
    547 {
    548   if (list_p == NULL)
    549     return;
    550 
    551   struct got_entry *list = *list_p;
    552   /* Traverse the list of got entries for this symbol.  */
    553   while (list)
    554     {
    555       create_got_dynrelocs_for_single_entry (list, output_bfd, info, h);
    556       list = list->next;
    557     }
    558 }
    559 
    560 #undef ADD_SYMBOL_REF_SEC_AND_RELOC
    561 
    562 #endif /* ARC_GOT_H */
    563