diff options
-rw-r--r-- | bfd/ChangeLog | 27 | ||||
-rw-r--r-- | bfd/elf-bfd.h | 74 | ||||
-rw-r--r-- | bfd/elf.c | 110 | ||||
-rw-r--r-- | bfd/elflink.h | 189 |
4 files changed, 344 insertions, 56 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 0e4e5cd..295938b 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,30 @@ +Sun Mar 9 23:08:49 1997 Ian Lance Taylor <ian@cygnus.com> + + From Eric Youngdale <eric@andante.jic.com>: + * elf-bfd.h (elf_symbol_type): Add version field. + * elfcode.h (elf_slurp_symbol_table): Set version field. + * elflink.h (elf_link_add_object_symbols): When creating an + indirect symbol for a default version symbol, set DEF_DYNAMIC if + appropriate. Set up an indirection from the nondefault version of + the symbol as well. + (NAME(bfd_elf,size_dynamic_sections)): Call + elf_link_assign_sym_version before checking whether there are any + versions. Always record the version name as a dynamic symbol. + Initialize counters. + (elf_link_assign_sym_version): After finding a version, see if a + symbol should be forced to local scope. Create a new version + definition if appropriate. + (elf_link_output_extsym): Correct indirect symbol handling. + * elf.c (bfd_elf_print_symbol): Print version information. + (bfd_section_from_shdr): Turn version sections into BFD sections. + (elf_fake_sections): Only copy cverdefs and cverrefs into sh_info + if sh_info is not already set. + (_bfd_elf_copy_private_section_data): Copy sh_info for version + sections. + * elflink.c (_bfd_elf_link_record_dynamic_symbol): Tell + _bfd_stringtab_add to copy the name into permanent memory if + appropriate. + Fri Mar 7 11:55:31 1997 H.J. Lu <hjl@gnu.ai.mit.edu> * elf64-alpha.c (alpha_elf_dynamic_symbol_p): Fully parenthesize. diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 2253259..455bc5c 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -1,5 +1,5 @@ /* BFD back-end data structures for ELF files. - Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1992, 93, 94, 95, 96, 1997 Free Software Foundation, Inc. Written by Cygnus Support. This file is part of BFD, the Binary File Descriptor library. @@ -62,6 +62,12 @@ typedef struct PTR any; } tc_data; + + /* Version information. This is from an Elf_Internal_Versym + structure in a SHT_GNU_versym section. It is zero if there is no + version information. */ + unsigned short version; + } elf_symbol_type; /* ELF linker hash table entries. */ @@ -107,6 +113,19 @@ struct elf_link_hash_entry from the beginning of the section. */ struct elf_linker_section_pointers *linker_section_pointer; + /* Version information. */ + union + { + /* This field is used for a symbol which is not defined in a + regular object. It points to the version information read in + from the dynamic object. */ + Elf_Internal_Verdef *verdef; + /* This field is used for a symbol which is defined in a regular + object. It is set up in size_dynamic_sections. It points to + the version information we should write out for this symbol. */ + struct bfd_elf_version_tree *vertree; + } verinfo; + /* Symbol type (STT_NOTYPE, STT_OBJECT, etc.). */ char type; @@ -114,7 +133,7 @@ struct elf_link_hash_entry unsigned char other; /* Some flags; legal values follow. */ - unsigned char elf_link_hash_flags; + unsigned short elf_link_hash_flags; /* Symbol is referenced by a non-shared object. */ #define ELF_LINK_HASH_REF_REGULAR 01 /* Symbol is defined by a non-shared object. */ @@ -131,8 +150,8 @@ struct elf_link_hash_entry #define ELF_LINK_HASH_NEEDS_PLT 0100 /* Symbol appears in a non-ELF input file. */ #define ELF_LINK_NON_ELF 0200 - /* Note: If you add more flags, you must change the type of - elf_link_hash_flags. */ + /* Symbol should be marked as hidden in the version information. */ +#define ELF_LINK_HIDDEN 0400 }; /* ELF linker hash table. */ @@ -572,8 +591,12 @@ struct elf_obj_tdata Elf_Internal_Shdr strtab_hdr; Elf_Internal_Shdr dynsymtab_hdr; Elf_Internal_Shdr dynstrtab_hdr; + Elf_Internal_Shdr dynversym_hdr; + Elf_Internal_Shdr dynverref_hdr; + Elf_Internal_Shdr dynverdef_hdr; unsigned int symtab_section, shstrtab_section; unsigned int strtab_section, dynsymtab_section; + unsigned int dynversym_section, dynverdef_section, dynverref_section; file_ptr next_file_pos; void *prstatus; /* The raw /proc prstatus structure */ void *prpsinfo; /* The raw /proc prpsinfo structure */ @@ -626,9 +649,26 @@ struct elf_obj_tdata find_nearest_line. */ struct mips_elf_find_line *find_line_info; + /* An array of stub sections indexed by symbol number, used by the + MIPS ELF linker. FIXME: We should figure out some way to only + include this field for a MIPS ELF target. */ + asection **local_stubs; + /* Used to determine if the e_flags field has been initialized */ boolean flags_init; + /* Number of symbol version definitions we are about to emit. */ + int cverdefs; + + /* Number of symbol version references we are about to emit. */ + int cverrefs; + + /* Symbol version definitions in external objects. */ + Elf_Internal_Verdef *verdef; + + /* Symbol version references to external objects. */ + Elf_Internal_Verneed *verref; + /* Linker sections that we are interested in. */ struct elf_linker_section *linker_section[ (int)LINKER_SECTION_MAX ]; }; @@ -639,6 +679,9 @@ struct elf_obj_tdata #define elf_shstrtab(bfd) (elf_tdata(bfd) -> strtab_ptr) #define elf_onesymtab(bfd) (elf_tdata(bfd) -> symtab_section) #define elf_dynsymtab(bfd) (elf_tdata(bfd) -> dynsymtab_section) +#define elf_dynversym(bfd) (elf_tdata(bfd) -> dynversym_section) +#define elf_dynverdef(bfd) (elf_tdata(bfd) -> dynverdef_section) +#define elf_dynverref(bfd) (elf_tdata(bfd) -> dynverref_section) #define elf_num_locals(bfd) (elf_tdata(bfd) -> num_locals) #define elf_num_globals(bfd) (elf_tdata(bfd) -> num_globals) #define elf_section_syms(bfd) (elf_tdata(bfd) -> section_syms) @@ -654,6 +697,27 @@ struct elf_obj_tdata #define elf_flags_init(bfd) (elf_tdata(bfd) -> flags_init) #define elf_linker_section(bfd,n) (elf_tdata(bfd) -> linker_section[(int)n]) +extern void _bfd_elf_swap_verdef_in + PARAMS ((bfd *, const Elf_External_Verdef *, Elf_Internal_Verdef *)); +extern void _bfd_elf_swap_verdef_out + PARAMS ((bfd *, const Elf_Internal_Verdef *, Elf_External_Verdef *)); +extern void _bfd_elf_swap_verdaux_in + PARAMS ((bfd *, const Elf_External_Verdaux *, Elf_Internal_Verdaux *)); +extern void _bfd_elf_swap_verdaux_out + PARAMS ((bfd *, const Elf_Internal_Verdaux *, Elf_External_Verdaux *)); +extern void _bfd_elf_swap_verneed_in + PARAMS ((bfd *, const Elf_External_Verneed *, Elf_Internal_Verneed *)); +extern void _bfd_elf_swap_verneed_out + PARAMS ((bfd *, const Elf_Internal_Verneed *, Elf_External_Verneed *)); +extern void _bfd_elf_swap_vernaux_in + PARAMS ((bfd *, const Elf_External_Vernaux *, Elf_Internal_Vernaux *)); +extern void _bfd_elf_swap_vernaux_out + PARAMS ((bfd *, const Elf_Internal_Vernaux *, Elf_External_Vernaux *)); +extern void _bfd_elf_swap_versym_in + PARAMS ((bfd *, const Elf_External_Versym *, Elf_Internal_Versym *)); +extern void _bfd_elf_swap_versym_out + PARAMS ((bfd *, const Elf_Internal_Versym *, Elf_External_Versym *)); + extern int _bfd_elf_section_from_bfd_section PARAMS ((bfd *, asection *)); extern char *bfd_elf_string_from_elf_section PARAMS ((bfd *, unsigned, unsigned)); @@ -690,6 +754,7 @@ extern boolean _bfd_elf_link_hash_table_init struct bfd_hash_entry *(*) (struct bfd_hash_entry *, struct bfd_hash_table *, const char *))); +extern boolean _bfd_elf_slurp_version_tables PARAMS ((bfd *)); extern boolean _bfd_elf_copy_private_symbol_data PARAMS ((bfd *, asymbol *, bfd *, asymbol *)); @@ -712,6 +777,7 @@ extern long _bfd_elf_canonicalize_dynamic_reloc PARAMS ((bfd *, arelent **, extern asymbol *_bfd_elf_make_empty_symbol PARAMS ((bfd *)); extern void _bfd_elf_get_symbol_info PARAMS ((bfd *, asymbol *, symbol_info *)); +extern boolean _bfd_elf_is_local_label_name PARAMS ((bfd *, const char *)); extern alent *_bfd_elf_get_lineno PARAMS ((bfd *, asymbol *)); extern boolean _bfd_elf_set_arch_mach PARAMS ((bfd *, enum bfd_architecture, unsigned long)); @@ -701,9 +701,10 @@ _bfd_elf_print_private_bfd_data (abfd, farg) } /* Display ELF-specific fields of a symbol. */ + void -bfd_elf_print_symbol (ignore_abfd, filep, symbol, how) - bfd *ignore_abfd; +bfd_elf_print_symbol (abfd, filep, symbol, how) + bfd *abfd; PTR filep; asymbol *symbol; bfd_print_symbol_type how; @@ -733,11 +734,64 @@ bfd_elf_print_symbol (ignore_abfd, filep, symbol, how) (bfd_is_com_section (symbol->section) ? ((elf_symbol_type *) symbol)->internal_elf_sym.st_value : ((elf_symbol_type *) symbol)->internal_elf_sym.st_size)); + + /* If we have version information, print it. */ + if (elf_tdata (abfd)->dynversym_section != 0 + && (elf_tdata (abfd)->dynverdef_section != 0 + || elf_tdata (abfd)->dynverref_section != 0)) + { + unsigned int vernum; + const char *version_string; + + vernum = ((elf_symbol_type *) symbol)->version & VERSYM_VERSION; + + if (vernum == 0) + version_string = ""; + else if (vernum == 1) + version_string = "Base"; + else if (vernum < elf_tdata (abfd)->cverdefs) + version_string = + elf_tdata (abfd)->verdef[vernum - 1].vd_nodename; + else + { + Elf_Internal_Verneed *t; + + version_string = ""; + for (t = elf_tdata (abfd)->verref; + t != NULL; + t = t->vn_nextref) + { + Elf_Internal_Vernaux *a; + + for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr) + { + if (a->vna_other == vernum) + { + version_string = a->vna_nodename; + break; + } + } + } + } + + if ((((elf_symbol_type *) symbol)->version & VERSYM_HIDDEN) == 0) + fprintf (file, " %-12s", version_string); + else + { + int i; + + fprintf (file, " (%s)", version_string); + for (i = strlen (version_string) - 10; i > 0; --i) + putc (' ', file); + } + } + /* If the st_other field is not zero, print it. */ if (((elf_symbol_type *) symbol)->internal_elf_sym.st_other != 0) fprintf (file, " 0x%02x", ((unsigned int) ((elf_symbol_type *) symbol)->internal_elf_sym.st_other)); + fprintf (file, " %s", symbol->name); } break; @@ -1098,16 +1152,19 @@ bfd_section_from_shdr (abfd, shindex) case SHT_GNU_verdef: elf_dynverdef (abfd) = shindex; elf_tdata (abfd)->dynverdef_hdr = *hdr; + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); break; case SHT_GNU_versym: elf_dynversym (abfd) = shindex; elf_tdata (abfd)->dynversym_hdr = *hdr; + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); break; case SHT_GNU_verneed: elf_dynverref (abfd) = shindex; elf_tdata (abfd)->dynverref_hdr = *hdr; + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); break; case SHT_SHLIB: @@ -1338,13 +1395,27 @@ elf_fake_sections (abfd, asect, failedptrarg) { this_hdr->sh_type = SHT_GNU_verdef; this_hdr->sh_entsize = 0; - this_hdr->sh_info = elf_tdata (abfd)->cverdefs; + /* objcopy or strip will copy over sh_info, but may not set + cverdefs. The linker will set cverdefs, but sh_info will be + zero. */ + if (this_hdr->sh_info == 0) + this_hdr->sh_info = elf_tdata (abfd)->cverdefs; + else + BFD_ASSERT (elf_tdata (abfd)->cverdefs == 0 + || this_hdr->sh_info == elf_tdata (abfd)->cverdefs); } else if (strcmp (asect->name, ".gnu.version_r") == 0) { this_hdr->sh_type = SHT_GNU_verneed; this_hdr->sh_entsize = 0; - this_hdr->sh_info = elf_tdata (abfd)->cverrefs; + /* objcopy or strip will copy over sh_info, but may not set + cverrefs. The linker will set cverrefs, but sh_info will be + zero. */ + if (this_hdr->sh_info == 0) + this_hdr->sh_info = elf_tdata (abfd)->cverrefs; + else + BFD_ASSERT (elf_tdata (abfd)->cverrefs == 0 + || this_hdr->sh_info == elf_tdata (abfd)->cverrefs); } else if ((asect->flags & SEC_ALLOC) != 0 && (asect->flags & SEC_LOAD) != 0) @@ -3187,7 +3258,9 @@ _bfd_elf_copy_private_section_data (ibfd, isec, obfd, osec) ohdr->sh_entsize = ihdr->sh_entsize; if (ihdr->sh_type == SHT_SYMTAB - || ihdr->sh_type == SHT_DYNSYM) + || ihdr->sh_type == SHT_DYNSYM + || ihdr->sh_type == SHT_GNU_verneed + || ihdr->sh_type == SHT_GNU_verdef) ohdr->sh_info = ihdr->sh_info; return true; @@ -3831,17 +3904,34 @@ _bfd_elf_get_symbol_info (ignore_abfd, symbol, ret) bfd_symbol_info (symbol, ret); } -/* Return whether a symbol name implies a local symbol. In ELF, local - symbols generally start with ``.L''. Most targets use this - function for the is_local_label_name entry point, but some override - it. */ +/* Return whether a symbol name implies a local symbol. Most targets + use this function for the is_local_label_name entry point, but some + override it. */ boolean _bfd_elf_is_local_label_name (abfd, name) bfd *abfd; const char *name; { - return name[0] == '.' && name[1] == 'L'; + /* Normal local symbols start with ``.L''. */ + if (name[0] == '.' && name[1] == 'L') + return true; + + /* At least some SVR4 compilers (e.g., UnixWare 2.1 cc) generate + DWARF debugging symbols starting with ``..''. */ + if (name[0] == '.' && name[1] == '.') + return true; + + /* gcc will sometimes generate symbols beginning with ``_.L_'' when + emitting DWARF debugging output. I suspect this is actually a + small bug in gcc (it calls ASM_OUTPUT_LABEL when it should call + ASM_GENERATE_INTERNAL_LABEL, and this causes the leading + underscore to be emitted on some ELF targets). For ease of use, + we treat such symbols as local. */ + if (name[0] == '_' && name[1] == '.' && name[2] == 'L' && name[3] == '_') + return true; + + return false; } alent * diff --git a/bfd/elflink.h b/bfd/elflink.h index 940bd2a..5c1ac67 100644 --- a/bfd/elflink.h +++ b/bfd/elflink.h @@ -1075,7 +1075,41 @@ elf_link_add_object_symbols (abfd, info) goto error_return; if (hi->root.type == bfd_link_hash_indirect) - hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF; + { + hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF; + if (dynamic) + hi->elf_link_hash_flags |= ELF_LINK_HASH_DEF_DYNAMIC; + /* We don't set DEF_REGULAR because we don't the + symbol to get exported even if we are + exporting all defined symbols. FIXME: What a + hack. */ + /* FIXME: Do we need to copy any flags from H to + HI? */ + } + + /* We also need to define an indirection from the + nondefault version of the symbol. */ + + shortname = bfd_hash_allocate (&info->hash->table, + strlen (name)); + if (shortname == NULL) + goto error_return; + strncpy (shortname, name, p - name); + strcpy (shortname + (p - name), p + 1); + + hi = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, shortname, BSF_INDIRECT, + bfd_ind_section_ptr, (bfd_vma) 0, name, false, + collect, (struct bfd_link_hash_entry **) &hi))) + goto error_return; + + if (hi->root.type == bfd_link_hash_indirect) + { + hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF; + if (dynamic) + hi->elf_link_hash_flags |= ELF_LINK_HASH_DEF_DYNAMIC; + } } } @@ -1823,30 +1857,35 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath, size_t i; size_t bucketcount = 0; Elf_Internal_Sym isym; + struct elf_assign_sym_version_info sinfo; /* Set up the version definition section. */ s = bfd_get_section_by_name (dynobj, ".gnu.version_d"); BFD_ASSERT (s != NULL); + + /* Attach all the symbols to their version information. This + may cause some symbols to be unexported. */ + sinfo.output_bfd = output_bfd; + sinfo.info = info; + sinfo.verdefs = verdefs; + sinfo.export_dynamic = export_dynamic; + sinfo.removed_dynamic = false; + sinfo.failed = false; + + elf_link_hash_traverse (elf_hash_table (info), + elf_link_assign_sym_version, + (PTR) &sinfo); + if (sinfo.failed) + return false; + + /* We may have created additional version definitions if we are + just linking a regular application. */ + verdefs = sinfo.verdefs; + if (verdefs == NULL) { - struct elf_assign_sym_version_info sinfo; asection **spp; - /* No version script was used. In this case, we just check - that there were no version overrides for any symbols. */ - sinfo.output_bfd = output_bfd; - sinfo.info = info; - sinfo.verdefs = verdefs; - sinfo.removed_dynamic = false; - sinfo.export_dynamic = export_dynamic; - sinfo.failed = false; - - elf_link_hash_traverse (elf_hash_table (info), - elf_link_assign_sym_version, - (PTR) &sinfo); - if (sinfo.failed) - return false; - /* Don't include this section in the output file. */ for (spp = &output_bfd->sections; *spp != s->output_section; @@ -1857,7 +1896,6 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath, } else { - struct elf_assign_sym_version_info sinfo; unsigned int cdefs; bfd_size_type size; struct bfd_elf_version_tree *t; @@ -1865,20 +1903,6 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath, Elf_Internal_Verdef def; Elf_Internal_Verdaux defaux; - /* Attach all of the symbols to their version information. - This may cause some symbols to be unexported. */ - sinfo.output_bfd = output_bfd; - sinfo.info = info; - sinfo.verdefs = verdefs; - sinfo.export_dynamic = export_dynamic; - sinfo.removed_dynamic = false; - sinfo.failed = false; - elf_link_hash_traverse (elf_hash_table (info), - elf_link_assign_sym_version, - (PTR) &sinfo); - if (sinfo.failed) - return false; - if (sinfo.removed_dynamic) { /* Some dynamic symbols were changed to be local @@ -1979,11 +2003,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath, h->type = STT_OBJECT; h->verinfo.vertree = t; - if (info->shared) - { - if (! _bfd_elf_link_record_dynamic_symbol (info, h)) - return false; - } + if (! _bfd_elf_link_record_dynamic_symbol (info, h)) + return false; def.vd_version = VER_DEF_CURRENT; def.vd_flags = 0; @@ -2075,6 +2096,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath, bfd_byte *p; /* Build the version definition section. */ + size = 0; + crefs = 0; for (t = elf_tdata (output_bfd)->verref; t != NULL; t = t->vn_nextref) @@ -2549,14 +2572,94 @@ elf_link_assign_sym_version (h, data) { h->verinfo.vertree = t; t->used = true; + + /* See if there is anything to force this symbol to + local scope. */ + if (t->locals != NULL) + { + int len; + char *alc; + struct bfd_elf_version_expr *d; + + len = p - h->root.root.string; + alc = bfd_alloc (sinfo->output_bfd, len); + if (alc == NULL) + return false; + strncpy (alc, h->root.root.string, len - 1); + alc[len - 1] = '\0'; + if (alc[len - 2] == ELF_VER_CHR) + alc[len - 2] = '\0'; + + for (d = t->locals; d != NULL; d = d->next) + { + if ((d->match[0] == '*' && d->match[1] == '\0') + || fnmatch (d->match, alc, 0) == 0) + { + if (h->dynindx != -1 + && info->shared + && ! sinfo->export_dynamic + && (h->elf_link_hash_flags + & ELF_LINK_HASH_NEEDS_PLT) == 0) + { + sinfo->removed_dynamic = true; + h->dynindx = -1; + /* FIXME: The name of the symbol has + already been recorded in the dynamic + string table section. */ + } + + break; + } + } + + bfd_release (sinfo->output_bfd, alc); + } + break; } } - if (t == NULL) + /* If we are building an application, we need to create a + version node for this version. */ + if (t == NULL && ! info->shared) + { + struct bfd_elf_version_tree **pp; + int version_index; + + /* If we aren't going to export this symbol, we don't need + to worry about it. */ + if (h->dynindx == -1) + return true; + + t = ((struct bfd_elf_version_tree *) + bfd_alloc (sinfo->output_bfd, sizeof *t)); + if (t == NULL) + { + sinfo->failed = true; + return false; + } + + t->next = NULL; + t->name = p; + t->globals = NULL; + t->locals = NULL; + t->deps = NULL; + t->name_indx = (unsigned int) -1; + t->used = true; + + version_index = 1; + for (pp = &sinfo->verdefs; *pp != NULL; pp = &(*pp)->next) + ++version_index; + t->vernum = version_index; + + *pp = t; + + h->verinfo.vertree = t; + } + else if (t == NULL) { - /* We could not find the version. Return an error. - FIXME: Why? */ + /* We could not find the version for a symbol when + generating a shared archive. Return an error. */ (*_bfd_error_handler) ("%s: invalid version %s", bfd_get_filename (sinfo->output_bfd), h->root.root.string); @@ -3617,8 +3720,10 @@ elf_link_output_extsym (h, data) to the decorated version of the name. For example, if the symbol foo@@GNU_1.2 is the default, which should be used when foo is used with no version, then we add an indirect symbol - foo which points to foo@@GNU_1.2. */ - if ((h->elf_link_hash_flags & ELF_LINK_NON_ELF) != 0) + foo which points to foo@@GNU_1.2. We ignore these symbols, + since the indirected symbol is already in the hash table. If + the indirect symbol is non-ELF, fall through and output it. */ + if ((h->elf_link_hash_flags & ELF_LINK_NON_ELF) == 0) return true; /* Fall through. */ |