This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: reference counts for garbage collecting


On Sun, 23 Apr 2000, H . J . Lu wrote:

> We need to check if local_got_refcounts is NULL before accessing it.

Thanks.  This same bug has been in the ppc and m68k gc code since June
1998.  What was the dlopen problem?

I'm cc'ing this to the list, because others might like to review this
patch too.  The patch implements reference counting on x86 for plt and got,
so that garbage collection can do something useful on these sections.

Regards, Alan Modra

-- 
Linuxcare.  Support for the Revolution.

bfd/ChangeLog
	* elf32-m68k.c (elf_m68k_gc_sweep_hook):  Return if dynobj NULL.
	Check local_got_refcounts before dereferencing. 

	* elf32-ppc.c (ppc_elf_relocate_section): Check splt != NULL
	before deciding we don't need R_PPC_PLT32 relocation.
	(ppc_elf_gc_sweep_hook): Check local_got_refcounts before
	dereferencing.

	* elflink.h (elf_gc_common_finalize_got_offsets): Fix comment.

	* elf32-i386.c (elf_i386_check_relocs): Reference count .got and
	.plt entries.
	(elf_i386_gc_sweep_hook): Garbage collect .got and .plt entries.
	(elf_i386_adjust_dynamic_symbol): Recognize unused .plt entries.
	(elf_i386_relocate_section): Allow for .plt to go missing.
	(elf_i386_finish_dynamic_symbol): Use same test to decide if we
	can use a relative reloc for got as elf_i386_relocate_section.
	(bfd_elf32_bfd_final_link): Define to use gc form of final link.

Index: elflink.h
===================================================================
RCS file: /cvs/src/src/bfd/elflink.h,v
retrieving revision 1.51
diff -u -p -r1.51 elflink.h
--- elflink.h	2000/03/01 20:39:07	1.51
+++ elflink.h	2000/04/24 01:33:21
@@ -6615,7 +6615,8 @@ elf_gc_common_finalize_got_offsets (abfd
 	}
     }
 
-  /* Then the global .got and .plt entries.  */
+  /* Then the global .got entries.  .plt refcounts are handled by
+     adjust_dynamic_symbol  */
   elf_link_hash_traverse (elf_hash_table (info),
 			  elf_gc_allocate_got_offsets,
 			  (PTR) &gotoff);
