summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlrich Drepper <[email protected]>2006-07-06 23:58:40 +0000
committerUlrich Drepper <[email protected]>2006-07-06 23:58:40 +0000
commit1b0714c81de473ad3a5ca88bd1c593d93a6ebcdd (patch)
tree152b48dddb7a07fbe0a534242457277e60ab2199 /src
parent7df97c777c3c8a688774882a1dd7d1701a127560 (diff)
Updated support for new hash table format.
Fix handling of discarded COMDAT symbols in ld.
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog8
-rw-r--r--src/elflint.c106
-rw-r--r--src/i386_ld.c4
-rw-r--r--src/ldgeneric.c55
-rw-r--r--src/readelf.c7
5 files changed, 129 insertions, 51 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index b75a7662..1c153b00 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,11 @@
+2006-07-06 Ulrich Drepper <[email protected]>
+
+ * elflint.c: Adjust for latest new hash table format.
+ * readelf.c: Likewise.
+
+ * elflint.c (check_versym): Ignore hidden bit when comparing version
+ numbers.
+
2006-07-05 Ulrich Drepper <[email protected]>
* ldgeneric.c (ld_generic_create_outfile): Correctly recognize
diff --git a/src/elflint.c b/src/elflint.c
index a3d7d5cb..a679acc4 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -1895,19 +1895,19 @@ static void
check_gnu_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
GElf_Shdr *symshdr)
{
- Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word nbuckets = ((Elf32_Word *) data->d_buf)[0];
Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
- if (shdr->sh_size < (2 + nbucket) * shdr->sh_entsize)
+ if (shdr->sh_size < (2 + 2 * nbuckets) * sizeof (Elf32_Word))
{
ERROR (gettext ("\
section [%2d] '%s': hash table section is too small (is %ld, expected at least%ld)\n"),
idx, section_name (ebl, idx), (long int) shdr->sh_size,
- (long int) ((2 + nbucket) * shdr->sh_entsize));
+ (long int) ((2 + 2 * nbuckets) * sizeof (Elf32_Word)));
return;
}
- size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (2 + nbucket);
+ size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (2 + 2 * nbuckets);
if (symshdr != NULL)
maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize);
@@ -1916,38 +1916,82 @@ section [%2d] '%s': hash table section is too small (is %ld, expected at least%l
Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL);
size_t cnt;
- for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+ for (cnt = 2; cnt < 2 + 2 * nbuckets; cnt += 2)
{
- Elf32_Word chainidx = ((Elf32_Word *) data->d_buf)[cnt];
+ Elf32_Word bitset = ((Elf32_Word *) data->d_buf)[cnt];
+ Elf32_Word symidx = ((Elf32_Word *) data->d_buf)[cnt + 1];
- if (chainidx == ~0u)
- /* Nothing in here. */
- continue;
+ if (symidx == 0)
+ {
+ /* Nothing in here. No bit in the bitset should be set either. */
+ if (bitset != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu empty but bitset is not\n"),
+ idx, section_name (ebl, idx), cnt / 2 - 1);
+
+ continue;
+ }
+
+ if (symidx < symbias)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu lower than symbol index bias\n"),
+ idx, section_name (ebl, idx), cnt / 2 - 1);
+ continue;
+ }
+
+ Elf32_Word collected_bitset = 0;
+ while (symidx - symbias < maxidx)
+ {
+ Elf32_Word chainhash = ((Elf32_Word *) data->d_buf)[2 + 2 * nbuckets
+ + symidx
+ - symbias];
+
+ if (symdata != NULL)
+ {
+ /* Check that the referenced symbol is not undefined. */
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, symidx, &sym_mem);
+ if (sym != NULL && sym->st_shndx == SHN_UNDEF)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"),
+ idx, section_name (ebl, idx), symidx, cnt / 2 - 1);
+
+ const char *symname = elf_strptr (ebl->elf, symshdr->sh_link,
+ sym->st_name);
+ if (symname != NULL)
+ {
+ Elf32_Word hval = elf_gnu_hash (symname);
+ if ((hval & ~1u) != (chainhash & ~1u))
+ ERROR (gettext ("\
+section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"),
+ idx, section_name (ebl, idx), symidx, cnt / 2 - 1);
+ }
+ }
+
+ collected_bitset |= 1 << ((chainhash >> 5) & 31);
- while (chainidx < maxidx
- && ((((Elf32_Word *) data->d_buf)[2 + nbucket + chainidx] & 1)
- == 0))
- ++chainidx;
+ if ((chainhash & 1) != 0)
+ break;
+
+ ++symidx;
+ }
- if (chainidx >= maxidx)
+ if (symidx - symbias >= maxidx)
ERROR (gettext ("\
section [%2d] '%s': hash chain for bucket %zu out of bounds\n"),
- idx, section_name (ebl, idx), cnt - 2);
+ idx, section_name (ebl, idx), cnt / 2 - 1);
else if (symshdr != NULL
- && symbias + chainidx > symshdr->sh_size / symshdr->sh_entsize)
+ && symidx > symshdr->sh_size / symshdr->sh_entsize)
ERROR (gettext ("\
section [%2d] '%s': symbol reference in chain for bucket %zu out of bounds\n"),
- idx, section_name (ebl, idx), cnt - 2);
- else if (symdata != NULL)
- {
- /* Check that the referenced symbol is not undefined. */
- GElf_Sym sym_mem;
- GElf_Sym *sym = gelf_getsym (symdata, symbias + cnt - 2, &sym_mem);
- if (sym != NULL && sym->st_shndx == SHN_UNDEF)
+ idx, section_name (ebl, idx), cnt / 2 - 1);
+
+ if (bitset != collected_bitset)
ERROR (gettext ("\
-section [%2d] '%s': symbol reference in chain for bucket %zu is undefined\n"),
- idx, section_name (ebl, idx), cnt - 2);
- }
+section [%2d] '%s': bitset for bucket %zu does not match chain entries: computed %#x, reported %#x\n"),
+ idx, section_name (ebl, idx), cnt / 2 - 1,
+ collected_bitset, bitset);
}
}
@@ -1979,9 +2023,11 @@ section [%2d] '%s': relocatable files cannot have hash tables\n"),
section [%2d] '%s': hash table not for dynamic symbol table\n"),
idx, section_name (ebl, idx));
- if (shdr->sh_entsize != sizeof (Elf32_Word))
+ if (shdr->sh_entsize != (tag == SHT_GNU_HASH
+ ? sizeof (Elf32_Word)
+ : (size_t) ebl_sysvhash_entrysize (ebl)))
ERROR (gettext ("\
-section [%2d] '%s': entry size does not match Elf32_Word\n"),
+section [%2d] '%s': hash table entry size incorrect\n"),
idx, section_name (ebl, idx));
if ((shdr->sh_flags & SHF_ALLOC) == 0)
@@ -1991,7 +2037,7 @@ section [%2d] '%s': entry size does not match Elf32_Word\n"),
if (shdr->sh_size < 2 * shdr->sh_entsize)
{
ERROR (gettext ("\
-section [%2d] '%s': hash table has not even room for nbucket and nchain\n"),
+section [%2d] '%s': hash table has not even room for initial two administrative entries\n"),
idx, section_name (ebl, idx));
return;
}
@@ -2399,7 +2445,7 @@ section [%2d] '%s': symbol %d: local symbol with version\n"),
index we need for this symbol. */
struct version_namelist *runp = version_namelist;
while (runp != NULL)
- if (runp->ndx == *versym)
+ if (runp->ndx == (*versym & 0x7fff))
break;
else
runp = runp->next;
diff --git a/src/i386_ld.c b/src/i386_ld.c
index 60e45f3f..c79804cd 100644
--- a/src/i386_ld.c
+++ b/src/i386_ld.c
@@ -149,8 +149,8 @@ elf_i386_relocate_section (struct ld_state *statep __attribute__ ((unused)),
assert (xndx < SHN_LORESERVE || xndx > SHN_HIRESERVE);
/* We fortunately don't have to do much. The relocations
- mostly get only updates of the offset. Only is a
- relocation referred to a section do we have to do
+ mostly get only updates of the offset. Only for a
+ relocation referring to a section do we have to do
something. In this case the reference to the sections
has no direct equivalent since the part the input section
contributes need not start at the same offset as in the
diff --git a/src/ldgeneric.c b/src/ldgeneric.c
index 6913d67e..d282a1d8 100644
--- a/src/ldgeneric.c
+++ b/src/ldgeneric.c
@@ -1436,9 +1436,9 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype)
_GLOBAL_OFFSET_TABLE_, _DYNAMIC. */
// XXX This loop is hot and the following tests hardly ever match.
// XXX Maybe move the tests somewhere they are executed less often.
- if (((unlikely (hval == 165832675)
+ if (((unlikely (hval == 165832675ul)
&& strcmp (search.name, "_DYNAMIC") == 0)
- || (unlikely (hval == 102264335)
+ || (unlikely (hval == 102264335ul)
&& strcmp (search.name, "_GLOBAL_OFFSET_TABLE_") == 0))
&& sym->st_shndx != SHN_UNDEF
/* If somebody defines such a variable in a relocatable we
@@ -1451,7 +1451,7 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype)
struct symbol *newp;
if (likely (oldp == NULL))
{
- /* No symbol of this name know. Add it. */
+ /* No symbol of this name known. Add it. */
newp = (struct symbol *) obstack_alloc (&ld_state.smem,
sizeof (*newp));
newp->name = search.name;
@@ -1467,6 +1467,8 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype)
newp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK;
newp->added = 0;
newp->merged = 0;
+ newp->local = 0;
+ newp->hidden = 0;
newp->need_copy = 0;
newp->on_dsolist = 0;
newp->in_dso = secttype == SHT_DYNSYM;
@@ -4767,6 +4769,7 @@ section index too large in dynamic symbol table"));
/* Once we know the name this field will get the correct
offset. For now set it to zero which means no name
associated. */
+ GElf_Word st_name = sym->st_name;
sym->st_name = 0;
/* If we had to merge sections we have a completely new
@@ -4783,24 +4786,44 @@ section index too large in dynamic symbol table"));
Find the symbol if this has not happened yet. We do
not need the information for local symbols. */
if (defp == NULL && cnt >= file->nlocalsymbols)
- defp = file->symref[cnt];
-
- /* Ignore symbols in discarded COMDAT group sections. */
- if (defp != NULL || cnt < file->nlocalsymbols)
{
- /* Store the reference to the symbol record. The
- sorting code will have to keep this array in the
- correct order, too. */
- ndxtosym[nsym] = defp;
+ defp = file->symref[cnt];
- /* One more entry finished. */
- if (cnt >= file->nlocalsymbols)
+ if (defp == NULL)
{
- assert (file->symref[cnt]->outsymidx == 0);
- file->symref[cnt]->outsymidx = nsym;
+ /* This is a symbol in a discarded COMDAT section.
+ Find the definition we actually use. */
+ // XXX The question is: do we have to do this here
+ // XXX or can we do it earlier when we discard the
+ // XXX section.
+ struct symbol search;
+ search.name = elf_strptr (file->elf, file->symstridx,
+ st_name);
+ struct symbol *realp
+ = ld_symbol_tab_find (&ld_state.symbol_tab,
+ elf_hash (search.name), &search);
+ if (realp == NULL)
+ // XXX What to do here?
+ error (EXIT_FAILURE, 0,
+ "couldn't find symbol from COMDAT section");
+
+ file->symref[cnt] = realp;
+
+ continue;
}
- file->symindirect[cnt] = nsym++;
}
+
+ /* Store the reference to the symbol record. The sorting
+ code will have to keep this array in the correct order, too. */
+ ndxtosym[nsym] = defp;
+
+ /* One more entry finished. */
+ if (cnt >= file->nlocalsymbols)
+ {
+ assert (file->symref[cnt]->outsymidx == 0);
+ file->symref[cnt]->outsymidx = nsym;
+ }
+ file->symindirect[cnt] = nsym++;
}
}
while ((file = file->next) != ld_state.relfiles->next);
diff --git a/src/readelf.c b/src/readelf.c
index b4b6a5aa..9fbc24d6 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -2480,17 +2480,18 @@ handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
}
Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2];
- Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + nbucket];
+ Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + 2 * nbucket];
uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
uint_fast32_t maxlength = 0;
uint_fast32_t nsyms = 0;
for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
- if (bucket[cnt] != ~0u)
+ if (bucket[2 * cnt + 1] != 0)
{
- Elf32_Word inner = bucket[cnt];
+ Elf32_Word inner = bucket[2 * cnt + 1] - symbias;
do
{
++nsyms;