diff options
| author | Ulrich Drepper <[email protected]> | 2006-07-05 07:34:27 +0000 |
|---|---|---|
| committer | Ulrich Drepper <[email protected]> | 2006-07-05 07:34:27 +0000 |
| commit | 4ba68f475d305b11e55c83994bad4824dad156d3 (patch) | |
| tree | 80193be4e20320e129fcef4859685132c23e6b1a /src/elflint.c | |
| parent | d5a7519085f35bf48c9b33b8698835b24caa2da1 (diff) | |
Linker work.
GNU hash support.
64-bit SysV hash support.
Diffstat (limited to 'src/elflint.c')
| -rw-r--r-- | src/elflint.c | 294 |
1 files changed, 258 insertions, 36 deletions
diff --git a/src/elflint.c b/src/elflint.c index cd335fe6..a3d7d5cb 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -951,14 +951,14 @@ section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segm static bool is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr, - bool rela) + bool is_rela) { /* If this is no executable or DSO it cannot be a .rel.dyn section. */ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) return false; /* Check the section name. Unfortunately necessary. */ - if (strcmp (section_name (ebl, idx), rela ? ".rela.dyn" : ".rel.dyn")) + if (strcmp (section_name (ebl, idx), is_rela ? ".rela.dyn" : ".rel.dyn")) return false; /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section @@ -984,14 +984,106 @@ is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr, if (dyn->d_tag == DT_RELCOUNT) { - /* Found it. One last check: does the number - specified number of relative relocations exceed - the total number of relocations? */ - if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + /* Found it. Does the type match. */ + if (is_rela) ERROR (gettext ("\ +section [%2d] '%s': DT_RELCOUNT used for this RELA section\n"), + idx, section_name (ebl, idx)); + else + { + /* Does the number specified number of relative + relocations exceed the total number of + relocations? */ + if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + ERROR (gettext ("\ section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), - idx, section_name (ebl, idx), - (int) dyn->d_un.d_val); + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + + /* Make sure the specified number of relocations are + relative. */ + Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf, + idx), NULL); + if (reldata != NULL) + for (size_t inner = 0; + inner < shdr->sh_size / shdr->sh_entsize; + ++inner) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (reldata, inner, + &rel_mem); + if (rel == NULL) + /* The problem will be reported elsewhere. */ + break; + + if (ebl_relative_reloc_p (ebl, + GELF_R_TYPE (rel->r_info))) + { + if (inner >= dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + } + else if (inner < dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"), + idx, section_name (ebl, idx), + inner, (int) dyn->d_un.d_val); + } + } + } + + if (dyn->d_tag == DT_RELACOUNT) + { + /* Found it. Does the type match. */ + if (!is_rela) + ERROR (gettext ("\ +section [%2d] '%s': DT_RELACOUNT used for this REL section\n"), + idx, section_name (ebl, idx)); + else + { + /* Does the number specified number of relative + relocations exceed the total number of + relocations? */ + if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + + /* Make sure the specified number of relocations are + relative. */ + Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf, + idx), NULL); + if (reldata != NULL) + for (size_t inner = 0; + inner < shdr->sh_size / shdr->sh_entsize; + ++inner) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (reldata, inner, + &rela_mem); + if (rela == NULL) + /* The problem will be reported elsewhere. */ + break; + + if (ebl_relative_reloc_p (ebl, + GELF_R_TYPE (rela->r_info))) + { + if (inner >= dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"), + idx, section_name (ebl, idx), + (int) dyn->d_un.d_val); + } + else if (inner < dyn->d_un.d_val) + ERROR (gettext ("\ +section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"), + idx, section_name (ebl, idx), + inner, (int) dyn->d_un.d_val); + } + } } } @@ -1718,7 +1810,150 @@ extended section index is %" PRIu32 " but symbol index is not XINDEX\n"), static void -check_hash (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) +check_sysv_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 nchain = ((Elf32_Word *) data->d_buf)[1]; + + if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), + idx, section_name (ebl, idx), (long int) shdr->sh_size, + (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); + + size_t maxidx = nchain; + + if (symshdr != NULL) + { + size_t symsize = symshdr->sh_size / symshdr->sh_entsize; + + if (nchain > symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("section [%2d] '%s': chain array too large\n"), + idx, section_name (ebl, idx)); + + maxidx = symsize; + } + + size_t cnt; + for (cnt = 2; cnt < 2 + nbucket; ++cnt) + if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash bucket reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2); + + for (; cnt < 2 + nbucket + nchain; ++cnt) + if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash chain reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2 - nbucket); +} + + +static void +check_sysv_hash64 (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx, + GElf_Shdr *symshdr) +{ + Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0]; + Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1]; + + if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) + ERROR (gettext ("\ +section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), + idx, section_name (ebl, idx), (long int) shdr->sh_size, + (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); + + size_t maxidx = nchain; + + if (symshdr != NULL) + { + size_t symsize = symshdr->sh_size / symshdr->sh_entsize; + + if (nchain > symshdr->sh_size / symshdr->sh_entsize) + ERROR (gettext ("section [%2d] '%s': chain array too large\n"), + idx, section_name (ebl, idx)); + + maxidx = symsize; + } + + size_t cnt; + for (cnt = 2; cnt < 2 + nbucket; ++cnt) + if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash bucket reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2); + + for (; cnt < 2 + nbucket + nchain; ++cnt) + if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash chain reference %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2 - nbucket); +} + + +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 symbias = ((Elf32_Word *) data->d_buf)[1]; + + if (shdr->sh_size < (2 + nbucket) * shdr->sh_entsize) + { + 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)); + return; + } + + size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (2 + nbucket); + + if (symshdr != NULL) + maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize); + + /* We need the symbol section data. */ + Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL); + + size_t cnt; + for (cnt = 2; cnt < 2 + nbucket; ++cnt) + { + Elf32_Word chainidx = ((Elf32_Word *) data->d_buf)[cnt]; + + if (chainidx == ~0u) + /* Nothing in here. */ + continue; + + while (chainidx < maxidx + && ((((Elf32_Word *) data->d_buf)[2 + nbucket + chainidx] & 1) + == 0)) + ++chainidx; + + if (chainidx >= maxidx) + ERROR (gettext ("\ +section [%2d] '%s': hash chain for bucket %zu out of bounds\n"), + idx, section_name (ebl, idx), cnt - 2); + else if (symshdr != NULL + && symbias + chainidx > 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) + ERROR (gettext ("\ +section [%2d] '%s': symbol reference in chain for bucket %zu is undefined\n"), + idx, section_name (ebl, idx), cnt - 2); + } + } +} + + +static void +check_hash (int tag, Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { if (ehdr->e_type == ET_REL) { @@ -1761,35 +1996,21 @@ section [%2d] '%s': hash table has not even room for nbucket and nchain\n"), return; } - Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; - Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1]; - - if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize) - ERROR (gettext ("\ -section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"), - idx, section_name (ebl, idx), (long int) shdr->sh_size, - (long int) ((2 + nbucket + nchain) * shdr->sh_entsize)); - - if (symshdr != NULL) + switch (tag) { - size_t symsize = symshdr->sh_size / symshdr->sh_entsize; - size_t cnt; - - if (nchain < symshdr->sh_size / symshdr->sh_entsize) - ERROR (gettext ("section [%2d] '%s': chain array not large enough\n"), - idx, section_name (ebl, idx)); + case SHT_HASH: + if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword)) + check_sysv_hash64 (ebl, shdr, data, idx, symshdr); + else + check_sysv_hash (ebl, shdr, data, idx, symshdr); + break; - for (cnt = 2; cnt < 2 + nbucket; ++cnt) - if (((Elf32_Word *) data->d_buf)[cnt] >= symsize) - ERROR (gettext ("\ -section [%2d] '%s': hash bucket reference %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - 2); + case SHT_GNU_HASH: + check_gnu_hash (ebl, shdr, data, idx, symshdr); + break; - for (; cnt < 2 + nbucket + nchain; ++cnt) - if (((Elf32_Word *) data->d_buf)[cnt] >= symsize) - ERROR (gettext ("\ -section [%2d] '%s': hash chain reference %zu out of bounds\n"), - idx, section_name (ebl, idx), cnt - 2 - nbucket); + default: + assert (! "should not happen"); } } @@ -2945,7 +3166,8 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), break; case SHT_HASH: - check_hash (ebl, ehdr, shdr, cnt); + case SHT_GNU_HASH: + check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt); break; case SHT_NULL: |
