From 58355477442d7d42b417f2e684eeaada925921be Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 27 Apr 2000 00:31:17 +0000 Subject: [PATCH] Reference count .plt and .got on x86 for garbage collection code. Fix a couple of m68k and ppc bugs discovered while testing x86 gc. --- bfd/ChangeLog | 23 ++++- bfd/elf32-i386.c | 265 ++++++++++++++++++++++++++++++++++--------------------- bfd/elf32-m68k.c | 16 ++-- bfd/elf32-ppc.c | 3 +- bfd/elflink.h | 3 +- 5 files changed, 200 insertions(+), 110 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 8cde4a3673..cdfc0c8153 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,24 @@ +2000-04-27 Alan Modra + + * 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. + Wed Apr 26 16:31:28 2000 Clinton Popetz * config.bfd: Remove extraneous bfd_powerpc_64_arch. @@ -5497,7 +5518,7 @@ Thu Jun 25 18:31:41 1998 Richard Henderson (ppc_elf_howto_raw): Handle them. (ppc_elf_reloc_type_lookup): Likewise. (ppc_elf_relocate_section): Likewise. - (ppc_elf_check_relocs): Reference count .got and .plt entires. + (ppc_elf_check_relocs): Reference count .got and .plt entries. Handle new vtable relocs. (ppc_elf_adjust_dynamic_symbol): Recognize unused .plt entries. (ppc_elf_gc_mark_hook, ppc_elf_gc_sweep_hook): New. diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index e4dbf9da09..8434053f2d 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -439,7 +439,7 @@ elf_i386_check_relocs (abfd, info, sec, relocs) 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, relocs) 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, relocs) 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: @@ -588,8 +585,13 @@ elf_i386_check_relocs (abfd, info, sec, relocs) 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, h, sym) 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, info, input_bfd, input_section, 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, info, input_bfd, input_section, 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, info, input_bfd, input_section, 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, info, input_bfd, input_section, /* 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, info, input_bfd, input_section, 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_bfd, info, h, sym) + 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_bfd, info) #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 @@ -1954,4 +2003,22 @@ elf_i386_finish_dynamic_sections (output_bfd, info) #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" diff --git a/bfd/elf32-m68k.c b/bfd/elf32-m68k.c index cc62230175..38e6e9cb39 100644 --- a/bfd/elf32-m68k.c +++ b/bfd/elf32-m68k.c @@ -847,19 +847,19 @@ elf_m68k_gc_sweep_hook (abfd, info, sec, relocs) 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, relocs) } } } - else + else if (local_got_refcounts != NULL) { if (local_got_refcounts[r_symndx] > 0) { diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index db5b268424..0ec909a921 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -2540,7 +2540,7 @@ ppc_elf_gc_sweep_hook (abfd, info, sec, relocs) 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, info, input_bfd, input_section, { 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) diff --git a/bfd/elflink.h b/bfd/elflink.h index bddf290711..b5e4eee912 100644 --- a/bfd/elflink.h +++ b/bfd/elflink.h @@ -6615,7 +6615,8 @@ elf_gc_common_finalize_got_offsets (abfd, info) } } - /* 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); -- 2.11.0