summaryrefslogtreecommitdiffstats
path: root/src/src/elflint.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/src/elflint.c')
-rw-r--r--src/src/elflint.c192
1 files changed, 125 insertions, 67 deletions
diff --git a/src/src/elflint.c b/src/src/elflint.c
index abca8b75..7e732531 100644
--- a/src/src/elflint.c
+++ b/src/src/elflint.c
@@ -1,28 +1,20 @@
/* Pedantic checking of ELF files compliance with gABI/psABI spec.
- Copyright (C) 2001-2012 Red Hat, Inc.
- This file is part of Red Hat elfutils.
+ Copyright (C) 2001-2014 Red Hat, Inc.
+ This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2001.
- Red Hat elfutils is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by the
- Free Software Foundation; version 2 of the License.
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
- Red Hat elfutils is distributed in the hope that it will be useful, but
+ elfutils is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with Red Hat elfutils; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
-
- Red Hat elfutils is an included package of the Open Invention Network.
- An included package of the Open Invention Network is a package for which
- Open Invention Network licensees cross-license their patents. No patent
- license is granted, either expressly or impliedly, by designation as an
- included package. Should you wish to participate in the Open Invention
- Network licensing program, please visit www.openinventionnetwork.com
- <http://www.openinventionnetwork.com>. */
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -131,6 +123,10 @@ static uint32_t shstrndx;
/* Array to count references in section groups. */
static int *scnref;
+/* Numbers of sections and program headers. */
+static unsigned int shnum;
+static unsigned int phnum;
+
int
main (int argc, char *argv[])
@@ -319,10 +315,19 @@ section_name (Ebl *ebl, int idx)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr;
+ const char *ret;
+
+ if ((unsigned int) idx > shnum)
+ return "<invalid>";
shdr = gelf_getshdr (elf_getscn (ebl->elf, idx), &shdr_mem);
+ if (shdr == NULL)
+ return "<invalid>";
- return elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+ ret = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+ if (ret == NULL)
+ return "<invalid>";
+ return ret;
}
@@ -338,17 +343,13 @@ static const int valid_e_machine[] =
EM_68HC16, EM_68HC11, EM_68HC08, EM_68HC05, EM_SVX, EM_ST19, EM_VAX,
EM_CRIS, EM_JAVELIN, EM_FIREPATH, EM_ZSP, EM_MMIX, EM_HUANY, EM_PRISM,
EM_AVR, EM_FR30, EM_D10V, EM_D30V, EM_V850, EM_M32R, EM_MN10300,
- EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA
+ EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA,
+ EM_TILEGX, EM_TILEPRO, EM_AARCH64
};
#define nvalid_e_machine \
(sizeof (valid_e_machine) / sizeof (valid_e_machine[0]))
-/* Numbers of sections and program headers. */
-static unsigned int shnum;
-static unsigned int phnum;
-
-
static void
check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size)
{
@@ -632,7 +633,8 @@ section [%2d] '%s': symbol table cannot have more than one extended index sectio
}
}
- if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT))
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT);
+ if (shdr->sh_entsize != sh_entsize)
ERROR (gettext ("\
section [%2u] '%s': entry size is does not match ElfXX_Sym\n"),
idx, section_name (ebl, idx));
@@ -670,7 +672,7 @@ section [%2d] '%s': XINDEX for zeroth entry not zero\n"),
xndxscnidx, section_name (ebl, xndxscnidx));
}
- for (size_t cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ for (size_t cnt = 1; cnt < shdr->sh_size / sh_entsize; ++cnt)
{
sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx);
if (sym == NULL)
@@ -690,7 +692,8 @@ section [%2d] '%s': symbol %zu: invalid name value\n"),
else
{
name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name);
- assert (name != NULL);
+ assert (name != NULL
+ || strshdr->sh_type != SHT_STRTAB);
}
if (sym->st_shndx == SHN_XINDEX)
@@ -765,29 +768,44 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"),
{
GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0
: destshdr->sh_addr);
+ GElf_Addr st_value;
+ if (GELF_ST_TYPE (sym->st_info) == STT_FUNC
+ || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
+ st_value = sym->st_value & ebl_func_addr_mask (ebl);
+ else
+ st_value = sym->st_value;
if (GELF_ST_TYPE (sym->st_info) != STT_TLS)
{
if (! ebl_check_special_symbol (ebl, ehdr, sym, name,
destshdr))
{
- if (sym->st_value - sh_addr > destshdr->sh_size)
+ if (st_value - sh_addr > destshdr->sh_size)
{
/* GNU ld has severe bugs. When it decides to remove
empty sections it leaves symbols referencing them
- behind. These are symbols in .symtab. */
+ behind. These are symbols in .symtab or .dynsym
+ and for the named symbols have zero size. See
+ sourceware PR13621. */
if (!gnuld
- || strcmp (section_name (ebl, idx), ".symtab")
+ || (strcmp (section_name (ebl, idx), ".symtab")
+ && strcmp (section_name (ebl, idx),
+ ".dynsym"))
+ || sym->st_size != 0
|| (strcmp (name, "__preinit_array_start") != 0
&& strcmp (name, "__preinit_array_end") != 0
&& strcmp (name, "__init_array_start") != 0
&& strcmp (name, "__init_array_end") != 0
&& strcmp (name, "__fini_array_start") != 0
- && strcmp (name, "__fini_array_end") != 0))
+ && strcmp (name, "__fini_array_end") != 0
+ && strcmp (name, "__bss_start") != 0
+ && strcmp (name, "__bss_start__") != 0
+ && strcmp (name, "__TMC_END__") != 0
+ && strcmp (name, ".TOC.") != 0))
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: st_value out of bounds\n"),
idx, section_name (ebl, idx), cnt);
}
- else if ((sym->st_value - sh_addr
+ else if ((st_value - sh_addr
+ sym->st_size) > destshdr->sh_size)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"),
@@ -807,12 +825,12 @@ section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_
{
/* For object files the symbol value must fall
into the section. */
- if (sym->st_value > destshdr->sh_size)
+ if (st_value > destshdr->sh_size)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"),
idx, section_name (ebl, idx), cnt,
(int) xndx, section_name (ebl, xndx));
- else if (sym->st_value + sym->st_size
+ else if (st_value + sym->st_size
> destshdr->sh_size)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"),
@@ -841,20 +859,20 @@ section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"),
}
else
{
- if (sym->st_value
+ if (st_value
< destshdr->sh_offset - phdr->p_offset)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: st_value short of referenced section [%2d] '%s'\n"),
idx, section_name (ebl, idx), cnt,
(int) xndx, section_name (ebl, xndx));
- else if (sym->st_value
+ else if (st_value
> (destshdr->sh_offset - phdr->p_offset
+ destshdr->sh_size))
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"),
idx, section_name (ebl, idx), cnt,
(int) xndx, section_name (ebl, xndx));
- else if (sym->st_value + sym->st_size
+ else if (st_value + sym->st_size
> (destshdr->sh_offset - phdr->p_offset
+ destshdr->sh_size))
ERROR (gettext ("\
@@ -1039,9 +1057,11 @@ is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
{
GElf_Shdr rcshdr_mem;
const GElf_Shdr *rcshdr = gelf_getshdr (scn, &rcshdr_mem);
- assert (rcshdr != NULL);
- if (rcshdr->sh_type == SHT_DYNAMIC)
+ if (rcshdr == NULL)
+ break;
+
+ if (rcshdr->sh_type == SHT_DYNAMIC && rcshdr->sh_entsize)
{
/* Found the dynamic section. Look through it. */
Elf_Data *d = elf_getdata (scn, NULL);
@@ -1051,7 +1071,9 @@ is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
{
GElf_Dyn dyn_mem;
GElf_Dyn *dyn = gelf_getdyn (d, cnt, &dyn_mem);
- assert (dyn != NULL);
+
+ if (dyn == NULL)
+ break;
if (dyn->d_tag == DT_RELCOUNT)
{
@@ -1065,7 +1087,9 @@ section [%2d] '%s': DT_RELCOUNT used for this RELA section\n"),
/* 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)
+ if (shdr->sh_entsize != 0
+ && 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),
@@ -1199,8 +1223,7 @@ check_reloc_shdr (Ebl *ebl, const GElf_Ehdr *ehdr, const GElf_Shdr *shdr,
destshdr_memp);
if (*destshdrp != NULL)
{
- if((*destshdrp)->sh_type != SHT_PROGBITS
- && (*destshdrp)->sh_type != SHT_NOBITS)
+ if(! ebl_check_reloc_target_type (ebl, (*destshdrp)->sh_type))
{
reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, true);
if (!reldyn)
@@ -1225,7 +1248,8 @@ section [%2d] '%s': no relocations for merge-able sections possible\n"),
}
}
- if (shdr->sh_entsize != gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT))
+ size_t sh_entsize = gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT);
+ if (shdr->sh_entsize != sh_entsize)
ERROR (gettext (reltype == ELF_T_RELA ? "\
section [%2d] '%s': section entry size does not match ElfXX_Rela\n" : "\
section [%2d] '%s': section entry size does not match ElfXX_Rel\n"),
@@ -1448,7 +1472,8 @@ check_rela (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
Elf_Data *symdata = elf_getdata (symscn, NULL);
enum load_state state = state_undecided;
- for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT);
+ for (size_t cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
{
GElf_Rela rela_mem;
GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
@@ -1498,7 +1523,8 @@ check_rel (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
Elf_Data *symdata = elf_getdata (symscn, NULL);
enum load_state state = state_undecided;
- for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT);
+ for (size_t cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
{
GElf_Rel rel_mem;
GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
@@ -1597,7 +1623,8 @@ section [%2d] '%s': referenced as string table for section [%2d] '%s' but type i
shdr->sh_link, section_name (ebl, shdr->sh_link),
idx, section_name (ebl, idx));
- if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT))
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT);
+ if (shdr->sh_entsize != sh_entsize)
ERROR (gettext ("\
section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"),
idx, section_name (ebl, idx));
@@ -1607,7 +1634,7 @@ section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"),
idx, section_name (ebl, idx));
bool non_null_warned = false;
- for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ for (cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
{
GElf_Dyn dyn_mem;
GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dyn_mem);
@@ -1879,6 +1906,8 @@ section [%2d] '%s': entry size does not match Elf32_Word\n"),
idx, section_name (ebl, idx));
if (symshdr != NULL
+ && shdr->sh_entsize
+ && symshdr->sh_entsize
&& (shdr->sh_size / shdr->sh_entsize
< symshdr->sh_size / symshdr->sh_entsize))
ERROR (gettext ("\
@@ -1905,6 +1934,12 @@ section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same
}
Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
if (*((Elf32_Word *) data->d_buf) != 0)
ERROR (gettext ("symbol 0 should have zero extended section index\n"));
@@ -1947,7 +1982,7 @@ section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
size_t maxidx = nchain;
- if (symshdr != NULL)
+ if (symshdr != NULL && symshdr->sh_entsize != 0)
{
size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
@@ -1958,18 +1993,28 @@ section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
maxidx = symsize;
}
+ Elf32_Word *buf = (Elf32_Word *) data->d_buf;
+ Elf32_Word *end = (Elf32_Word *) ((char *) data->d_buf + shdr->sh_size);
size_t cnt;
for (cnt = 2; cnt < 2 + nbucket; ++cnt)
- if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (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)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (buf[cnt] >= maxidx)
ERROR (gettext ("\
section [%2d] '%s': hash chain reference %zu out of bounds\n"),
idx, section_name (ebl, idx), cnt - 2 - nbucket);
+ }
}
@@ -1999,18 +2044,28 @@ section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
maxidx = symsize;
}
+ Elf64_Xword *buf = (Elf64_Xword *) data->d_buf;
+ Elf64_Xword *end = (Elf64_Xword *) ((char *) data->d_buf + shdr->sh_size);
size_t cnt;
for (cnt = 2; cnt < 2 + nbucket; ++cnt)
- if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (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)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (buf[cnt] >= maxidx)
ERROR (gettext ("\
section [%2d] '%s': hash chain reference %" PRIu64 " out of bounds\n"),
- idx, section_name (ebl, idx), (uint64_t) (cnt - 2 - nbucket));
+ idx, section_name (ebl, idx), (uint64_t) cnt - 2 - nbucket);
+ }
}
@@ -2035,7 +2090,7 @@ section [%2d] '%s': bitmask size not power of 2: %u\n"),
if (shdr->sh_size < (4 + bitmask_words + nbuckets) * sizeof (Elf32_Word))
{
ERROR (gettext ("\
-section [%2d] '%s': hash table section is too small (is %ld, expected at least%ld)\n"),
+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) ((4 + bitmask_words + nbuckets) * sizeof (Elf32_Word)));
return;
@@ -2707,8 +2762,9 @@ section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic s
/* The number of elements in the version symbol table must be the
same as the number of symbols. */
- if (shdr->sh_size / shdr->sh_entsize
- != symshdr->sh_size / symshdr->sh_entsize)
+ if (shdr->sh_entsize && symshdr->sh_entsize
+ && (shdr->sh_size / shdr->sh_entsize
+ != symshdr->sh_size / symshdr->sh_entsize))
ERROR (gettext ("\
section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\n"),
idx, section_name (ebl, idx),
@@ -3210,7 +3266,7 @@ section [%2d] '%s': offset %zu: unterminated vendor name string\n"),
unsigned const char *chunk = q;
unsigned int subsection_tag;
- get_uleb128 (subsection_tag, q);
+ get_uleb128 (subsection_tag, q, p);
if (q >= p)
{
@@ -3265,13 +3321,13 @@ section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"),
while (chunk < q)
{
unsigned int tag;
- get_uleb128 (tag, chunk);
+ get_uleb128 (tag, chunk, q);
uint64_t value = 0;
const unsigned char *r = chunk;
if (tag == 32 || (tag & 1) == 0)
{
- get_uleb128 (value, r);
+ get_uleb128 (value, r, q);
if (r > q)
{
ERROR (gettext ("\
@@ -3357,8 +3413,8 @@ static const struct
{ ".note", 6, SHT_NOTE, atleast, 0, SHF_ALLOC },
{ ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests
{ ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
- { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC }, // XXX more tests
- { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests
+ { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests
{ ".rodata", 8, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS },
{ ".rodata1", 9, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS },
{ ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 },
@@ -3476,7 +3532,7 @@ cannot get section header for section [%2zu] '%s': %s\n"),
GElf_Word good_type = special_sections[s].type;
if (IS_KNOWN_SPECIAL (s, ".plt", false)
- && ebl_bss_plt_p (ebl, ehdr))
+ && ebl_bss_plt_p (ebl))
good_type = SHT_NOBITS;
/* In a debuginfo file, any normal section can be SHT_NOBITS.
@@ -3716,8 +3772,10 @@ section [%2zu] '%s' is both executable and writable\n"),
|| (phdr->p_type == PT_TLS
&& (shdr->sh_flags & SHF_TLS) != 0))
&& phdr->p_offset <= shdr->sh_offset
- && (phdr->p_offset + phdr->p_filesz > shdr->sh_offset
- || (phdr->p_offset + phdr->p_memsz > shdr->sh_offset
+ && ((shdr->sh_offset - phdr->p_offset <= phdr->p_filesz
+ && (shdr->sh_offset - phdr->p_offset < phdr->p_filesz
+ || shdr->sh_size == 0))
+ || (shdr->sh_offset - phdr->p_offset < phdr->p_memsz
&& shdr->sh_type == SHT_NOBITS)))
{
/* Found the segment. */