aboutsummaryrefslogtreecommitdiff
path: root/bfd/elflink.h
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elflink.h')
-rw-r--r--bfd/elflink.h110
1 files changed, 109 insertions, 1 deletions
diff --git a/bfd/elflink.h b/bfd/elflink.h
index ab679a7..6e21a1f 100644
--- a/bfd/elflink.h
+++ b/bfd/elflink.h
@@ -80,6 +80,97 @@ elf_bfd_link_add_symbols (abfd, info)
}
}
+/* Search the symbol table of the archive element of the archive ABFD
+ whoes archove map contains a mention of SYMDEF, and determine if
+ the symbol is defined in this element. */
+static boolean
+elf_link_is_defined_archive_symbol (abfd, symdef)
+ bfd * abfd;
+ carsym * symdef;
+{
+ Elf_Internal_Shdr * hdr;
+ Elf_External_Sym * esym;
+ Elf_External_Sym * esymend;
+ Elf_External_Sym * buf = NULL;
+ size_t symcount;
+ size_t extsymcount;
+ size_t extsymoff;
+ boolean result = false;
+
+ abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
+ if (abfd == (bfd *) NULL)
+ return false;
+
+ if (! bfd_check_format (abfd, bfd_object))
+ return false;
+
+ /* Select the appropriate symbol table. */
+ if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
+ hdr = &elf_tdata (abfd)->symtab_hdr;
+ else
+ hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+
+ symcount = hdr->sh_size / sizeof (Elf_External_Sym);
+
+ /* The sh_info field of the symtab header tells us where the
+ external symbols start. We don't care about the local symbols. */
+ if (elf_bad_symtab (abfd))
+ {
+ extsymcount = symcount;
+ extsymoff = 0;
+ }
+ else
+ {
+ extsymcount = symcount - hdr->sh_info;
+ extsymoff = hdr->sh_info;
+ }
+
+ buf = ((Elf_External_Sym *)
+ bfd_malloc (extsymcount * sizeof (Elf_External_Sym)));
+ if (buf == NULL && extsymcount != 0)
+ return false;
+
+ /* Read in the symbol table.
+ FIXME: This ought to be cached somewhere. */
+ if (bfd_seek (abfd,
+ hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym),
+ SEEK_SET) != 0
+ || (bfd_read ((PTR) buf, sizeof (Elf_External_Sym), extsymcount, abfd)
+ != extsymcount * sizeof (Elf_External_Sym)))
+ {
+ free (buf);
+ return false;
+ }
+
+ /* Scan the symbol table looking for SYMDEF. */
+ esymend = buf + extsymcount;
+ for (esym = buf;
+ esym < esymend;
+ esym++)
+ {
+ Elf_Internal_Sym sym;
+ const char * name;
+
+ elf_swap_symbol_in (abfd, esym, & sym);
+
+ name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, sym.st_name);
+ if (name == (const char *) NULL)
+ break;
+
+ if (strcmp (name, symdef->name) == 0)
+ {
+ result =
+ (ELF_ST_BIND (sym.st_info) == STB_GLOBAL)
+ && (sym.st_shndx != SHN_UNDEF);
+ break;
+ }
+ }
+
+ free (buf);
+
+ return result;
+}
+
/* Add symbols from an ELF archive file to the linker hash table. We
don't use _bfd_generic_link_add_archive_symbols because of a
@@ -200,7 +291,24 @@ elf_link_add_archive_symbols (abfd, info)
if (h == NULL)
continue;
- if (h->root.type != bfd_link_hash_undefined)
+ if (h->root.type == bfd_link_hash_common)
+ {
+ /* We currently have a common symbol. The archive map contains
+ a reference to this symbol, so we may want to include it. We
+ only want to include it however, if this archive element
+ contains a definition of the symbol, not just another common
+ declaration of it.
+
+ Unfortunately some archivers (including GNU ar) will put
+ declarations of common symbols into their archive maps, as
+ well as real definitions, so we cannot just go by the archive
+ map alone. Instead we must read in the element's symbol
+ table and check that to see what kind of symbol definition
+ this is. */
+ if (! elf_link_is_defined_archive_symbol (abfd, symdef))
+ continue;
+ }
+ else if (h->root.type != bfd_link_hash_undefined)
{
if (h->root.type != bfd_link_hash_undefweak)
defined[i] = true;