Index: elf32-i386.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-i386.c,v
retrieving revision 1.9
diff -u -p -r1.9 elf32-i386.c
--- elf32-i386.c	2000/04/20 05:37:05	1.9
+++ elf32-i386.c	2000/04/24 01:33:26
@@ -439,7 +439,7 @@ elf_i386_check_relocs (abfd, info, sec, 
   bfd *dynobj;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
-  bfd_vma *local_got_offsets;
+  bfd_signed_vma *local_got_refcounts;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sgot;
@@ -452,7 +452,7 @@ elf_i386_check_relocs (abfd, info, sec, 
   dynobj = elf_hash_table (info)->dynobj;
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
-  local_got_offsets = elf_local_got_offsets (abfd);
+  local_got_refcounts = elf_local_got_refcounts (abfd);
 
   sgot = NULL;
   srelgot = NULL;
@@ -522,57 +522,54 @@ elf_i386_check_relocs (abfd, info, sec, 
 
 	  if (h != NULL)
 	    {
-	      if (h->got.offset != (bfd_vma) -1)
+	      if (h->got.refcount == -1)
 		{
-		  /* We have already allocated space in the .got.  */
-		  break;
-		}
-	      h->got.offset = sgot->_raw_size;
+		  h->got.refcount = 1;
 
-	      /* Make sure this symbol is output as a dynamic symbol.  */
-	      if (h->dynindx == -1)
-		{
-		  if (! bfd_elf32_link_record_dynamic_symbol (info, h))
-		    return false;
-		}
+		  /* Make sure this symbol is output as a dynamic symbol.  */
+		  if (h->dynindx == -1)
+		    {
+		      if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+			return false;
+		    }
 
-	      srelgot->_raw_size += sizeof (Elf32_External_Rel);
+		  sgot->_raw_size += 4;
+		  srelgot->_raw_size += sizeof (Elf32_External_Rel);
+		}
+	      else
+		h->got.refcount += 1;
 	    }
 	  else
 	    {
-     	      /* This is a global offset table entry for a local
-                 symbol.  */
-	      if (local_got_offsets == NULL)
+     	      /* This is a global offset table entry for a local symbol.  */
+	      if (local_got_refcounts == NULL)
 		{
 		  size_t size;
-		  register unsigned int i;
 
-		  size = symtab_hdr->sh_info * sizeof (bfd_vma);
-		  local_got_offsets = (bfd_vma *) bfd_alloc (abfd, size);
-		  if (local_got_offsets == NULL)
+		  size = symtab_hdr->sh_info * sizeof (bfd_signed_vma);
+		  local_got_refcounts = ((bfd_signed_vma *)
+					 bfd_alloc (abfd, size));
+		  if (local_got_refcounts == NULL)
 		    return false;
-		  elf_local_got_offsets (abfd) = local_got_offsets;
-		  for (i = 0; i < symtab_hdr->sh_info; i++)
-		    local_got_offsets[i] = (bfd_vma) -1;
+		  elf_local_got_refcounts (abfd) = local_got_refcounts;
+		  memset (local_got_refcounts, -1, size);
 		}
-	      if (local_got_offsets[r_symndx] != (bfd_vma) -1)
+	      if (local_got_refcounts[r_symndx] == -1)
 		{
-		  /* We have already allocated space in the .got.  */
-		  break;
-		}
-	      local_got_offsets[r_symndx] = sgot->_raw_size;
+		  local_got_refcounts[r_symndx] = 1;
 
-	      if (info->shared)
-		{
-		  /* If we are generating a shared object, we need to
-                     output a R_386_RELATIVE reloc so that the dynamic
-                     linker can adjust this GOT entry.  */
-		  srelgot->_raw_size += sizeof (Elf32_External_Rel);
+		  sgot->_raw_size += 4;
+		  if (info->shared)
+		    {
+		      /* If we are generating a shared object, we need to
+			 output a R_386_RELATIVE reloc so that the dynamic
+			 linker can adjust this GOT entry.  */
+		      srelgot->_raw_size += sizeof (Elf32_External_Rel);
+		    }
 		}
+	      else
+		local_got_refcounts[r_symndx] += 1;
 	    }
-
-	  sgot->_raw_size += 4;
-
 	  break;
 
 	case R_386_PLT32:
@@ -587,9 +584,14 @@ elf_i386_check_relocs (abfd, info, sec, 
              creating a procedure linkage table entry.  */
 	  if (h == NULL)
 	    continue;
-
-	  h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
 
+	  if (h->plt.refcount == -1)
+	    {
+	      h->plt.refcount = 1;
+	      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+	    }
+	  else
+	    h->plt.refcount += 1;
 	  break;
 
 	case R_386_32:
@@ -766,14 +768,81 @@ elf_i386_gc_mark_hook (abfd, info, rel, 
 
 static boolean
 elf_i386_gc_sweep_hook (abfd, info, sec, relocs)
-     bfd *abfd ATTRIBUTE_UNUSED;
+     bfd *abfd;
      struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     asection *sec ATTRIBUTE_UNUSED;
-     const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
+     asection *sec;
+     const Elf_Internal_Rela *relocs;
 {
-  /* ??? It would seem that the existing i386 code does no sort
-     of reference counting or whatnot on its GOT and PLT entries,
-     so it is not possible to garbage collect them at this time.  */
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  bfd_signed_vma *local_got_refcounts;
+  const Elf_Internal_Rela *rel, *relend;
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+  bfd *dynobj;
+  asection *sgot;
+  asection *srelgot;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (abfd);
+  local_got_refcounts = elf_local_got_refcounts (abfd);
+
+  dynobj = elf_hash_table (info)->dynobj;
+  if (dynobj == NULL)
+    return true;
+
+  sgot = bfd_get_section_by_name (dynobj, ".got");
+  srelgot = bfd_get_section_by_name (dynobj, ".rel.got");
+
+  relend = relocs + sec->reloc_count;
+  for (rel = relocs; rel < relend; rel++)
+    switch (ELF32_R_TYPE (rel->r_info))
+      {
+      case R_386_GOT32:
+      case R_386_GOTOFF:
+      case R_386_GOTPC:
+	r_symndx = ELF32_R_SYM (rel->r_info);
+	if (r_symndx >= symtab_hdr->sh_info)
+	  {
+	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	    if (h->got.refcount > 0)
+	      {
+		h->got.refcount -= 1;
+		if (h->got.refcount == 0)
+		  {
+		    sgot->_raw_size -= 4;
+		    srelgot->_raw_size -= sizeof (Elf32_External_Rel);
+		  }
+	      }
+	  }
+	else if (local_got_refcounts != NULL)
+	  {
+	    if (local_got_refcounts[r_symndx] > 0)
+	      {
+		local_got_refcounts[r_symndx] -= 1;
+		if (local_got_refcounts[r_symndx] == 0)
+		  {
+		    sgot->_raw_size -= 4;
+		    if (info->shared)
+		      srelgot->_raw_size -= sizeof (Elf32_External_Rel);
+		  }
+	      }
+	  }
+	break;
+
+      case R_386_PLT32:
+	r_symndx = ELF32_R_SYM (rel->r_info);
+	if (r_symndx >= symtab_hdr->sh_info)
+	  {
+	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	    if (h->plt.refcount > 0)
+	      h->plt.refcount -= 1;
+	  }
+	break;
+
+      default:
+	break;
+      }
 
   return true;
 }
@@ -812,16 +881,18 @@ elf_i386_adjust_dynamic_symbol (info, h)
   if (h->type == STT_FUNC
       || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
     {
-      if (! info->shared
-	  && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
-	  && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0)
+      if ((! info->shared
+	   && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
+	   && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0)
+	  || (info->shared && h->plt.refcount <= 0))
 	{
 	  /* This case can occur if we saw a PLT32 reloc in an input
-             file, but the symbol was never referred to by a dynamic
-             object.  In such a case, we don't actually need to build
-             a procedure linkage table, and we can just do a PC32
-             reloc instead.  */
-	  BFD_ASSERT ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0);
+	     file, but the symbol was never referred to by a dynamic
+	     object, or if all references were garbage collected.  In
+	     such a case, we don't actually need to build a procedure
+	     linkage table, and we can just do a PC32 reloc instead.  */
+	  h->plt.offset = (bfd_vma) -1;
+	  h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
 	  return true;
 	}
 
@@ -859,13 +930,11 @@ elf_i386_adjust_dynamic_symbol (info, h)
 
       /* We also need to make an entry in the .got.plt section, which
 	 will be placed in the .got section by the linker script.  */
-
       s = bfd_get_section_by_name (dynobj, ".got.plt");
       BFD_ASSERT (s != NULL);
       s->_raw_size += 4;
 
       /* We also need to make an entry in the .rel.plt section.  */
-
       s = bfd_get_section_by_name (dynobj, ".rel.plt");
       BFD_ASSERT (s != NULL);
       s->_raw_size += sizeof (Elf32_External_Rel);
@@ -1193,9 +1262,14 @@ elf_i386_relocate_section (output_bfd, i
   sym_hashes = elf_sym_hashes (input_bfd);
   local_got_offsets = elf_local_got_offsets (input_bfd);
 
-  sgot = NULL;
-  splt = NULL;
   sreloc = NULL;
+  splt = NULL;
+  sgot = NULL;
+  if (dynobj != NULL)
+    {
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+    }
 
   rel = relocs;
   relend = relocs + input_section->reloc_count;
@@ -1273,6 +1347,7 @@ elf_i386_relocate_section (output_bfd, i
 	      sec = h->root.u.def.section;
 	      if (r_type == R_386_GOTPC
 		  || (r_type == R_386_PLT32
+		      && splt != NULL
 		      && h->plt.offset != (bfd_vma) -1)
 		  || (r_type == R_386_GOT32
 		      && elf_hash_table (info)->dynamic_sections_created
@@ -1333,11 +1408,7 @@ elf_i386_relocate_section (output_bfd, i
 	case R_386_GOT32:
 	  /* Relocation is to the entry for this symbol in the global
 	     offset table.  */
-	  if (sgot == NULL)
-	    {
-	      sgot = bfd_get_section_by_name (dynobj, ".got");
-	      BFD_ASSERT (sgot != NULL);
-	    }
+	  BFD_ASSERT (sgot != NULL);
 
 	  if (h != NULL)
 	    {
@@ -1456,12 +1527,13 @@ elf_i386_relocate_section (output_bfd, i
 	  /* Relocation is to the entry for this symbol in the
 	     procedure linkage table.  */
 
-	  /* Resolve a PLT32 reloc again a local symbol directly,
+	  /* Resolve a PLT32 reloc against a local symbol directly,
              without using the procedure linkage table.  */
 	  if (h == NULL)
 	    break;
 
-	  if (h->plt.offset == (bfd_vma) -1)
+	  if (h->plt.offset == (bfd_vma) -1
+	      || splt == NULL)
 	    {
 	      /* We didn't make a PLT entry for this symbol.  This
                  happens when statically linking PIC code, or when
@@ -1469,12 +1541,6 @@ elf_i386_relocate_section (output_bfd, i
 	      break;
 	    }
 
-	  if (splt == NULL)
-	    {
-	      splt = bfd_get_section_by_name (dynobj, ".plt");
-	      BFD_ASSERT (splt != NULL);
-	    }
-
 	  relocation = (splt->output_section->vma
 			+ splt->output_offset
 			+ h->plt.offset);
@@ -1741,17 +1807,21 @@ elf_i386_finish_dynamic_symbol (output_b
 		      + sgot->output_offset
 		      + (h->got.offset &~ 1));
 
-      /* If this is a -Bsymbolic link, and the symbol is defined
-	 locally, we just want to emit a RELATIVE reloc.  Likewise if
-	 the symbol was forced to be local because of a version file.
+      /* If this is a static link, or it is a -Bsymbolic link and the
+	 symbol is defined locally or was forced to be local because
+	 of a version file, we just want to emit a RELATIVE reloc.
 	 The entry in the global offset table will already have been
 	 initialized in the relocate_section function.  */
-      if (info->shared
-	  && (info->symbolic || h->dynindx == -1)
-	  && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
-	rel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
+      if (! elf_hash_table (info)->dynamic_sections_created
+	  || (info->shared
+	      && (info->symbolic || h->dynindx == -1)
+	      && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+	{
+	  rel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
+	}
       else
 	{
+	  BFD_ASSERT((h->got.offset & 1) == 0);
 	  bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
 	  rel.r_info = ELF32_R_INFO (h->dynindx, R_386_GLOB_DAT);
 	}
@@ -1925,27 +1995,6 @@ elf_i386_finish_dynamic_sections (output
 #define ELF_ARCH			bfd_arch_i386
 #define ELF_MACHINE_CODE		EM_386
 #define ELF_MAXPAGESIZE			0x1000
-#define elf_info_to_howto		elf_i386_info_to_howto
-#define elf_info_to_howto_rel		elf_i386_info_to_howto_rel
-#define bfd_elf32_bfd_reloc_type_lookup	elf_i386_reloc_type_lookup
-#define bfd_elf32_bfd_is_local_label_name \
-					elf_i386_is_local_label_name
-#define elf_backend_create_dynamic_sections \
-					_bfd_elf_create_dynamic_sections
-#define bfd_elf32_bfd_link_hash_table_create \
-					elf_i386_link_hash_table_create
-#define elf_backend_check_relocs	elf_i386_check_relocs
-#define elf_backend_adjust_dynamic_symbol \
-					elf_i386_adjust_dynamic_symbol
-#define elf_backend_size_dynamic_sections \
-					elf_i386_size_dynamic_sections
-#define elf_backend_relocate_section	elf_i386_relocate_section
-#define elf_backend_finish_dynamic_symbol \
-					elf_i386_finish_dynamic_symbol
-#define elf_backend_finish_dynamic_sections \
-					elf_i386_finish_dynamic_sections
-#define elf_backend_gc_mark_hook	elf_i386_gc_mark_hook
-#define elf_backend_gc_sweep_hook	elf_i386_gc_sweep_hook
 
 #define elf_backend_can_gc_sections	1
 #define elf_backend_want_got_plt	1
@@ -1953,5 +2002,23 @@ elf_i386_finish_dynamic_sections (output
 #define elf_backend_want_plt_sym	0
 #define elf_backend_got_header_size	12
 #define elf_backend_plt_header_size	PLT_ENTRY_SIZE
+
+#define elf_info_to_howto		      elf_i386_info_to_howto
+#define elf_info_to_howto_rel		      elf_i386_info_to_howto_rel
+
+#define bfd_elf32_bfd_final_link	      _bfd_elf32_gc_common_final_link
+#define bfd_elf32_bfd_is_local_label_name     elf_i386_is_local_label_name
+#define bfd_elf32_bfd_link_hash_table_create  elf_i386_link_hash_table_create
+#define bfd_elf32_bfd_reloc_type_lookup	      elf_i386_reloc_type_lookup
+
+#define elf_backend_adjust_dynamic_symbol     elf_i386_adjust_dynamic_symbol
+#define elf_backend_check_relocs	      elf_i386_check_relocs
+#define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
+#define elf_backend_finish_dynamic_sections   elf_i386_finish_dynamic_sections
+#define elf_backend_finish_dynamic_symbol     elf_i386_finish_dynamic_symbol
+#define elf_backend_gc_mark_hook	      elf_i386_gc_mark_hook
+#define elf_backend_gc_sweep_hook	      elf_i386_gc_sweep_hook
+#define elf_backend_relocate_section	      elf_i386_relocate_section
+#define elf_backend_size_dynamic_sections     elf_i386_size_dynamic_sections
 
 #include "elf32-target.h"
Index: elf32-m68k.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-m68k.c,v
retrieving revision 1.12
diff -u -p -r1.12 elf32-m68k.c
--- elf32-m68k.c	2000/03/01 20:39:07	1.12
+++ elf32-m68k.c	2000/04/24 01:33:30
@@ -847,19 +847,19 @@ elf_m68k_gc_sweep_hook (abfd, info, sec,
   unsigned long r_symndx;
   struct elf_link_hash_entry *h;
   bfd *dynobj;
-  asection *sgot = NULL;
-  asection *srelgot = NULL;
+  asection *sgot;
+  asection *srelgot;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
   dynobj = elf_hash_table (info)->dynobj;
-  if (dynobj)
-    {
-      sgot = bfd_get_section_by_name (dynobj, ".got");
-      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-    }
+  if (dynobj == NULL)
+    return true;
+
+  sgot = bfd_get_section_by_name (dynobj, ".got");
+  srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
@@ -887,7 +887,7 @@ elf_m68k_gc_sweep_hook (abfd, info, sec,
 		    }
 		}
 	    }
-	  else
+	  else if (local_got_refcounts != NULL)
 	    {
 	      if (local_got_refcounts[r_symndx] > 0)
 		{
Index: elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.10
diff -u -p -r1.10 elf32-ppc.c
--- elf32-ppc.c	2000/04/20 05:37:05	1.10
+++ elf32-ppc.c	2000/04/24 01:33:38
@@ -2540,7 +2540,7 @@ ppc_elf_gc_sweep_hook (abfd, info, sec, 
 	    if (h->got.refcount > 0)
 	      h->got.refcount--;
 	  }
-	else
+	else if (local_got_refcounts != NULL)
 	  {
 	    if (local_got_refcounts[r_symndx] > 0)
 	      local_got_refcounts[r_symndx]--;
@@ -3022,6 +3022,7 @@ ppc_elf_relocate_section (output_bfd, in
 	    {
 	      sec = h->root.u.def.section;
 	      if ((r_type == R_PPC_PLT32
+		   && splt != NULL
 		   && h->plt.offset != (bfd_vma) -1)
 		  || (r_type == R_PPC_LOCAL24PC
 		      && sec->output_section == NULL)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]