forked from Imagelibrary/binutils-gdb
2004-02-28 H.J. Lu <hongjiu.lu@intel.com>
* elf-bfd.h (_bfd_elf_link_add_archive_symbols): New prototype. * elflink.h (is_global_data_symbol_definition): Moved to elflink.c. (elf_link_is_defined_archive_symbol): Likewise. (elf_link_add_archive_symbols): Likewise. Renamed to _bfd_elf_link_add_archive_symbols. * elflink.c (elf_link_is_defined_archive_symbol): Get the size of ELF symbol table entry from backend. (_bfd_elf_link_add_archive_symbols): Call bfd_link_add_symbols instead of elf_link_add_object_symbols.
This commit is contained in:
345
bfd/elflink.c
345
bfd/elflink.c
@@ -2564,3 +2564,348 @@ _bfd_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
|
||||
|
||||
return tls;
|
||||
}
|
||||
|
||||
/* Return TRUE iff this is a non-common, definition of a non-function symbol. */
|
||||
static bfd_boolean
|
||||
is_global_data_symbol_definition (bfd *abfd ATTRIBUTE_UNUSED,
|
||||
Elf_Internal_Sym *sym)
|
||||
{
|
||||
/* Local symbols do not count, but target specific ones might. */
|
||||
if (ELF_ST_BIND (sym->st_info) != STB_GLOBAL
|
||||
&& ELF_ST_BIND (sym->st_info) < STB_LOOS)
|
||||
return FALSE;
|
||||
|
||||
/* Function symbols do not count. */
|
||||
if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
|
||||
return FALSE;
|
||||
|
||||
/* If the section is undefined, then so is the symbol. */
|
||||
if (sym->st_shndx == SHN_UNDEF)
|
||||
return FALSE;
|
||||
|
||||
/* If the symbol is defined in the common section, then
|
||||
it is a common definition and so does not count. */
|
||||
if (sym->st_shndx == SHN_COMMON)
|
||||
return FALSE;
|
||||
|
||||
/* If the symbol is in a target specific section then we
|
||||
must rely upon the backend to tell us what it is. */
|
||||
if (sym->st_shndx >= SHN_LORESERVE && sym->st_shndx < SHN_ABS)
|
||||
/* FIXME - this function is not coded yet:
|
||||
|
||||
return _bfd_is_global_symbol_definition (abfd, sym);
|
||||
|
||||
Instead for now assume that the definition is not global,
|
||||
Even if this is wrong, at least the linker will behave
|
||||
in the same way that it used to do. */
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Search the symbol table of the archive element of the archive ABFD
|
||||
whose archive map contains a mention of SYMDEF, and determine if
|
||||
the symbol is defined in this element. */
|
||||
static bfd_boolean
|
||||
elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
|
||||
{
|
||||
Elf_Internal_Shdr * hdr;
|
||||
bfd_size_type symcount;
|
||||
bfd_size_type extsymcount;
|
||||
bfd_size_type extsymoff;
|
||||
Elf_Internal_Sym *isymbuf;
|
||||
Elf_Internal_Sym *isym;
|
||||
Elf_Internal_Sym *isymend;
|
||||
bfd_boolean result;
|
||||
|
||||
abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
|
||||
if (abfd == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (! bfd_check_format (abfd, bfd_object))
|
||||
return FALSE;
|
||||
|
||||
/* If we have already included the element containing this symbol in the
|
||||
link then we do not need to include it again. Just claim that any symbol
|
||||
it contains is not a definition, so that our caller will not decide to
|
||||
(re)include this element. */
|
||||
if (abfd->archive_pass)
|
||||
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 / get_elf_backend_data (abfd)->s->sizeof_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;
|
||||
}
|
||||
|
||||
if (extsymcount == 0)
|
||||
return FALSE;
|
||||
|
||||
/* Read in the symbol table. */
|
||||
isymbuf = bfd_elf_get_elf_syms (abfd, hdr, extsymcount, extsymoff,
|
||||
NULL, NULL, NULL);
|
||||
if (isymbuf == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Scan the symbol table looking for SYMDEF. */
|
||||
result = FALSE;
|
||||
for (isym = isymbuf, isymend = isymbuf + extsymcount; isym < isymend; isym++)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
|
||||
isym->st_name);
|
||||
if (name == NULL)
|
||||
break;
|
||||
|
||||
if (strcmp (name, symdef->name) == 0)
|
||||
{
|
||||
result = is_global_data_symbol_definition (abfd, isym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free (isymbuf);
|
||||
|
||||
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
|
||||
problem which arises on UnixWare. The UnixWare libc.so is an
|
||||
archive which includes an entry libc.so.1 which defines a bunch of
|
||||
symbols. The libc.so archive also includes a number of other
|
||||
object files, which also define symbols, some of which are the same
|
||||
as those defined in libc.so.1. Correct linking requires that we
|
||||
consider each object file in turn, and include it if it defines any
|
||||
symbols we need. _bfd_generic_link_add_archive_symbols does not do
|
||||
this; it looks through the list of undefined symbols, and includes
|
||||
any object file which defines them. When this algorithm is used on
|
||||
UnixWare, it winds up pulling in libc.so.1 early and defining a
|
||||
bunch of symbols. This means that some of the other objects in the
|
||||
archive are not included in the link, which is incorrect since they
|
||||
precede libc.so.1 in the archive.
|
||||
|
||||
Fortunately, ELF archive handling is simpler than that done by
|
||||
_bfd_generic_link_add_archive_symbols, which has to allow for a.out
|
||||
oddities. In ELF, if we find a symbol in the archive map, and the
|
||||
symbol is currently undefined, we know that we must pull in that
|
||||
object file.
|
||||
|
||||
Unfortunately, we do have to make multiple passes over the symbol
|
||||
table until nothing further is resolved. */
|
||||
|
||||
bfd_boolean
|
||||
_bfd_elf_link_add_archive_symbols (bfd *abfd,
|
||||
struct bfd_link_info *info)
|
||||
{
|
||||
symindex c;
|
||||
bfd_boolean *defined = NULL;
|
||||
bfd_boolean *included = NULL;
|
||||
carsym *symdefs;
|
||||
bfd_boolean loop;
|
||||
bfd_size_type amt;
|
||||
|
||||
if (! bfd_has_map (abfd))
|
||||
{
|
||||
/* An empty archive is a special case. */
|
||||
if (bfd_openr_next_archived_file (abfd, NULL) == NULL)
|
||||
return TRUE;
|
||||
bfd_set_error (bfd_error_no_armap);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Keep track of all symbols we know to be already defined, and all
|
||||
files we know to be already included. This is to speed up the
|
||||
second and subsequent passes. */
|
||||
c = bfd_ardata (abfd)->symdef_count;
|
||||
if (c == 0)
|
||||
return TRUE;
|
||||
amt = c;
|
||||
amt *= sizeof (bfd_boolean);
|
||||
defined = bfd_zmalloc (amt);
|
||||
included = bfd_zmalloc (amt);
|
||||
if (defined == NULL || included == NULL)
|
||||
goto error_return;
|
||||
|
||||
symdefs = bfd_ardata (abfd)->symdefs;
|
||||
|
||||
do
|
||||
{
|
||||
file_ptr last;
|
||||
symindex i;
|
||||
carsym *symdef;
|
||||
carsym *symdefend;
|
||||
|
||||
loop = FALSE;
|
||||
last = -1;
|
||||
|
||||
symdef = symdefs;
|
||||
symdefend = symdef + c;
|
||||
for (i = 0; symdef < symdefend; symdef++, i++)
|
||||
{
|
||||
struct elf_link_hash_entry *h;
|
||||
bfd *element;
|
||||
struct bfd_link_hash_entry *undefs_tail;
|
||||
symindex mark;
|
||||
|
||||
if (defined[i] || included[i])
|
||||
continue;
|
||||
if (symdef->file_offset == last)
|
||||
{
|
||||
included[i] = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
h = elf_link_hash_lookup (elf_hash_table (info), symdef->name,
|
||||
FALSE, FALSE, FALSE);
|
||||
|
||||
if (h == NULL)
|
||||
{
|
||||
char *p, *copy;
|
||||
size_t len, first;
|
||||
|
||||
/* If this is a default version (the name contains @@),
|
||||
look up the symbol again with only one `@' as well
|
||||
as without the version. The effect is that references
|
||||
to the symbol with and without the version will be
|
||||
matched by the default symbol in the archive. */
|
||||
|
||||
p = strchr (symdef->name, ELF_VER_CHR);
|
||||
if (p == NULL || p[1] != ELF_VER_CHR)
|
||||
continue;
|
||||
|
||||
/* First check with only one `@'. */
|
||||
len = strlen (symdef->name);
|
||||
copy = bfd_alloc (abfd, len);
|
||||
if (copy == NULL)
|
||||
goto error_return;
|
||||
first = p - symdef->name + 1;
|
||||
memcpy (copy, symdef->name, first);
|
||||
memcpy (copy + first, symdef->name + first + 1, len - first);
|
||||
|
||||
h = elf_link_hash_lookup (elf_hash_table (info), copy,
|
||||
FALSE, FALSE, FALSE);
|
||||
|
||||
if (h == NULL)
|
||||
{
|
||||
/* We also need to check references to the symbol
|
||||
without the version. */
|
||||
|
||||
copy[first - 1] = '\0';
|
||||
h = elf_link_hash_lookup (elf_hash_table (info),
|
||||
copy, FALSE, FALSE, FALSE);
|
||||
}
|
||||
|
||||
bfd_release (abfd, copy);
|
||||
}
|
||||
|
||||
if (h == NULL)
|
||||
continue;
|
||||
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We need to include this archive member. */
|
||||
element = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
|
||||
if (element == NULL)
|
||||
goto error_return;
|
||||
|
||||
if (! bfd_check_format (element, bfd_object))
|
||||
goto error_return;
|
||||
|
||||
/* Doublecheck that we have not included this object
|
||||
already--it should be impossible, but there may be
|
||||
something wrong with the archive. */
|
||||
if (element->archive_pass != 0)
|
||||
{
|
||||
bfd_set_error (bfd_error_bad_value);
|
||||
goto error_return;
|
||||
}
|
||||
element->archive_pass = 1;
|
||||
|
||||
undefs_tail = info->hash->undefs_tail;
|
||||
|
||||
if (! (*info->callbacks->add_archive_element) (info, element,
|
||||
symdef->name))
|
||||
goto error_return;
|
||||
if (! bfd_link_add_symbols (element, info))
|
||||
goto error_return;
|
||||
|
||||
/* If there are any new undefined symbols, we need to make
|
||||
another pass through the archive in order to see whether
|
||||
they can be defined. FIXME: This isn't perfect, because
|
||||
common symbols wind up on undefs_tail and because an
|
||||
undefined symbol which is defined later on in this pass
|
||||
does not require another pass. This isn't a bug, but it
|
||||
does make the code less efficient than it could be. */
|
||||
if (undefs_tail != info->hash->undefs_tail)
|
||||
loop = TRUE;
|
||||
|
||||
/* Look backward to mark all symbols from this object file
|
||||
which we have already seen in this pass. */
|
||||
mark = i;
|
||||
do
|
||||
{
|
||||
included[mark] = TRUE;
|
||||
if (mark == 0)
|
||||
break;
|
||||
--mark;
|
||||
}
|
||||
while (symdefs[mark].file_offset == symdef->file_offset);
|
||||
|
||||
/* We mark subsequent symbols from this object file as we go
|
||||
on through the loop. */
|
||||
last = symdef->file_offset;
|
||||
}
|
||||
}
|
||||
while (loop);
|
||||
|
||||
free (defined);
|
||||
free (included);
|
||||
|
||||
return TRUE;
|
||||
|
||||
error_return:
|
||||
if (defined != NULL)
|
||||
free (defined);
|
||||
if (included != NULL)
|
||||
free (included);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user