summaryrefslogtreecommitdiffstats
path: root/src/elflint.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/elflint.c')
-rw-r--r--src/elflint.c3293
1 files changed, 2484 insertions, 809 deletions
diff --git a/src/elflint.c b/src/elflint.c
index d66cdd13..bc4219b3 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -1,16 +1,28 @@
/* Pedantic checking of ELF files compliance with gABI/psABI spec.
- Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc.
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2001.
- This program is Open Source software; you can redistribute it and/or
- modify it under the terms of the Open Software License version 1.0 as
- published by the Open Source Initiative.
+ 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.
- You should have received a copy of the Open Software License along
- with this program; if not, you may obtain a copy of the Open Software
- License version 1.0 from http://www.opensource.org/licenses/osl.php or
- by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
- 3001 King Ranch Road, Ukiah, CA 95482. */
+ Red Hat 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.
+
+ 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>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -24,7 +36,6 @@
#include <fcntl.h>
#include <gelf.h>
#include <inttypes.h>
-#include <libebl.h>
#include <libintl.h>
#include <locale.h>
#include <stdbool.h>
@@ -35,12 +46,20 @@
#include <elf-knowledge.h>
#include <system.h>
+#include "../libelf/libelfP.h"
+#include "../libelf/common.h"
+#include "../libebl/libeblP.h"
+#include "../libdw/libdwP.h"
+#include "../libdwfl/libdwflP.h"
+#include "../libdw/memory-access.h"
/* Name and version of program. */
static void print_version (FILE *stream, struct argp_state *state);
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+/* Bug report address. */
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
#define ARGP_strict 300
#define ARGP_gnuld 301
@@ -50,12 +69,13 @@ static const struct argp_option options[] =
{
{ "strict", ARGP_strict, NULL, 0,
- N_("Be extremely strict, flag level 2 features.") },
- { "quiet", 'q', NULL, 0, N_("Do not print anything if successful") },
+ N_("Be extremely strict, flag level 2 features."), 0 },
+ { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 },
+ { "debuginfo", 'd', NULL, 0, N_("Binary is a separate debuginfo file"), 0 },
{ "gnu-ld", ARGP_gnuld, NULL, 0,
N_("Binary has been created with GNU ld and is therefore known to be \
-broken in certain ways") },
- { NULL, 0, NULL, 0, NULL }
+broken in certain ways"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
};
/* Short description of program. */
@@ -68,13 +88,10 @@ static const char args_doc[] = N_("FILE...");
/* Prototype for option handler. */
static error_t parse_opt (int key, char *arg, struct argp_state *state);
-/* Function to print some extra text in the help message. */
-static char *more_help (int key, const char *text, void *input);
-
/* Data structure to communicate with argp functions. */
static struct argp argp =
{
- options, parse_opt, args_doc, doc, NULL, more_help
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
};
@@ -84,6 +101,9 @@ static void process_file (int fd, Elf *elf, const char *prefix,
bool only_one);
static void process_elf_file (Elf *elf, const char *prefix, const char *suffix,
const char *fname, size_t size, bool only_one);
+static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr,
+ GElf_Shdr *shdr, int idx);
+
/* Report an error. */
#define ERROR(str, args...) \
@@ -91,7 +111,7 @@ static void process_elf_file (Elf *elf, const char *prefix, const char *suffix,
printf (str, ##args); \
++error_count; \
} while (0)
-static int error_count;
+static unsigned int error_count;
/* True if we should perform very strict testing. */
static bool be_strict;
@@ -99,6 +119,9 @@ static bool be_strict;
/* True if no message is to be printed if the run is succesful. */
static bool be_quiet;
+/* True if binary is from strip -f, not a normal ELF file. */
+static bool is_debuginfo;
+
/* True if binary is assumed to be generated with GNU ld. */
static bool gnuld;
@@ -112,38 +135,25 @@ static int *scnref;
int
main (int argc, char *argv[])
{
- int remaining;
- bool only_one;
-
/* Set locale. */
setlocale (LC_ALL, "");
/* Initialize the message catalog. */
- textdomain (PACKAGE);
+ textdomain (PACKAGE_TARNAME);
/* Parse and process arguments. */
+ int remaining;
argp_parse (&argp, argc, argv, 0, &remaining, NULL);
- /* If no ELF file is given punt. */
- if (remaining >= argc)
- {
- argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
- program_invocation_short_name);
- exit (1);
- }
-
/* Before we start tell the ELF library which version we are using. */
elf_version (EV_CURRENT);
/* Now process all the files given at the command line. */
- only_one = remaining + 1 == argc;
+ bool only_one = remaining + 1 == argc;
do
{
- int fd;
- Elf *elf;
-
/* Open the file. */
- fd = open (argv[remaining], O_RDONLY);
+ int fd = open (argv[remaining], O_RDONLY);
if (fd == -1)
{
error (0, errno, gettext ("cannot open input file"));
@@ -151,7 +161,7 @@ main (int argc, char *argv[])
}
/* Create an `Elf' descriptor. */
- elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
if (elf == NULL)
ERROR (gettext ("cannot generate Elf descriptor: %s\n"),
elf_errmsg (-1));
@@ -189,7 +199,8 @@ main (int argc, char *argv[])
/* Handle program arguments. */
static error_t
-parse_opt (int key, char *arg, struct argp_state *state)
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
{
switch (key)
{
@@ -201,10 +212,19 @@ parse_opt (int key, char *arg, struct argp_state *state)
be_quiet = true;
break;
+ case 'd':
+ is_debuginfo = true;
+
case ARGP_gnuld:
gnuld = true;
break;
+ case ARGP_KEY_NO_ARGS:
+ fputs (gettext ("Missing file name.\n"), stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+ program_invocation_short_name);
+ exit (1);
+
default:
return ARGP_ERR_UNKNOWN;
}
@@ -212,37 +232,16 @@ parse_opt (int key, char *arg, struct argp_state *state)
}
-static char *
-more_help (int key, const char *text, void *input)
-{
- char *buf;
-
- switch (key)
- {
- case ARGP_KEY_HELP_EXTRA:
- /* We print some extra information. */
- if (asprintf (&buf, gettext ("Please report bugs to %s.\n"),
- PACKAGE_BUGREPORT) < 0)
- buf = NULL;
- return buf;
-
- default:
- break;
- }
- return (char *) text;
-}
-
-
/* Print the version information. */
static void
-print_version (FILE *stream, struct argp_state *state)
+print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
{
- fprintf (stream, "elflint (%s) %s\n", PACKAGE_NAME, VERSION);
+ fprintf (stream, "elflint (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
fprintf (stream, gettext ("\
Copyright (C) %s Red Hat, Inc.\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "2004");
+"), "2008");
fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
}
@@ -310,14 +309,14 @@ process_file (int fd, Elf *elf, const char *prefix, const char *suffix,
default:
/* We cannot do anything. */
ERROR (gettext ("\
-Not an ELF file - it has the wrong magic bytes at the start"));
+Not an ELF file - it has the wrong magic bytes at the start\n"));
break;
}
}
static const char *
-section_name (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+section_name (Ebl *ebl, int idx)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr;
@@ -340,7 +339,7 @@ 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_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA
};
#define nvalid_e_machine \
(sizeof (valid_e_machine) / sizeof (valid_e_machine[0]))
@@ -382,7 +381,7 @@ check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size)
/* We currently don't handle any OS ABIs. */
if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE)
- ERROR (gettext ("unsupported OS ABI e_ident[%d] == \"%s\"\n"),
+ ERROR (gettext ("unsupported OS ABI e_ident[%d] == '%s'\n"),
EI_OSABI,
ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf)));
@@ -441,9 +440,7 @@ executables and DSOs cannot have zero program header offset\n"));
/* Get the header of the zeroth section. The sh_size field
might contain the section number. */
GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
-
- shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
if (shdr != NULL)
{
/* The error will be reported later. */
@@ -460,17 +457,9 @@ invalid number of section header table entries\n"));
/* Get the header of the zeroth section. The sh_size field
might contain the section number. */
GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
-
- shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
- if (shdr != NULL)
- {
- /* The error will be reported later. */
- if (shdr->sh_link >= shnum)
- ERROR (gettext ("invalid section header index\n"));
- else
- shstrndx = shdr->sh_link;
- }
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL && shdr->sh_link < shnum)
+ shstrndx = shdr->sh_link;
}
else if (shstrndx >= shnum)
ERROR (gettext ("invalid section header index\n"));
@@ -522,7 +511,7 @@ invalid number of section header table entries\n"));
/* Check that there is a section group section with index < IDX which
contains section IDX and that there is exactly one. */
static void
-check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_scn_group (Ebl *ebl, int idx)
{
if (scnref[idx] == 0)
{
@@ -532,15 +521,9 @@ check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
for (cnt = idx + 1; cnt < shnum; ++cnt)
{
- Elf_Scn *scn;
+ Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
- Elf_Data *data;
- Elf32_Word *grpdata;
- size_t inner;
-
- scn = elf_getscn (ebl->elf, cnt);
- shdr = gelf_getshdr (scn, &shdr_mem);
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr == NULL)
/* We cannot get the section header so we cannot check it.
The error to get the section header will be shown
@@ -550,13 +533,14 @@ check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
if (shdr->sh_type != SHT_GROUP)
continue;
- data = elf_getdata (scn, NULL);
+ Elf_Data *data = elf_getdata (scn, NULL);
if (data == NULL || data->d_size < sizeof (Elf32_Word))
/* Cannot check the section. */
continue;
- grpdata = (Elf32_Word *) data->d_buf;
- for (inner = 1; inner < data->d_size / sizeof (Elf32_Word); ++inner)
+ Elf32_Word *grpdata = (Elf32_Word *) data->d_buf;
+ for (size_t inner = 1; inner < data->d_size / sizeof (Elf32_Word);
+ ++inner)
if (grpdata[inner] == (Elf32_Word) idx)
goto out;
}
@@ -565,74 +549,74 @@ check_scn_group (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
if (cnt == shnum)
ERROR (gettext ("\
section [%2d] '%s': section with SHF_GROUP flag set not part of a section group\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
else
ERROR (gettext ("\
section [%2d] '%s': section group [%2zu] '%s' does not preceed group member\n"),
- idx, section_name (ebl, ehdr, idx),
- cnt, section_name (ebl, ehdr, cnt));
+ idx, section_name (ebl, idx),
+ cnt, section_name (ebl, cnt));
}
}
static void
-check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
{
bool no_xndx_warned = false;
int no_pt_tls = 0;
-
- Elf_Scn *scn = elf_getscn (ebl->elf, idx);
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
- GElf_Shdr strshdr_mem;
- GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
- &strshdr_mem);
- if (shdr == NULL || strshdr == NULL)
- return;
- Elf_Data *data = elf_getdata (scn, NULL);
+ 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, ehdr, idx));
+ idx, section_name (ebl, idx));
return;
}
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &strshdr_mem);
+ if (strshdr == NULL)
+ return;
+
if (strshdr->sh_type != SHT_STRTAB)
- ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
- shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link),
- idx, section_name (ebl, ehdr, idx));
+ {
+ ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
+ shdr->sh_link, section_name (ebl, shdr->sh_link),
+ idx, section_name (ebl, idx));
+ strshdr = NULL;
+ }
/* Search for an extended section index table section. */
- size_t cnt;
- GElf_Shdr xndxshdr_mem;
- GElf_Shdr *xndxshdr = NULL;
Elf_Data *xndxdata = NULL;
Elf32_Word xndxscnidx = 0;
- for (cnt = 1; cnt < shnum; ++cnt)
+ bool found_xndx = false;
+ for (size_t cnt = 1; cnt < shnum; ++cnt)
if (cnt != (size_t) idx)
{
Elf_Scn *xndxscn = elf_getscn (ebl->elf, cnt);
- xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
- xndxdata = elf_getdata (xndxscn, NULL);
- xndxscnidx = elf_ndxscn (xndxscn);
-
- if (xndxshdr == NULL || xndxdata == NULL)
+ GElf_Shdr xndxshdr_mem;
+ GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
+ if (xndxshdr == NULL)
continue;
if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX
&& xndxshdr->sh_link == (GElf_Word) idx)
- break;
+ {
+ if (found_xndx)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol table cannot have more than one extended index section\n"),
+ idx, section_name (ebl, idx));
+
+ xndxdata = elf_getdata (xndxscn, NULL);
+ xndxscnidx = elf_ndxscn (xndxscn);
+ found_xndx = true;
+ }
}
- if (cnt == shnum)
- {
- xndxshdr = NULL;
- xndxdata = NULL;
- }
if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT))
ERROR (gettext ("\
-section [%2zu] '%s': entry size is does not match ElfXX_Sym\n"),
- cnt, section_name (ebl, ehdr, cnt));
+section [%2u] '%s': entry size is does not match ElfXX_Sym\n"),
+ idx, section_name (ebl, idx));
/* Test the zeroth entry. */
GElf_Sym sym_mem;
@@ -640,48 +624,50 @@ section [%2zu] '%s': entry size is does not match ElfXX_Sym\n"),
GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, 0, &sym_mem, &xndx);
if (sym == NULL)
ERROR (gettext ("section [%2d] '%s': cannot get symbol %d: %s\n"),
- idx, section_name (ebl, ehdr, idx), 0, elf_errmsg (-1));
+ idx, section_name (ebl, idx), 0, elf_errmsg (-1));
else
{
if (sym->st_name != 0)
ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
- idx, section_name (ebl, ehdr, idx), "st_name");
+ idx, section_name (ebl, idx), "st_name");
if (sym->st_value != 0)
ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
- idx, section_name (ebl, ehdr, idx), "st_value");
+ idx, section_name (ebl, idx), "st_value");
if (sym->st_size != 0)
ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
- idx, section_name (ebl, ehdr, idx), "st_size");
+ idx, section_name (ebl, idx), "st_size");
if (sym->st_info != 0)
ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
- idx, section_name (ebl, ehdr, idx), "st_info");
+ idx, section_name (ebl, idx), "st_info");
if (sym->st_other != 0)
ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
- idx, section_name (ebl, ehdr, idx), "st_other");
+ idx, section_name (ebl, idx), "st_other");
if (sym->st_shndx != 0)
ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
- idx, section_name (ebl, ehdr, idx), "st_shndx");
+ idx, section_name (ebl, idx), "st_shndx");
if (xndxdata != NULL && xndx != 0)
ERROR (gettext ("\
section [%2d] '%s': XINDEX for zeroth entry not zero\n"),
- xndxscnidx, section_name (ebl, ehdr, xndxscnidx));
+ xndxscnidx, section_name (ebl, xndxscnidx));
}
- for (cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ for (size_t cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
{
sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx);
if (sym == NULL)
{
ERROR (gettext ("section [%2d] '%s': cannot get symbol %zu: %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1));
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
continue;
}
const char *name = NULL;
- if (sym->st_name >= strshdr->sh_size)
+ if (strshdr == NULL)
+ name = "";
+ else if (sym->st_name >= strshdr->sh_size)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: invalid name value\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
else
{
name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name);
@@ -694,13 +680,13 @@ section [%2d] '%s': symbol %zu: invalid name value\n"),
{
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: too large section index but no extended section index section\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
no_xndx_warned = true;
}
else if (xndx < SHN_LORESERVE)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shndx (%" PRIu32 ")\n"),
- xndxscnidx, section_name (ebl, ehdr, xndxscnidx), cnt,
+ xndxscnidx, section_name (ebl, xndxscnidx), cnt,
xndx);
}
else if ((sym->st_shndx >= SHN_LORESERVE
@@ -712,18 +698,19 @@ section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shnd
/* || sym->st_shndx > SHN_HIRESERVE always false */)))
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: invalid section index\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
else
xndx = sym->st_shndx;
- if (GELF_ST_TYPE (sym->st_info) >= STT_NUM)
+ if (GELF_ST_TYPE (sym->st_info) >= STT_NUM
+ && !ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), NULL, 0))
ERROR (gettext ("section [%2d] '%s': symbol %zu: unknown type\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
if (GELF_ST_BIND (sym->st_info) >= STB_NUM)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: unknown symbol binding\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
if (xndx == SHN_COMMON)
{
@@ -731,15 +718,15 @@ section [%2d] '%s': symbol %zu: unknown symbol binding\n"),
if (ehdr->e_type != ET_REL)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: COMMON only allowed in relocatable files\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
if (cnt < shdr->sh_info)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: local COMMON symbols are nonsense\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
if (GELF_R_TYPE (sym->st_info) == STT_FUNC)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
}
else if (xndx > 0 && xndx < shnum)
{
@@ -749,26 +736,45 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"),
destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem);
if (destshdr != NULL)
{
+ GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0
+ : destshdr->sh_addr);
if (GELF_ST_TYPE (sym->st_info) != STT_TLS)
{
- if ((sym->st_value - destshdr->sh_addr) > destshdr->sh_size)
- ERROR (gettext ("\
+ if (! ebl_check_special_symbol (ebl, ehdr, sym, name,
+ destshdr))
+ {
+ if (sym->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. */
+ if (!gnuld
+ || strcmp (section_name (ebl, idx), ".symtab")
+ || (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))
+ ERROR (gettext ("\
section [%2d] '%s': symbol %zu: st_value out of bounds\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
- else if ((sym->st_value - destshdr->sh_addr + sym->st_size)
- > destshdr->sh_size)
- ERROR (gettext ("\
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if ((sym->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"),
- idx, section_name (ebl, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ }
}
else
{
if ((destshdr->sh_flags & SHF_TLS) == 0)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_TLS flag set\n"),
- idx, section_name (ebl, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
if (ehdr->e_type == ET_REL)
{
@@ -777,14 +783,14 @@ section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_
if (sym->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, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
else if (sym->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"),
- idx, section_name (ebl, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
}
else
{
@@ -804,7 +810,7 @@ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2
if (no_pt_tls++ == 0)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
}
else
{
@@ -812,22 +818,22 @@ section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"),
< 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, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
else if (sym->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, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
else if (sym->st_value + sym->st_size
> (destshdr->sh_offset - phdr->p_offset
+ destshdr->sh_size))
ERROR (gettext ("\
section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"),
- idx, section_name (ebl, ehdr, idx), cnt,
- (int) xndx, section_name (ebl, ehdr, xndx));
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
}
}
}
@@ -839,100 +845,137 @@ section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2
if (cnt >= shdr->sh_info)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: local symbol outside range described in sh_info\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
}
else
{
if (cnt < shdr->sh_info)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: non-local symbol outside range described in sh_info\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
}
if (GELF_ST_TYPE (sym->st_info) == STT_SECTION
&& GELF_ST_BIND (sym->st_info) != STB_LOCAL)
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: non-local section symbol\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
if (name != NULL)
{
if (strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
{
- /* Check that address and size match the global offset
- table. We have to locate the GOT by searching for a
- section named ".got". */
- Elf_Scn *gscn = NULL;
+ /* Check that address and size match the global offset table. */
- while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL)
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx),
+ &destshdr_mem);
+
+ if (destshdr == NULL && xndx == SHN_ABS)
+ {
+ /* In a DSO, we have to find the GOT section by name. */
+ Elf_Scn *gotscn = NULL;
+ Elf_Scn *gscn = NULL;
+ while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL)
+ {
+ destshdr = gelf_getshdr (gscn, &destshdr_mem);
+ assert (destshdr != NULL);
+ const char *sname = elf_strptr (ebl->elf,
+ ehdr->e_shstrndx,
+ destshdr->sh_name);
+ if (sname != NULL)
+ {
+ if (strcmp (sname, ".got.plt") == 0)
+ break;
+ if (strcmp (sname, ".got") == 0)
+ /* Do not stop looking.
+ There might be a .got.plt section. */
+ gotscn = gscn;
+ }
+
+ destshdr = NULL;
+ }
+
+ if (destshdr == NULL && gotscn != NULL)
+ destshdr = gelf_getshdr (gotscn, &destshdr_mem);
+ }
+
+ const char *sname = ((destshdr == NULL || xndx == SHN_UNDEF)
+ ? NULL
+ : elf_strptr (ebl->elf, ehdr->e_shstrndx,
+ destshdr->sh_name));
+ if (sname == NULL)
{
- GElf_Shdr gshdr_mem;
- GElf_Shdr *gshdr = gelf_getshdr (gscn, &gshdr_mem);
- assert (gshdr != NULL);
+ if (xndx != SHN_UNDEF || ehdr->e_type != ET_REL)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \
+bad section [%2d]\n"),
+ idx, section_name (ebl, idx), xndx);
+ }
+ else if (strcmp (sname, ".got.plt") != 0
+ && strcmp (sname, ".got") != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \
+section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), xndx, sname);
- const char *sname = elf_strptr (ebl->elf, ehdr->e_shstrndx,
- gshdr->sh_name);
- if (sname != NULL && strcmp (sname, ".got") == 0)
+ if (destshdr != NULL)
+ {
+ /* Found it. */
+ if (!ebl_check_special_symbol (ebl, ehdr, sym, name,
+ destshdr))
{
- /* Found it. */
- if (sym->st_value != gshdr->sh_addr)
- /* This test is more strict than the psABIs
- which usually allow the symbol to be in the
- middle of the .got section, allowing
- negative offsets. */
+ if (ehdr->e_type != ET_REL
+ && sym->st_value != destshdr->sh_addr)
+ /* This test is more strict than the psABIs which
+ usually allow the symbol to be in the middle of
+ the .got section, allowing negative offsets. */
ERROR (gettext ("\
-section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match .got section address %#" PRIx64 "\n"),
- idx, section_name (ebl, ehdr, idx),
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match %s section address %#" PRIx64 "\n"),
+ idx, section_name (ebl, idx),
(uint64_t) sym->st_value,
- (uint64_t) gshdr->sh_addr);
+ sname, (uint64_t) destshdr->sh_addr);
- if (sym->st_size != gshdr->sh_size)
+ if (!gnuld && sym->st_size != destshdr->sh_size)
ERROR (gettext ("\
-section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match .got section size %" PRIu64 "\n"),
- idx, section_name (ebl, ehdr, idx),
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match %s section size %" PRIu64 "\n"),
+ idx, section_name (ebl, idx),
(uint64_t) sym->st_size,
- (uint64_t) gshdr->sh_size);
-
- break;
+ sname, (uint64_t) destshdr->sh_size);
}
}
-
- if (gscn == NULL)
+ else
ERROR (gettext ("\
section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
}
else if (strcmp (name, "_DYNAMIC") == 0)
- {
- /* Check that address and size match the dynamic
- section. We locate the dynamic section via the
- program header entry. */
- int pcnt;
-
- for (pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt)
- {
- GElf_Phdr phdr_mem;
- GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
+ /* Check that address and size match the dynamic section.
+ We locate the dynamic section via the program header
+ entry. */
+ for (int pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
- if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
- {
- if (sym->st_value != phdr->p_vaddr)
- ERROR (gettext ("\
+ if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
+ {
+ if (sym->st_value != phdr->p_vaddr)
+ ERROR (gettext ("\
section [%2d] '%s': _DYNAMIC_ symbol value %#" PRIx64 " does not match dynamic segment address %#" PRIx64 "\n"),
- idx, section_name (ebl, ehdr, idx),
- (uint64_t) sym->st_value,
- (uint64_t) phdr->p_vaddr);
+ idx, section_name (ebl, idx),
+ (uint64_t) sym->st_value,
+ (uint64_t) phdr->p_vaddr);
- if (sym->st_size != phdr->p_memsz)
- ERROR (gettext ("\
+ if (!gnuld && sym->st_size != phdr->p_memsz)
+ ERROR (gettext ("\
section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segment size %" PRIu64 "\n"),
- idx, section_name (ebl, ehdr, idx),
- (uint64_t) sym->st_size,
- (uint64_t) phdr->p_memsz);
+ idx, section_name (ebl, idx),
+ (uint64_t) sym->st_size,
+ (uint64_t) phdr->p_memsz);
- break;
- }
- }
+ break;
+ }
}
}
}
@@ -940,14 +983,15 @@ section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segm
static bool
-is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela)
+is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
+ 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, ehdr, 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
@@ -963,7 +1007,7 @@ is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela)
{
/* Found the dynamic section. Look through it. */
Elf_Data *d = elf_getdata (scn, NULL);
- int cnt;
+ size_t cnt;
for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt)
{
@@ -973,14 +1017,106 @@ is_rel_dyn (Ebl *ebl, GElf_Ehdr *ehdr, int idx, GElf_Shdr *shdr, bool rela)
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);
+
+ /* 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, ehdr, 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_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);
+ }
+ }
}
}
@@ -992,273 +1128,360 @@ section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
}
-static void
-check_rela (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+struct loaded_segment
{
- Elf_Scn *scn;
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
- Elf_Data *data;
- GElf_Shdr destshdr_mem;
- GElf_Shdr *destshdr = NULL;
- size_t cnt;
- bool reldyn = false;
- bool known_broken = gnuld;
+ GElf_Addr from;
+ GElf_Addr to;
+ bool read_only;
+ struct loaded_segment *next;
+};
- scn = elf_getscn (ebl->elf, idx);
- shdr = gelf_getshdr (scn, &shdr_mem);
- if (shdr == NULL)
- return;
- data = elf_getdata (scn, NULL);
- if (data == NULL)
- {
- ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
- idx, section_name (ebl, ehdr, idx));
- return;
- }
+
+/* Check whether binary has text relocation flag set. */
+static bool textrel;
+
+/* Keep track of whether text relocation flag is needed. */
+static bool needed_textrel;
+
+
+static bool
+check_reloc_shdr (Ebl *ebl, const GElf_Ehdr *ehdr, const GElf_Shdr *shdr,
+ int idx, int reltype, GElf_Shdr **destshdrp,
+ GElf_Shdr *destshdr_memp, struct loaded_segment **loadedp)
+{
+ bool reldyn = false;
/* Check whether the link to the section we relocate is reasonable. */
if (shdr->sh_info >= shnum)
ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"),
- idx, section_name (ebl, ehdr, idx));
- else
+ idx, section_name (ebl, idx));
+ else if (shdr->sh_info != 0)
{
- destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
- &destshdr_mem);
- if (destshdr != NULL)
+ *destshdrp = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
+ destshdr_memp);
+ if (*destshdrp != NULL)
{
- if(destshdr->sh_type != SHT_PROGBITS
- && destshdr->sh_type != SHT_NOBITS)
+ if((*destshdrp)->sh_type != SHT_PROGBITS
+ && (*destshdrp)->sh_type != SHT_NOBITS)
{
reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, true);
if (!reldyn)
ERROR (gettext ("\
section [%2d] '%s': invalid destination section type\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
else
{
- /* There is no standard, but we require that .rela.dyn
+ /* There is no standard, but we require that .rel{,a}.dyn
sections have a sh_info value of zero. */
if (shdr->sh_info != 0)
ERROR (gettext ("\
section [%2d] '%s': sh_info should be zero\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
}
}
- if ((destshdr->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0)
+ if (((*destshdrp)->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0)
ERROR (gettext ("\
section [%2d] '%s': no relocations for merge-able sections possible\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
}
}
- if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT))
- ERROR (gettext ("\
-section [%2d] '%s': section entry size does not match ElfXX_Rela\n"),
- idx, section_name (ebl, ehdr, idx));
-
- Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
- GElf_Shdr symshdr_mem;
- GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
- Elf_Data *symdata = elf_getdata (symscn, NULL);
-
- for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ if (shdr->sh_entsize != gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT))
+ 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"),
+ idx, section_name (ebl, idx));
+
+ /* In preparation of checking whether relocations are text
+ relocations or not we need to determine whether the file is
+ flagged to have text relocation and we need to determine a) what
+ the loaded segments are and b) which are read-only. This will
+ also allow us to determine whether the same reloc section is
+ modifying loaded and not loaded segments. */
+ for (int i = 0; i < ehdr->e_phnum; ++i)
{
- GElf_Rela rela_mem;
- GElf_Rela *rela;
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem);
+ if (phdr == NULL)
+ continue;
- rela = gelf_getrela (data, cnt, &rela_mem);
- if (rela == NULL)
+ if (phdr->p_type == PT_LOAD)
{
- ERROR (gettext ("\
-section [%2d] '%s': cannot get relocation %zu: %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1));
- continue;
+ struct loaded_segment *newp = xmalloc (sizeof (*newp));
+ newp->from = phdr->p_vaddr;
+ newp->to = phdr->p_vaddr + phdr->p_memsz;
+ newp->read_only = (phdr->p_flags & PF_W) == 0;
+ newp->next = *loadedp;
+ *loadedp = newp;
}
+ else if (phdr->p_type == PT_DYNAMIC)
+ {
+ Elf_Scn *dynscn = gelf_offscn (ebl->elf, phdr->p_offset);
+ GElf_Shdr dynshdr_mem;
+ GElf_Shdr *dynshdr = gelf_getshdr (dynscn, &dynshdr_mem);
+ Elf_Data *dyndata = elf_getdata (dynscn, NULL);
+ if (dynshdr != NULL && dynshdr->sh_type == SHT_DYNAMIC
+ && dyndata != NULL)
+ for (size_t j = 0; j < dynshdr->sh_size / dynshdr->sh_entsize; ++j)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (dyndata, j, &dyn_mem);
+ if (dyn != NULL
+ && (dyn->d_tag == DT_TEXTREL
+ || (dyn->d_tag == DT_FLAGS
+ && (dyn->d_un.d_val & DF_TEXTREL) != 0)))
+ {
+ textrel = true;
+ break;
+ }
+ }
+ }
+ }
- if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (rela->r_info)))
- ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
- else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (rela->r_info)))
+ /* A quick test which can be easily done here (although it is a bit
+ out of place): the text relocation flag makes only sense if there
+ is a segment which is not writable. */
+ if (textrel)
+ {
+ struct loaded_segment *seg = *loadedp;
+ while (seg != NULL && !seg->read_only)
+ seg = seg->next;
+ if (seg == NULL)
ERROR (gettext ("\
+text relocation flag set but there is no read-only segment\n"));
+ }
+
+ return reldyn;
+}
+
+
+enum load_state
+ {
+ state_undecided,
+ state_loaded,
+ state_unloaded,
+ state_error
+ };
+
+
+static void
+check_one_reloc (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *relshdr, int idx,
+ size_t cnt, const GElf_Shdr *symshdr, Elf_Data *symdata,
+ GElf_Addr r_offset, GElf_Xword r_info,
+ const GElf_Shdr *destshdr, bool reldyn,
+ struct loaded_segment *loaded, enum load_state *statep)
+{
+ bool known_broken = gnuld;
+
+ if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (r_info)))
+ ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"),
+ idx, section_name (ebl, idx), cnt);
+ else if (((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ /* The executable/DSO can contain relocation sections with
+ all the relocations the linker has applied. Those sections
+ are marked non-loaded, though. */
+ || (relshdr->sh_flags & SHF_ALLOC) != 0)
+ && !ebl_reloc_valid_use (ebl, GELF_R_TYPE (r_info)))
+ ERROR (gettext ("\
section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
- if (symshdr != NULL
- && ((GELF_R_SYM (rela->r_info) + 1)
- * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)
- > symshdr->sh_size))
- ERROR (gettext ("\
+ if (symshdr != NULL
+ && ((GELF_R_SYM (r_info) + 1)
+ * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)
+ > symshdr->sh_size))
+ ERROR (gettext ("\
section [%2d] '%s': relocation %zu: invalid symbol index\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
- if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (rela->r_info)))
- {
- const char *name;
- char buf[64];
- GElf_Sym sym_mem;
- GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rela->r_info),
- &sym_mem);
- if (sym != NULL
- /* Get the name for the symbol. */
- && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name))
- && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 )
- ERROR (gettext ("\
+ /* No more tests if this is a no-op relocation. */
+ if (ebl_none_reloc_p (ebl, GELF_R_TYPE (r_info)))
+ return;
+
+ if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (r_info)))
+ {
+ const char *name;
+ char buf[64];
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem);
+ if (sym != NULL
+ /* Get the name for the symbol. */
+ && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name))
+ && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 )
+ ERROR (gettext ("\
section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt,
- ebl_reloc_type_name (ebl, GELF_R_SYM (rela->r_info),
- buf, sizeof (buf)));
- }
+ idx, section_name (ebl, idx), cnt,
+ ebl_reloc_type_name (ebl, GELF_R_SYM (r_info),
+ buf, sizeof (buf)));
+ }
- if (reldyn)
+ if (reldyn)
+ {
+ // XXX TODO Check .rel.dyn section addresses.
+ }
+ else if (!known_broken)
+ {
+ if (destshdr != NULL
+ && GELF_R_TYPE (r_info) != 0
+ && (r_offset - (ehdr->e_type == ET_REL ? 0
+ : destshdr->sh_addr)) >= destshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': relocation %zu: offset out of bounds\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem);
+
+ if (ebl_copy_reloc_p (ebl, GELF_R_TYPE (r_info))
+ /* Make sure the referenced symbol is an object or unspecified. */
+ && sym != NULL
+ && GELF_ST_TYPE (sym->st_info) != STT_NOTYPE
+ && GELF_ST_TYPE (sym->st_info) != STT_OBJECT)
+ {
+ char buf[64];
+ ERROR (gettext ("section [%2d] '%s': relocation %zu: copy relocation against symbol of type %s\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info),
+ buf, sizeof (buf)));
+ }
+
+ if ((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ || (relshdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ bool in_loaded_seg = false;
+ while (loaded != NULL)
{
- // XXX TODO Check .rel.dyn section addresses.
+ if (r_offset < loaded->to
+ && r_offset + (sym == NULL ? 0 : sym->st_size) >= loaded->from)
+ {
+ /* The symbol is in this segment. */
+ if (loaded->read_only)
+ {
+ if (textrel)
+ needed_textrel = true;
+ else
+ ERROR (gettext ("section [%2d] '%s': relocation %zu: read-only section modified but text relocation flag not set\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+
+ in_loaded_seg = true;
+ }
+
+ loaded = loaded->next;
}
- else if (!known_broken)
+
+ if (*statep == state_undecided)
+ *statep = in_loaded_seg ? state_loaded : state_unloaded;
+ else if ((*statep == state_unloaded && in_loaded_seg)
+ || (*statep == state_loaded && !in_loaded_seg))
{
- if (destshdr != NULL
- && (rela->r_offset - destshdr->sh_addr) >= destshdr->sh_size)
- ERROR (gettext ("\
-section [%2d] '%s': relocation %zu: offset out of bounds\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ ERROR (gettext ("\
+section [%2d] '%s': relocations are against loaded and unloaded data\n"),
+ idx, section_name (ebl, idx));
+ *statep = state_error;
}
}
}
static void
-check_rel (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_rela (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
{
- Elf_Scn *scn;
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
- Elf_Data *data;
- GElf_Shdr destshdr_mem;
- GElf_Shdr *destshdr = NULL;
- size_t cnt;
- bool reldyn = false;
- bool known_broken = gnuld;
-
- scn = elf_getscn (ebl->elf, idx);
- shdr = gelf_getshdr (scn, &shdr_mem);
- if (shdr == NULL)
- return;
- data = elf_getdata (scn, NULL);
+ 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, ehdr, idx));
+ idx, section_name (ebl, idx));
return;
}
- /* Check whether the link to the section we relocate is reasonable. */
- if (shdr->sh_info >= shnum)
- ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"),
- idx, section_name (ebl, ehdr, idx));
- else
+ /* Check the fields of the section header. */
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = NULL;
+ struct loaded_segment *loaded = NULL;
+ bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_RELA, &destshdr,
+ &destshdr_mem, &loaded);
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ 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)
{
- destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
- &destshdr_mem);
- if (destshdr != NULL)
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
+ if (rela == NULL)
{
- if (destshdr->sh_type != SHT_PROGBITS
- && destshdr->sh_type != SHT_NOBITS)
- {
- reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, false);
- if (!reldyn)
- ERROR (gettext ("\
-section [%2d] '%s': invalid destination section type\n"),
- idx, section_name (ebl, ehdr, idx));
- else
- {
- /* There is no standard, but we require that .rela.dyn
- sections have a sh_info value of zero. */
- if (shdr->sh_info != 0)
- ERROR (gettext ("\
-section [%2d] '%s': sh_info should be zero\n"),
- idx, section_name (ebl, ehdr, idx));
- }
- }
-
- if ((destshdr->sh_flags & (SHF_MERGE | SHF_STRINGS)) != 0)
- ERROR (gettext ("\
-section [%2d] '%s': no relocations for merge-able sections possible\n"),
- idx, section_name (ebl, ehdr, idx));
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get relocation %zu: %s\n"),
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
+ continue;
}
+
+ check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata,
+ rela->r_offset, rela->r_info, destshdr, reldyn, loaded,
+ &state);
}
- if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT))
- ERROR (gettext ("\
-section [%2d] '%s': section entry size does not match ElfXX_Rel\n"),
- idx, section_name (ebl, ehdr, idx));
+ while (loaded != NULL)
+ {
+ struct loaded_segment *old = loaded;
+ loaded = loaded->next;
+ free (old);
+ }
+}
+
+
+static void
+check_rel (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ 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;
+ }
+
+ /* Check the fields of the section header. */
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = NULL;
+ struct loaded_segment *loaded = NULL;
+ bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_REL, &destshdr,
+ &destshdr_mem, &loaded);
Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
Elf_Data *symdata = elf_getdata (symscn, NULL);
+ enum load_state state = state_undecided;
- for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
{
GElf_Rel rel_mem;
- GElf_Rel *rel;
-
- rel = gelf_getrel (data, cnt, &rel_mem);
+ GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
if (rel == NULL)
{
ERROR (gettext ("\
section [%2d] '%s': cannot get relocation %zu: %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1));
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
continue;
}
- if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)))
- ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
- else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (rel->r_info)))
- ERROR (gettext ("\
-section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
-
- if (symshdr != NULL
- && ((GELF_R_SYM (rel->r_info) + 1)
- * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)
- > symshdr->sh_size))
- ERROR (gettext ("\
-section [%2d] '%s': relocation %zu: invalid symbol index\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
-
- if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (rel->r_info)))
- {
- const char *name;
- char buf[64];
- GElf_Sym sym_mem;
- GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (rel->r_info),
- &sym_mem);
- if (sym != NULL
- /* Get the name for the symbol. */
- && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name))
- && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 )
- ERROR (gettext ("\
-section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt,
- ebl_reloc_type_name (ebl, GELF_R_SYM (rel->r_info),
- buf, sizeof (buf)));
- }
+ check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata,
+ rel->r_offset, rel->r_info, destshdr, reldyn, loaded,
+ &state);
+ }
- if (reldyn)
- {
- // XXX TODO Check .rel.dyn section addresses.
- }
- else if (!known_broken)
- {
- if (destshdr != NULL
- && GELF_R_TYPE (rel->r_info) != 0
- && (rel->r_offset - destshdr->sh_addr) >= destshdr->sh_size)
- ERROR (gettext ("\
-section [%2d] '%s': relocation %zu: offset out of bounds\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
- }
+ while (loaded != NULL)
+ {
+ struct loaded_segment *old = loaded;
+ loaded = loaded->next;
+ free (old);
}
}
@@ -1268,11 +1491,8 @@ static int ndynamic;
static void
-check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
{
- Elf_Scn *scn;
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
Elf_Data *data;
GElf_Shdr strshdr_mem;
GElf_Shdr *strshdr;
@@ -1283,8 +1503,7 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
[DT_PLTRELSZ] = { [DT_JMPREL] = true },
[DT_HASH] = { [DT_SYMTAB] = true },
[DT_STRTAB] = { [DT_STRSZ] = true },
- [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_HASH] = true,
- [DT_SYMENT] = true },
+ [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_SYMENT] = true },
[DT_RELA] = { [DT_RELASZ] = true, [DT_RELAENT] = true },
[DT_RELASZ] = { [DT_RELA] = true },
[DT_RELAENT] = { [DT_RELA] = true },
@@ -1298,9 +1517,10 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
[DT_JMPREL] = { [DT_PLTRELSZ] = true, [DT_PLTREL] = true },
[DT_RUNPATH] = { [DT_STRTAB] = true },
[DT_PLTREL] = { [DT_JMPREL] = true },
- [DT_PLTRELSZ] = { [DT_JMPREL] = true }
};
bool has_dt[DT_NUM];
+ bool has_val_dt[DT_VALNUM];
+ bool has_addr_dt[DT_ADDRNUM];
static const bool level2[DT_NUM] =
{
[DT_RPATH] = true,
@@ -1311,7 +1531,6 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
static const bool mandatory[DT_NUM] =
{
[DT_NULL] = true,
- [DT_HASH] = true,
[DT_STRTAB] = true,
[DT_SYMTAB] = true,
[DT_STRSZ] = true,
@@ -1323,19 +1542,17 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
GElf_Word pltrelsz = 0;
memset (has_dt, '\0', sizeof (has_dt));
+ memset (has_val_dt, '\0', sizeof (has_val_dt));
+ memset (has_addr_dt, '\0', sizeof (has_addr_dt));
if (++ndynamic == 2)
ERROR (gettext ("more than one dynamic section present\n"));
- scn = elf_getscn (ebl->elf, idx);
- shdr = gelf_getshdr (scn, &shdr_mem);
- if (shdr == NULL)
- return;
- data = elf_getdata (scn, NULL);
+ 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, ehdr, idx));
+ idx, section_name (ebl, idx));
return;
}
@@ -1343,30 +1560,28 @@ check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
if (strshdr != NULL && strshdr->sh_type != SHT_STRTAB)
ERROR (gettext ("\
section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
- shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link),
- idx, section_name (ebl, ehdr, idx));
+ 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))
ERROR (gettext ("\
section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
if (shdr->sh_info != 0)
ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
bool non_null_warned = false;
for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
{
GElf_Dyn dyn_mem;
- GElf_Dyn *dyn;
-
- dyn = gelf_getdyn (data, cnt, &dyn_mem);
+ GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dyn_mem);
if (dyn == NULL)
{
ERROR (gettext ("\
section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt, elf_errmsg (-1));
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
continue;
}
@@ -1374,15 +1589,15 @@ section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"),
{
ERROR (gettext ("\
section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
non_null_warned = true;
}
if (!ebl_dynamic_tag_check (ebl, dyn->d_tag))
ERROR (gettext ("section [%2d] '%s': entry %zu: unknown tag\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
- if (dyn->d_tag < DT_NUM)
+ if (dyn->d_tag >= 0 && dyn->d_tag < DT_NUM)
{
if (has_dt[dyn->d_tag]
&& dyn->d_tag != DT_NEEDED
@@ -1392,7 +1607,7 @@ section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"),
char buf[50];
ERROR (gettext ("\
section [%2d] '%s': entry %zu: more than one entry with tag %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt,
+ idx, section_name (ebl, idx), cnt,
ebl_dynamic_tag_name (ebl, dyn->d_tag,
buf, sizeof (buf)));
}
@@ -1402,19 +1617,25 @@ section [%2d] '%s': entry %zu: more than one entry with tag %s\n"),
char buf[50];
ERROR (gettext ("\
section [%2d] '%s': entry %zu: level 2 tag %s used\n"),
- idx, section_name (ebl, ehdr, idx), cnt,
+ idx, section_name (ebl, idx), cnt,
ebl_dynamic_tag_name (ebl, dyn->d_tag,
buf, sizeof (buf)));
}
has_dt[dyn->d_tag] = true;
}
+ else if (dyn->d_tag <= DT_VALRNGHI
+ && DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM)
+ has_val_dt[DT_VALTAGIDX (dyn->d_tag)] = true;
+ else if (dyn->d_tag <= DT_ADDRRNGHI
+ && DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)
+ has_addr_dt[DT_ADDRTAGIDX (dyn->d_tag)] = true;
if (dyn->d_tag == DT_PLTREL && dyn->d_un.d_val != DT_REL
&& dyn->d_un.d_val != DT_RELA)
ERROR (gettext ("\
section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"),
- idx, section_name (ebl, ehdr, idx), cnt);
+ idx, section_name (ebl, idx), cnt);
if (dyn->d_tag == DT_REL)
reladdr = dyn->d_un.d_ptr;
@@ -1424,14 +1645,89 @@ section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"),
pltreladdr = dyn->d_un.d_ptr;
if (dyn->d_tag == DT_PLTRELSZ)
pltrelsz = dyn->d_un.d_val;
+
+ /* Check that addresses for entries are in loaded segments. */
+ switch (dyn->d_tag)
+ {
+ size_t n;
+ case DT_STRTAB:
+ /* We require the referenced section is the same as the one
+ specified in sh_link. */
+ if (strshdr->sh_addr != dyn->d_un.d_val)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: pointer does not match address of section [%2d] '%s' referenced by sh_link\n"),
+ idx, section_name (ebl, idx), cnt,
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+ break;
+ }
+ goto check_addr;
+
+ default:
+ if (dyn->d_tag < DT_ADDRRNGLO || dyn->d_tag > DT_ADDRRNGHI)
+ /* Value is no pointer. */
+ break;
+ /* FALLTHROUGH */
+
+ case DT_AUXILIARY:
+ case DT_FILTER:
+ case DT_FINI:
+ case DT_FINI_ARRAY:
+ case DT_HASH:
+ case DT_INIT:
+ case DT_INIT_ARRAY:
+ case DT_JMPREL:
+ case DT_PLTGOT:
+ case DT_REL:
+ case DT_RELA:
+ case DT_SYMBOLIC:
+ case DT_SYMTAB:
+ case DT_VERDEF:
+ case DT_VERNEED:
+ case DT_VERSYM:
+ check_addr:
+ for (n = 0; n < ehdr->e_phnum; ++n)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, n, &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_LOAD
+ && phdr->p_vaddr <= dyn->d_un.d_ptr
+ && phdr->p_vaddr + phdr->p_memsz > dyn->d_un.d_ptr)
+ break;
+ }
+ if (unlikely (n >= ehdr->e_phnum))
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: %s value must point into loaded segment\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_dynamic_tag_name (ebl, dyn->d_tag, buf,
+ sizeof (buf)));
+ }
+ break;
+
+ case DT_NEEDED:
+ case DT_RPATH:
+ case DT_RUNPATH:
+ case DT_SONAME:
+ if (dyn->d_un.d_ptr >= strshdr->sh_size)
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: %s value must be valid offset in section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_dynamic_tag_name (ebl, dyn->d_tag, buf,
+ sizeof (buf)),
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+ }
+ break;
+ }
}
for (cnt = 1; cnt < DT_NUM; ++cnt)
if (has_dt[cnt])
{
- int inner;
-
- for (inner = 0; inner < DT_NUM; ++inner)
+ for (int inner = 0; inner < DT_NUM; ++inner)
if (dependencies[cnt][inner] && ! has_dt[inner])
{
char buf1[50];
@@ -1439,7 +1735,7 @@ section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"),
ERROR (gettext ("\
section [%2d] '%s': contains %s entry but not %s\n"),
- idx, section_name (ebl, ehdr, idx),
+ idx, section_name (ebl, idx),
ebl_dynamic_tag_name (ebl, cnt, buf1, sizeof (buf1)),
ebl_dynamic_tag_name (ebl, inner, buf2, sizeof (buf2)));
}
@@ -1451,95 +1747,144 @@ section [%2d] '%s': contains %s entry but not %s\n"),
char buf[50];
ERROR (gettext ("\
section [%2d] '%s': mandatory tag %s not present\n"),
- idx, section_name (ebl, ehdr, idx),
+ idx, section_name (ebl, idx),
ebl_dynamic_tag_name (ebl, cnt, buf, sizeof (buf)));
}
}
+ /* Make sure we have an hash table. */
+ if (!has_dt[DT_HASH] && !has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)])
+ ERROR (gettext ("\
+section [%2d] '%s': no hash section present\n"),
+ idx, section_name (ebl, idx));
+
+ /* The GNU-style hash table also needs a symbol table. */
+ if (!has_dt[DT_HASH] && has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)]
+ && !has_dt[DT_SYMTAB])
+ ERROR (gettext ("\
+section [%2d] '%s': contains %s entry but not %s\n"),
+ idx, section_name (ebl, idx),
+ "DT_GNU_HASH", "DT_SYMTAB");
+
/* Check the rel/rela tags. At least one group must be available. */
if ((has_dt[DT_RELA] || has_dt[DT_RELASZ] || has_dt[DT_RELAENT])
&& (!has_dt[DT_RELA] || !has_dt[DT_RELASZ] || !has_dt[DT_RELAENT]))
ERROR (gettext ("\
section [%2d] '%s': not all of %s, %s, and %s are present\n"),
- idx, section_name (ebl, ehdr, idx),
+ idx, section_name (ebl, idx),
"DT_RELA", "DT_RELASZ", "DT_RELAENT");
if ((has_dt[DT_REL] || has_dt[DT_RELSZ] || has_dt[DT_RELENT])
&& (!has_dt[DT_REL] || !has_dt[DT_RELSZ] || !has_dt[DT_RELENT]))
ERROR (gettext ("\
section [%2d] '%s': not all of %s, %s, and %s are present\n"),
- idx, section_name (ebl, ehdr, idx),
+ idx, section_name (ebl, idx),
"DT_REL", "DT_RELSZ", "DT_RELENT");
+
+ /* Check that all prelink sections are present if any of them is. */
+ if (has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)]
+ || has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)])
+ {
+ if (!has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"),
+ idx, section_name (ebl, idx), "DT_GNU_PRELINKED");
+ if (!has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"),
+ idx, section_name (ebl, idx), "DT_CHECKSUM");
+
+ /* Only DSOs can be marked like this. */
+ if (ehdr->e_type != ET_DYN)
+ ERROR (gettext ("\
+section [%2d] '%s': non-DSO file marked as dependency during prelink\n"),
+ idx, section_name (ebl, idx));
+ }
+
+ if (has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)]
+ || has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)]
+ || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)]
+ || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)])
+ {
+ if (!has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_CONFLICTSZ");
+ if (!has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_LIBLISTSZ");
+ if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_CONFLICT");
+ if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_LIBLIST");
+ }
}
static void
-check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
{
- Elf_Scn *scn;
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
- GElf_Shdr symshdr_mem;
- GElf_Shdr *symshdr;
- Elf_Scn *symscn;
- size_t cnt;
- Elf_Data *data;
- Elf_Data *symdata;
-
- scn = elf_getscn (ebl->elf, idx);
- shdr = gelf_getshdr (scn, &shdr_mem);
- if (shdr == NULL)
- return;
+ if (ehdr->e_type != ET_REL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': only relocatable files can have extended section index\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
- symscn = elf_getscn (ebl->elf, shdr->sh_link);
- symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
if (symshdr != NULL && symshdr->sh_type != SHT_SYMTAB)
ERROR (gettext ("\
section [%2d] '%s': extended section index section not for symbol table\n"),
- idx, section_name (ebl, ehdr, idx));
- symdata = elf_getdata (symscn, NULL);
+ idx, section_name (ebl, idx));
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
if (symdata == NULL)
ERROR (gettext ("cannot get data for symbol section\n"));
if (shdr->sh_entsize != sizeof (Elf32_Word))
ERROR (gettext ("\
section [%2d] '%s': entry size does not match Elf32_Word\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
if (symshdr != NULL
&& (shdr->sh_size / shdr->sh_entsize
< symshdr->sh_size / symshdr->sh_entsize))
ERROR (gettext ("\
section [%2d] '%s': extended index table too small for symbol table\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
if (shdr->sh_info != 0)
ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
- for (cnt = idx + 1; cnt < shnum; ++cnt)
+ for (size_t cnt = idx + 1; cnt < shnum; ++cnt)
{
GElf_Shdr rshdr_mem;
- GElf_Shdr *rshdr;
-
- rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem);
+ GElf_Shdr *rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem);
if (rshdr != NULL && rshdr->sh_type == SHT_SYMTAB_SHNDX
&& rshdr->sh_link == shdr->sh_link)
{
ERROR (gettext ("\
section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same symbol table\n"),
- idx, section_name (ebl, ehdr, idx),
- cnt, section_name (ebl, ehdr, cnt));
+ idx, section_name (ebl, idx),
+ cnt, section_name (ebl, cnt));
break;
}
}
- data = elf_getdata (scn, NULL);
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
if (*((Elf32_Word *) data->d_buf) != 0)
ERROR (gettext ("symbol 0 should have zero extended section index\n"));
- for (cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt)
+ for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt)
{
Elf32_Word xndx = ((Elf32_Word *) data->d_buf)[cnt];
@@ -1563,92 +1908,437 @@ extended section index is %" PRIu32 " but symbol index is not XINDEX\n"),
static void
-check_hash (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_sysv_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
+ GElf_Shdr *symshdr)
{
- Elf_Scn *scn;
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
- Elf_Data *data;
- Elf32_Word nbucket;
- Elf32_Word nchain;
- GElf_Shdr symshdr_mem;
- GElf_Shdr *symshdr;
+ Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1];
- scn = elf_getscn (ebl->elf, idx);
- shdr = gelf_getshdr (scn, &shdr_mem);
- if (shdr == NULL)
- return;
- data = elf_getdata (scn, NULL);
+ 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 %" PRIu64 " out of bounds\n"),
+ idx, section_name (ebl, idx), (uint64_t) (cnt - 2 - nbucket));
+}
+
+
+static void
+check_gnu_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
+ GElf_Shdr *symshdr)
+{
+ Elf32_Word nbuckets = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
+ Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2];
+
+ if (!powerof2 (bitmask_words))
+ ERROR (gettext ("\
+section [%2d] '%s': bitmask size not power of 2: %u\n"),
+ idx, section_name (ebl, idx), bitmask_words);
+
+ size_t bitmask_idxmask = bitmask_words - 1;
+ if (gelf_getclass (ebl->elf) == ELFCLASS64)
+ bitmask_words *= 2;
+ Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3];
+
+ 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"),
+ idx, section_name (ebl, idx), (long int) shdr->sh_size,
+ (long int) ((4 + bitmask_words + nbuckets) * sizeof (Elf32_Word)));
+ return;
+ }
+
+ if (shift > 31)
+ ERROR (gettext ("\
+section [%2d] '%s': 2nd hash function shift too big: %u\n"),
+ idx, section_name (ebl, idx), shift);
+
+ size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (4 + bitmask_words
+ + nbuckets);
+
+ 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);
+
+ union
+ {
+ Elf32_Word *p32;
+ Elf64_Xword *p64;
+ } bitmask = { .p32 = &((Elf32_Word *) data->d_buf)[4] },
+ collected = { .p32 = xcalloc (bitmask_words, sizeof (Elf32_Word)) };
+
+ size_t classbits = gelf_getclass (ebl->elf) == ELFCLASS32 ? 32 : 64;
+
+ size_t cnt;
+ for (cnt = 4 + bitmask_words; cnt < 4 + bitmask_words + nbuckets; ++cnt)
+ {
+ Elf32_Word symidx = ((Elf32_Word *) data->d_buf)[cnt];
+
+ if (symidx == 0)
+ 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 - (4 + bitmask_words));
+ continue;
+ }
+
+ while (symidx - symbias < maxidx)
+ {
+ Elf32_Word chainhash = ((Elf32_Word *) data->d_buf)[4
+ + bitmask_words
+ + 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
+ && GELF_ST_TYPE (sym->st_info) != STT_FUNC)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"),
+ idx, section_name (ebl, idx), symidx,
+ cnt - (4 + bitmask_words));
+
+ 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 - (4 + bitmask_words));
+
+ /* Set the bits in the bitmask. */
+ size_t maskidx = (hval / classbits) & bitmask_idxmask;
+ if (classbits == 32)
+ {
+ collected.p32[maskidx]
+ |= UINT32_C (1) << (hval & (classbits - 1));
+ collected.p32[maskidx]
+ |= UINT32_C (1) << ((hval >> shift) & (classbits - 1));
+ }
+ else
+ {
+ collected.p64[maskidx]
+ |= UINT64_C (1) << (hval & (classbits - 1));
+ collected.p64[maskidx]
+ |= UINT64_C (1) << ((hval >> shift) & (classbits - 1));
+ }
+ }
+ }
+
+ if ((chainhash & 1) != 0)
+ break;
+
+ ++symidx;
+ }
+
+ if (symidx - symbias >= maxidx)
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu out of bounds\n"),
+ idx, section_name (ebl, idx), cnt - (4 + bitmask_words));
+ else if (symshdr != NULL
+ && 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 - (4 + bitmask_words));
+ }
+
+ if (memcmp (collected.p32, bitmask.p32, bitmask_words * sizeof (Elf32_Word)))
+ ERROR (gettext ("\
+section [%2d] '%s': bitmask does not match names in the hash table\n"),
+ idx, section_name (ebl, idx));
+
+ free (collected.p32);
+}
+
+
+static void
+check_hash (int tag, Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (ehdr->e_type == ET_REL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': relocatable files cannot have hash tables\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ 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, ehdr, idx));
+ idx, section_name (ebl, idx));
return;
}
- symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &symshdr_mem);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &symshdr_mem);
if (symshdr != NULL && symshdr->sh_type != SHT_DYNSYM)
ERROR (gettext ("\
section [%2d] '%s': hash table not for dynamic symbol table\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
- if (shdr->sh_entsize != sizeof (Elf32_Word))
+ if (shdr->sh_entsize != (tag == SHT_GNU_HASH
+ ? (gelf_getclass (ebl->elf) == ELFCLASS32
+ ? sizeof (Elf32_Word) : 0)
+ : (size_t) ebl_sysvhash_entrysize (ebl)))
ERROR (gettext ("\
-section [%2d] '%s': entry size does not match Elf32_Word\n"),
- idx, section_name (ebl, ehdr, idx));
+section [%2d] '%s': hash table entry size incorrect\n"),
+ idx, section_name (ebl, idx));
if ((shdr->sh_flags & SHF_ALLOC) == 0)
ERROR (gettext ("section [%2d] '%s': not marked to be allocated\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
- if (shdr->sh_size < 2 * shdr->sh_entsize)
+ if (shdr->sh_size < (tag == SHT_GNU_HASH ? 4 : 2) * (shdr->sh_entsize ?: 4))
{
ERROR (gettext ("\
-section [%2d] '%s': hash table has not even room for nbucket and nchain\n"),
- idx, section_name (ebl, ehdr, idx));
+section [%2d] '%s': hash table has not even room for initial administrative entries\n"),
+ idx, section_name (ebl, idx));
return;
}
- nbucket = ((Elf32_Word *) data->d_buf)[0];
- nchain = ((Elf32_Word *) data->d_buf)[1];
+ switch (tag)
+ {
+ 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;
- 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, ehdr, idx), (long int) shdr->sh_size,
- (long int) ((2 + nbucket + nchain) * shdr->sh_entsize));
+ case SHT_GNU_HASH:
+ check_gnu_hash (ebl, shdr, data, idx, symshdr);
+ break;
- if (symshdr != NULL)
+ default:
+ assert (! "should not happen");
+ }
+}
+
+
+/* Compare content of both hash tables, it must be identical. */
+static void
+compare_hash_gnu_hash (Ebl *ebl, GElf_Ehdr *ehdr, size_t hash_idx,
+ size_t gnu_hash_idx)
+{
+ Elf_Scn *hash_scn = elf_getscn (ebl->elf, hash_idx);
+ Elf_Data *hash_data = elf_getdata (hash_scn, NULL);
+ GElf_Shdr hash_shdr_mem;
+ GElf_Shdr *hash_shdr = gelf_getshdr (hash_scn, &hash_shdr_mem);
+ Elf_Scn *gnu_hash_scn = elf_getscn (ebl->elf, gnu_hash_idx);
+ Elf_Data *gnu_hash_data = elf_getdata (gnu_hash_scn, NULL);
+ GElf_Shdr gnu_hash_shdr_mem;
+ GElf_Shdr *gnu_hash_shdr = gelf_getshdr (gnu_hash_scn, &gnu_hash_shdr_mem);
+
+ if (hash_shdr == NULL || gnu_hash_shdr == NULL
+ || hash_data == NULL || gnu_hash_data == NULL)
+ /* None of these pointers should be NULL since we used the
+ sections already. We are careful nonetheless. */
+ return;
+
+ /* The link must point to the same symbol table. */
+ if (hash_shdr->sh_link != gnu_hash_shdr->sh_link)
{
- size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
- size_t cnt;
+ ERROR (gettext ("\
+sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"),
+ hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name),
+ gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name));
+ return;
+ }
- if (nchain < symshdr->sh_size / symshdr->sh_entsize)
- ERROR (gettext ("section [%2d] '%s': chain array not large enough\n"),
- idx, section_name (ebl, ehdr, idx));
+ Elf_Scn *sym_scn = elf_getscn (ebl->elf, hash_shdr->sh_link);
+ Elf_Data *sym_data = elf_getdata (sym_scn, NULL);
+ GElf_Shdr sym_shdr_mem;
+ GElf_Shdr *sym_shdr = gelf_getshdr (sym_scn, &sym_shdr_mem);
- 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, ehdr, idx), cnt - 2);
+ if (sym_data == NULL || sym_shdr == NULL)
+ return;
- 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, ehdr, idx), cnt - 2 - nbucket);
+ int nentries = sym_shdr->sh_size / sym_shdr->sh_entsize;
+ char *used = alloca (nentries);
+ memset (used, '\0', nentries);
+
+ /* First go over the GNU_HASH table and mark the entries as used. */
+ const Elf32_Word *gnu_hasharr = (Elf32_Word *) gnu_hash_data->d_buf;
+ Elf32_Word gnu_nbucket = gnu_hasharr[0];
+ const int bitmap_factor = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 1 : 2;
+ const Elf32_Word *gnu_bucket = (gnu_hasharr
+ + (4 + gnu_hasharr[2] * bitmap_factor));
+ const Elf32_Word *gnu_chain = gnu_bucket + gnu_hasharr[0] - gnu_hasharr[1];
+
+ for (Elf32_Word cnt = 0; cnt < gnu_nbucket; ++cnt)
+ {
+ Elf32_Word symidx = gnu_bucket[cnt];
+ if (symidx != STN_UNDEF)
+ do
+ used[symidx] |= 1;
+ while ((gnu_chain[symidx++] & 1u) == 0);
+ }
+
+ /* Now go over the old hash table and check that we cover the same
+ entries. */
+ if (hash_shdr->sh_entsize == sizeof (Elf32_Word))
+ {
+ const Elf32_Word *hasharr = (Elf32_Word *) hash_data->d_buf;
+ Elf32_Word nbucket = hasharr[0];
+ const Elf32_Word *bucket = &hasharr[2];
+ const Elf32_Word *chain = &hasharr[2 + nbucket];
+
+ for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
+ {
+ Elf32_Word symidx = bucket[cnt];
+ while (symidx != STN_UNDEF)
+ {
+ used[symidx] |= 2;
+ symidx = chain[symidx];
+ }
+ }
+ }
+ else
+ {
+ const Elf64_Xword *hasharr = (Elf64_Xword *) hash_data->d_buf;
+ Elf64_Xword nbucket = hasharr[0];
+ const Elf64_Xword *bucket = &hasharr[2];
+ const Elf64_Xword *chain = &hasharr[2 + nbucket];
+
+ for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt)
+ {
+ Elf64_Xword symidx = bucket[cnt];
+ while (symidx != STN_UNDEF)
+ {
+ used[symidx] |= 2;
+ symidx = chain[symidx];
+ }
+ }
}
+
+ /* Now see which entries are not set in one or both hash tables
+ (unless the symbol is undefined in which case it can be omitted
+ in the new table format). */
+ if ((used[0] & 1) != 0)
+ ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"),
+ gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name));
+ if ((used[0] & 2) != 0)
+ ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"),
+ hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name));
+
+ for (int cnt = 1; cnt < nentries; ++cnt)
+ if (used[cnt] != 0 && used[cnt] != 3)
+ {
+ if (used[cnt] == 1)
+ ERROR (gettext ("\
+symbol %d referenced in new hash table in [%2zu] '%s' but not in old hash table in [%2zu] '%s'\n"),
+ cnt, gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name),
+ hash_idx,
+ elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name));
+ else
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (sym_data, cnt, &sym_mem);
+
+ if (sym != NULL && sym->st_shndx != STN_UNDEF)
+ ERROR (gettext ("\
+symbol %d referenced in old hash table in [%2zu] '%s' but not in new hash table in [%2zu] '%s'\n"),
+ cnt, hash_idx,
+ elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name),
+ gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name));
+ }
+ }
}
static void
-check_null (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+check_null (Ebl *ebl, GElf_Shdr *shdr, int idx)
{
#define TEST(name, extra) \
if (extra && shdr->sh_##name != 0) \
ERROR (gettext ("section [%2d] '%s': nonzero sh_%s for NULL section\n"), \
- idx, section_name (ebl, ehdr, idx), #name)
+ idx, section_name (ebl, idx), #name)
TEST (name, 1);
TEST (flags, 1);
@@ -1669,44 +2359,57 @@ check_group (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
{
ERROR (gettext ("\
section [%2d] '%s': section groups only allowed in relocatable object files\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
return;
}
/* Check that sh_link is an index of a symbol table. */
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
- GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
- &symshdr_mem);
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
if (symshdr == NULL)
ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"),
- idx, section_name (ebl, ehdr, idx), elf_errmsg (-1));
+ idx, section_name (ebl, idx), elf_errmsg (-1));
else
{
if (symshdr->sh_type != SHT_SYMTAB)
ERROR (gettext ("\
section [%2d] '%s': section reference in sh_link is no symbol table\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
if (shdr->sh_info >= symshdr->sh_size / gelf_fsize (ebl->elf, ELF_T_SYM,
1, EV_CURRENT))
ERROR (gettext ("\
section [%2d] '%s': invalid symbol index in sh_info\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
if (shdr->sh_flags != 0)
ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
+
+ GElf_Sym sym_data;
+ GElf_Sym *sym = gelf_getsym (elf_getdata (symscn, NULL), shdr->sh_info,
+ &sym_data);
+ if (sym == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get symbol for signature\n"),
+ idx, section_name (ebl, idx));
+ else if (strcmp (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name),
+ "") == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': signature symbol canot be empty string\n"),
+ idx, section_name (ebl, idx));
if (be_strict
&& shdr->sh_entsize != elf32_fsize (ELF_T_WORD, 1, EV_CURRENT))
ERROR (gettext ("section [%2d] '%s': sh_flags not set correctly\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
}
Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
if (data == NULL)
ERROR (gettext ("section [%2d] '%s': cannot get data: %s\n"),
- idx, section_name (ebl, ehdr, idx), elf_errmsg (-1));
+ idx, section_name (ebl, idx), elf_errmsg (-1));
else
{
size_t elsize = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT);
@@ -1716,22 +2419,22 @@ section [%2d] '%s': invalid symbol index in sh_info\n"),
if (data->d_size % elsize != 0)
ERROR (gettext ("\
section [%2d] '%s': section size not multiple of sizeof(Elf32_Word)\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
if (data->d_size < elsize)
ERROR (gettext ("\
section [%2d] '%s': section group without flags word\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
else if (be_strict)
{
if (data->d_size < 2 * elsize)
ERROR (gettext ("\
section [%2d] '%s': section group without member\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
else if (data->d_size < 3 * elsize)
ERROR (gettext ("\
section [%2d] '%s': section group with only one member\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
}
#if ALLOW_UNALIGNED
@@ -1741,7 +2444,7 @@ section [%2d] '%s': section group with only one member\n"),
#endif
if ((val & ~GRP_COMDAT) != 0)
ERROR (gettext ("section [%2d] '%s': unknown section group flags\n"),
- idx, section_name (ebl, ehdr, idx));
+ idx, section_name (ebl, idx));
for (cnt = elsize; cnt < data->d_size; cnt += elsize)
{
@@ -1754,97 +2457,48 @@ section [%2d] '%s': section group with only one member\n"),
if (val > shnum)
ERROR (gettext ("\
section [%2d] '%s': section index %Zu out of range\n"),
- idx, section_name (ebl, ehdr, idx), cnt / elsize);
+ idx, section_name (ebl, idx), cnt / elsize);
else
{
GElf_Shdr refshdr_mem;
- GElf_Shdr *refshdr;
-
- refshdr = gelf_getshdr (elf_getscn (ebl->elf, val),
- &refshdr_mem);
+ GElf_Shdr *refshdr = gelf_getshdr (elf_getscn (ebl->elf, val),
+ &refshdr_mem);
if (refshdr == NULL)
ERROR (gettext ("\
section [%2d] '%s': cannot get section header for element %zu: %s\n"),
- idx, section_name (ebl, ehdr, idx), cnt / elsize,
+ idx, section_name (ebl, idx), cnt / elsize,
elf_errmsg (-1));
else
{
if (refshdr->sh_type == SHT_GROUP)
ERROR (gettext ("\
section [%2d] '%s': section group contains another group [%2d] '%s'\n"),
- idx, section_name (ebl, ehdr, idx),
- val, section_name (ebl, ehdr, val));
+ idx, section_name (ebl, idx),
+ val, section_name (ebl, val));
if ((refshdr->sh_flags & SHF_GROUP) == 0)
ERROR (gettext ("\
section [%2d] '%s': element %Zu references section [%2d] '%s' without SHF_GROUP flag set\n"),
- idx, section_name (ebl, ehdr, idx), cnt / elsize,
- val, section_name (ebl, ehdr, val));
+ idx, section_name (ebl, idx), cnt / elsize,
+ val, section_name (ebl, val));
}
if (++scnref[val] == 2)
ERROR (gettext ("\
section [%2d] '%s' is contained in more than one section group\n"),
- val, section_name (ebl, ehdr, val));
+ val, section_name (ebl, val));
}
}
}
}
-static bool has_loadable_segment;
-static bool has_interp_segment;
-
-static const struct
-{
- const char *name;
- size_t namelen;
- GElf_Word type;
- enum { unused, exact, atleast } attrflag;
- GElf_Word attr;
- GElf_Word attr2;
-} special_sections[] =
- {
- /* See figure 4-14 in the gABI. */
- { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
- { ".comment", 8, SHT_PROGBITS, exact, 0, 0 },
- { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
- { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
- { ".debug", 7, SHT_PROGBITS, exact, 0, 0 },
- { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE },
- { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 },
- { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 },
- { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
- { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
- { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info?
- { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 },
- { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
- { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
- { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests?
- { ".line", 6, SHT_PROGBITS, exact, 0, 0 },
- { ".note", 6, SHT_NOTE, exact, 0, 0 },
- { ".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
- { ".rodata", 8, SHT_PROGBITS, exact, SHF_ALLOC, 0 },
- { ".rodata1", 9, SHT_PROGBITS, exact, SHF_ALLOC, 0 },
- { ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 },
- { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests
- { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests
- { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests
- { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
- { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
- { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
- { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }
- };
-#define nspecial_sections \
- (sizeof (special_sections) / sizeof (special_sections[0]))
-
-
static const char *
section_flags_string (GElf_Word flags, char *buf, size_t len)
{
+ if (flags == 0)
+ return "none";
+
static const struct
{
GElf_Word flag;
@@ -1867,9 +2521,8 @@ section_flags_string (GElf_Word flags, char *buf, size_t len)
const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]);
char *cp = buf;
- size_t cnt;
- for (cnt = 0; cnt < nknown_flags; ++cnt)
+ for (size_t cnt = 0; cnt < nknown_flags; ++cnt)
if (flags & known_flags[cnt].flag)
{
if (cp != buf && len > 1)
@@ -1894,14 +2547,126 @@ section_flags_string (GElf_Word flags, char *buf, size_t len)
}
+static int
+has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx)
+{
+ /* First find the relocation section for the symbol table. */
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL
+ && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ && shdr->sh_link == symscnndx)
+ /* Found the section. */
+ break;
+ }
+
+ if (scn == NULL)
+ return 0;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return 0;
+
+ if (shdr->sh_type == SHT_REL)
+ for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, i, &rel_mem);
+ if (rel == NULL)
+ continue;
+
+ if (GELF_R_SYM (rel->r_info) == symndx
+ && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
+ return 1;
+ }
+ else
+ for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, i, &rela_mem);
+ if (rela == NULL)
+ continue;
+
+ if (GELF_R_SYM (rela->r_info) == symndx
+ && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+in_nobits_scn (Ebl *ebl, unsigned int shndx)
+{
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, shndx), &shdr_mem);
+ return shdr != NULL && shdr->sh_type == SHT_NOBITS;
+}
+
+
+static struct version_namelist
+{
+ const char *objname;
+ const char *name;
+ GElf_Versym ndx;
+ enum { ver_def, ver_need } type;
+ struct version_namelist *next;
+} *version_namelist;
+
+
+static int
+add_version (const char *objname, const char *name, GElf_Versym ndx, int type)
+{
+ /* Check that there are no duplications. */
+ struct version_namelist *nlp = version_namelist;
+ while (nlp != NULL)
+ {
+ if (((nlp->objname == NULL && objname == NULL)
+ || (nlp->objname != NULL && objname != NULL
+ && strcmp (nlp->objname, objname) == 0))
+ && strcmp (nlp->name, name) == 0)
+ return nlp->type == ver_def ? 1 : -1;
+ nlp = nlp->next;
+ }
+
+ nlp = xmalloc (sizeof (*nlp));
+ nlp->objname = objname;
+ nlp->name = name;
+ nlp->ndx = ndx;
+ nlp->type = type;
+ nlp->next = version_namelist;
+ version_namelist = nlp;
+
+ return 0;
+}
+
+
static void
-check_versym (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+check_versym (Ebl *ebl, int idx)
{
- /* The number of elements in the version symbol table must be the
- same as the number of symbols. */
+ Elf_Scn *scn = elf_getscn (ebl->elf, idx);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ /* The error has already been reported. */
+ return;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
- GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
- &symshdr_mem);
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
if (symshdr == NULL)
/* The error has already been reported. */
return;
@@ -1910,33 +2675,693 @@ check_versym (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
{
ERROR (gettext ("\
section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic symbol table\n"),
- idx, section_name (ebl, ehdr, idx),
- shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link));
+ idx, section_name (ebl, idx),
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
return;
}
+ /* 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)
ERROR (gettext ("\
section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\n"),
- idx, section_name (ebl, ehdr, idx),
- shdr->sh_link, section_name (ebl, ehdr, shdr->sh_link));
+ idx, section_name (ebl, idx),
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ if (symdata == NULL)
+ /* The error has already been reported. */
+ return;
+
+ for (int cnt = 1; (size_t) cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ {
+ GElf_Versym versym_mem;
+ GElf_Versym *versym = gelf_getversym (data, cnt, &versym_mem);
+ if (versym == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: cannot read version data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_mem);
+ if (sym == NULL)
+ /* Already reported elsewhere. */
+ continue;
- // XXX TODO A lot more tests
- // check value of the fields. local symbols must have zero entries.
- // nonlocal symbols refer to valid version. Check that version index
- // in bound.
+ if (*versym == VER_NDX_GLOBAL)
+ {
+ /* Global symbol. Make sure it is not defined as local. */
+ if (GELF_ST_BIND (sym->st_info) == STB_LOCAL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: local symbol with global scope\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if (*versym != VER_NDX_LOCAL)
+ {
+ /* Versioned symbol. Make sure it is not defined as local. */
+ if (!gnuld && GELF_ST_BIND (sym->st_info) == STB_LOCAL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: local symbol with version\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ /* Look through the list of defined versions and locate the
+ index we need for this symbol. */
+ struct version_namelist *runp = version_namelist;
+ while (runp != NULL)
+ if (runp->ndx == (*versym & (GElf_Versym) 0x7fff))
+ break;
+ else
+ runp = runp->next;
+
+ if (runp == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: invalid version index %d\n"),
+ idx, section_name (ebl, idx), cnt, (int) *versym);
+ else if (sym->st_shndx == SHN_UNDEF
+ && runp->type == ver_def)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: version index %d is for defined version\n"),
+ idx, section_name (ebl, idx), cnt, (int) *versym);
+ else if (sym->st_shndx != SHN_UNDEF
+ && runp->type == ver_need)
+ {
+ /* Unless this symbol has a copy relocation associated
+ this must not happen. */
+ if (!has_copy_reloc (ebl, shdr->sh_link, cnt)
+ && !in_nobits_scn (ebl, sym->st_shndx))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: version index %d is for requested version\n"),
+ idx, section_name (ebl, idx), cnt, (int) *versym);
+ }
+ }
+ }
}
-static void
-check_sections (Ebl *ebl, GElf_Ehdr *ehdr)
+static int
+unknown_dependency_p (Elf *elf, GElf_Ehdr *ehdr, const char *fname)
{
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = NULL;
+
+ int i;
+ for (i = 0; i < ehdr->e_phnum; ++i)
+ if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL
+ && phdr->p_type == PT_DYNAMIC)
+ break;
+
+ if (i == ehdr->e_phnum)
+ return 1;
+ assert (phdr != NULL);
+ Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset);
GElf_Shdr shdr_mem;
- GElf_Shdr *shdr;
- size_t cnt;
- bool dot_interp_section = false;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL)
+ for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
+ if (dyn != NULL && dyn->d_tag == DT_NEEDED)
+ {
+ const char *str = elf_strptr (elf, shdr->sh_link, dyn->d_un.d_val);
+ if (str != NULL && strcmp (str, fname) == 0)
+ /* Found it. */
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static unsigned int nverneed;
+
+static void
+check_verneed (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (++nverneed == 2)
+ ERROR (gettext ("more than one version reference section present\n"));
+
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &strshdr_mem);
+ if (strshdr == NULL)
+ return;
+ if (strshdr->sh_type != SHT_STRTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_link does not link to string table\n"),
+ idx, section_name (ebl, idx));
+
+ 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;
+ }
+ unsigned int offset = 0;
+ for (int cnt = shdr->sh_info; --cnt >= 0; )
+ {
+ /* Get the data at the next offset. */
+ GElf_Verneed needmem;
+ GElf_Verneed *need = gelf_getverneed (data, offset, &needmem);
+ if (need == NULL)
+ break;
+
+ unsigned int auxoffset = offset + need->vn_aux;
+
+ if (need->vn_version != EV_CURRENT)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong version %d\n"),
+ idx, section_name (ebl, idx), cnt, (int) need->vn_version);
+
+ if (need->vn_cnt > 0 && need->vn_aux < gelf_fsize (ebl->elf, ELF_T_VNEED,
+ 1, EV_CURRENT))
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ const char *libname = elf_strptr (ebl->elf, shdr->sh_link,
+ need->vn_file);
+ if (libname == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid file reference\n"),
+ idx, section_name (ebl, idx), cnt);
+ goto next_need;
+ }
+
+ /* Check that there is a DT_NEEDED entry for the referenced library. */
+ if (unknown_dependency_p (ebl->elf, ehdr, libname))
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d references unknown dependency\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ for (int cnt2 = need->vn_cnt; --cnt2 >= 0; )
+ {
+ GElf_Vernaux auxmem;
+ GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem);
+ if (aux == NULL)
+ break;
+
+ if ((aux->vna_flags & ~VER_FLG_WEAK) != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+
+ const char *verstr = elf_strptr (ebl->elf, shdr->sh_link,
+ aux->vna_name);
+ if (verstr == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+ else
+ {
+ GElf_Word hashval = elf_hash (verstr);
+ if (hashval != aux->vna_hash)
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, expected %#x\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2,
+ cnt, (int) hashval, (int) aux->vna_hash);
+
+ int res = add_version (libname, verstr, aux->vna_other,
+ ver_need);
+ if (unlikely (res !=0))
+ {
+ assert (res > 0);
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2,
+ cnt, verstr);
+ }
+ }
+
+ if ((aux->vna_next != 0 || cnt2 > 0)
+ && aux->vna_next < gelf_fsize (ebl->elf, ELF_T_VNAUX, 1,
+ EV_CURRENT))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+ break;
+ }
+
+ auxoffset += MAX (aux->vna_next,
+ gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, EV_CURRENT));
+ }
+
+ /* Find the next offset. */
+ next_need:
+ offset += need->vn_next;
+
+ if ((need->vn_next != 0 || cnt > 0)
+ && offset < auxoffset)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid offset to next entry\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+}
+
+
+static unsigned int nverdef;
+
+static void
+check_verdef (Ebl *ebl, GElf_Shdr *shdr, int idx)
+{
+ if (++nverdef == 2)
+ ERROR (gettext ("more than one version definition section present\n"));
+
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &strshdr_mem);
+ if (strshdr == NULL)
+ return;
+ if (strshdr->sh_type != SHT_STRTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_link does not link to string table\n"),
+ idx, section_name (ebl, idx));
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ no_data:
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ /* Iterate over all version definition entries. We check that there
+ is a BASE entry and that each index is unique. To do the later
+ we collection the information in a list which is later
+ examined. */
+ struct namelist
+ {
+ const char *name;
+ struct namelist *next;
+ } *namelist = NULL;
+ struct namelist *refnamelist = NULL;
+
+ bool has_base = false;
+ unsigned int offset = 0;
+ for (int cnt = shdr->sh_info; --cnt >= 0; )
+ {
+ /* Get the data at the next offset. */
+ GElf_Verdef defmem;
+ GElf_Verdef *def = gelf_getverdef (data, offset, &defmem);
+ if (def == NULL)
+ goto no_data;
+
+ if ((def->vd_flags & VER_FLG_BASE) != 0)
+ {
+ if (has_base)
+ ERROR (gettext ("\
+section [%2d] '%s': more than one BASE definition\n"),
+ idx, section_name (ebl, idx));
+ if (def->vd_ndx != VER_NDX_GLOBAL)
+ ERROR (gettext ("\
+section [%2d] '%s': BASE definition must have index VER_NDX_GLOBAL\n"),
+ idx, section_name (ebl, idx));
+ has_base = true;
+ }
+ if ((def->vd_flags & ~(VER_FLG_BASE|VER_FLG_WEAK)) != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has unknown flag\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (def->vd_version != EV_CURRENT)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong version %d\n"),
+ idx, section_name (ebl, idx), cnt, (int) def->vd_version);
+
+ if (def->vd_cnt > 0 && def->vd_aux < gelf_fsize (ebl->elf, ELF_T_VDEF,
+ 1, EV_CURRENT))
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ unsigned int auxoffset = offset + def->vd_aux;
+ GElf_Verdaux auxmem;
+ GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem);
+ if (aux == NULL)
+ goto no_data;
+
+ const char *name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+ if (name == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid name reference\n"),
+ idx, section_name (ebl, idx), cnt);
+ goto next_def;
+ }
+ GElf_Word hashval = elf_hash (name);
+ if (def->vd_hash != hashval)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"),
+ idx, section_name (ebl, idx), cnt, (int) hashval,
+ (int) def->vd_hash);
+
+ int res = add_version (NULL, name, def->vd_ndx, ver_def);
+ if (unlikely (res !=0))
+ {
+ assert (res > 0);
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has duplicate version name '%s'\n"),
+ idx, section_name (ebl, idx), cnt, name);
+ }
+
+ struct namelist *newname = alloca (sizeof (*newname));
+ newname->name = name;
+ newname->next = namelist;
+ namelist = newname;
+
+ auxoffset += aux->vda_next;
+ for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2)
+ {
+ aux = gelf_getverdaux (data, auxoffset, &auxmem);
+ if (aux == NULL)
+ goto no_data;
+
+ name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+ if (name == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid name reference in auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+ else
+ {
+ newname = alloca (sizeof (*newname));
+ newname->name = name;
+ newname->next = refnamelist;
+ refnamelist = newname;
+ }
+
+ if ((aux->vda_next != 0 || cnt2 + 1 < def->vd_cnt)
+ && aux->vda_next < gelf_fsize (ebl->elf, ELF_T_VDAUX, 1,
+ EV_CURRENT))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ auxoffset += MAX (aux->vda_next,
+ gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, EV_CURRENT));
+ }
+
+ /* Find the next offset. */
+ next_def:
+ offset += def->vd_next;
+
+ if ((def->vd_next != 0 || cnt > 0)
+ && offset < auxoffset)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid offset to next entry\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+
+ if (!has_base)
+ ERROR (gettext ("section [%2d] '%s': no BASE definition\n"),
+ idx, section_name (ebl, idx));
+
+ /* Check whether the referenced names are available. */
+ while (namelist != NULL)
+ {
+ struct version_namelist *runp = version_namelist;
+ while (runp != NULL)
+ {
+ if (runp->type == ver_def
+ && strcmp (runp->name, namelist->name) == 0)
+ break;
+ runp = runp->next;
+ }
+
+ if (runp == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': unknown parent version '%s'\n"),
+ idx, section_name (ebl, idx), namelist->name);
+
+ namelist = namelist->next;
+ }
+}
+
+static void
+check_attributes (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (shdr->sh_size == 0)
+ {
+ ERROR (gettext ("section [%2d] '%s': empty object attributes section\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf_Data *data = elf_rawdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL || data->d_size == 0)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ inline size_t pos (const unsigned char *p)
+ {
+ return p - (const unsigned char *) data->d_buf;
+ }
+
+ const unsigned char *p = data->d_buf;
+ if (*p++ != 'A')
+ {
+ ERROR (gettext ("section [%2d] '%s': unrecognized attribute format\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ inline size_t left (void)
+ {
+ return (const unsigned char *) data->d_buf + data->d_size - p;
+ }
+
+ while (left () >= 4)
+ {
+ uint32_t len;
+ memcpy (&len, p, sizeof len);
+
+ if (len == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: zero length field in attribute section\n"),
+ idx, section_name (ebl, idx), pos (p));
+
+ if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
+ CONVERT (len);
+
+ if (len > left ())
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: invalid length in attribute section\n"),
+ idx, section_name (ebl, idx), pos (p));
+ break;
+ }
+
+ const unsigned char *name = p + sizeof len;
+ p += len;
+
+ unsigned const char *q = memchr (name, '\0', len);
+ if (q == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unterminated vendor name string\n"),
+ idx, section_name (ebl, idx), pos (p));
+ continue;
+ }
+ ++q;
+
+ if (q - name == sizeof "gnu" && !memcmp (name, "gnu", sizeof "gnu"))
+ while (q < p)
+ {
+ unsigned const char *chunk = q;
+
+ unsigned int subsection_tag;
+ get_uleb128 (subsection_tag, q);
+
+ if (q >= p)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: endless ULEB128 in attribute subsection tag\n"),
+ idx, section_name (ebl, idx), pos (chunk));
+ break;
+ }
+ uint32_t subsection_len;
+ if (p - q < (ptrdiff_t) sizeof subsection_len)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: truncated attribute section\n"),
+ idx, section_name (ebl, idx), pos (q));
+ break;
+ }
+
+ memcpy (&subsection_len, q, sizeof subsection_len);
+ if (subsection_len == 0)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: zero length field in attribute subsection\n"),
+ idx, section_name (ebl, idx), pos (q));
+
+ q += sizeof subsection_len;
+ continue;
+ }
+
+ if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
+ CONVERT (subsection_len);
+
+ if (p - chunk < (ptrdiff_t) subsection_len)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: invalid length in attribute subsection\n"),
+ idx, section_name (ebl, idx), pos (q));
+ break;
+ }
+
+ const unsigned char *subsection_end = chunk + subsection_len;
+ chunk = q;
+ q = subsection_end;
+
+ if (subsection_tag != 1) /* Tag_File */
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"),
+ idx, section_name (ebl, idx), pos (chunk), subsection_tag);
+ else
+ {
+ chunk += sizeof subsection_len;
+ while (chunk < q)
+ {
+ unsigned int tag;
+ get_uleb128 (tag, chunk);
+
+ uint64_t value = 0;
+ const unsigned char *r = chunk;
+ if (tag == 32 || (tag & 1) == 0)
+ {
+ get_uleb128 (value, r);
+ if (r > q)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: endless ULEB128 in attribute tag\n"),
+ idx, section_name (ebl, idx), pos (chunk));
+ break;
+ }
+ }
+ if (tag == 32 || (tag & 1) != 0)
+ {
+ r = memchr (r, '\0', q - r);
+ if (r == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unterminated string in attribute\n"),
+ idx, section_name (ebl, idx), pos (chunk));
+ break;
+ }
+ ++r;
+ }
+
+ const char *tag_name = NULL;
+ const char *value_name = NULL;
+ if (!ebl_check_object_attribute (ebl, (const char *) name,
+ tag, value,
+ &tag_name, &value_name))
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unrecognized attribute tag %u\n"),
+ idx, section_name (ebl, idx), pos (chunk), tag);
+ else if ((tag & 1) == 0 && value_name == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unrecognized %s attribute value %" PRIu64 "\n"),
+ idx, section_name (ebl, idx), pos (chunk),
+ tag_name, value);
+
+ chunk = r;
+ }
+ }
+ }
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: vendor '%s' unknown\n"),
+ idx, section_name (ebl, idx), pos (p), name);
+ }
+
+ if (left () != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: extra bytes after last attribute section\n"),
+ idx, section_name (ebl, idx), pos (p));
+}
+
+static bool has_loadable_segment;
+static bool has_interp_segment;
+
+static const struct
+{
+ const char *name;
+ size_t namelen;
+ GElf_Word type;
+ enum { unused, exact, atleast, exact_or_gnuld } attrflag;
+ GElf_Word attr;
+ GElf_Word attr2;
+} special_sections[] =
+ {
+ /* See figure 4-14 in the gABI. */
+ { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".comment", 8, SHT_PROGBITS, exact, 0, 0 },
+ { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".debug_str", 11, SHT_PROGBITS, exact_or_gnuld, SHF_MERGE | SHF_STRINGS, 0 },
+ { ".debug", 6, SHT_PROGBITS, exact, 0, 0 },
+ { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE },
+ { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 },
+ { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 },
+ { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
+ { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info?
+ { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 },
+ { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
+ { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests?
+ { ".line", 6, SHT_PROGBITS, exact, 0, 0 },
+ { ".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
+ { ".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 },
+ { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
+ { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
+ { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
+ { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
+
+ /* The following are GNU extensions. */
+ { ".gnu.version", 13, SHT_GNU_versym, exact, SHF_ALLOC, 0 },
+ { ".gnu.version_d", 15, SHT_GNU_verdef, exact, SHF_ALLOC, 0 },
+ { ".gnu.version_r", 15, SHT_GNU_verneed, exact, SHF_ALLOC, 0 },
+ { ".gnu.attributes", 16, SHT_GNU_ATTRIBUTES, exact, 0, 0 },
+ };
+#define nspecial_sections \
+ (sizeof (special_sections) / sizeof (special_sections[0]))
+
+#define IS_KNOWN_SPECIAL(idx, string, prefix) \
+ (special_sections[idx].namelen == sizeof string - (prefix ? 1 : 0) \
+ && !memcmp (special_sections[idx].name, string, \
+ sizeof string - (prefix ? 1 : 0)))
+
+static void
+check_sections (Ebl *ebl, GElf_Ehdr *ehdr)
+{
if (ehdr->e_shoff == 0)
/* No section header. */
return;
@@ -1947,7 +3372,8 @@ check_sections (Ebl *ebl, GElf_Ehdr *ehdr)
/* Check the zeroth section first. It must not have any contents
and the section header must contain nonzero value at most in the
sh_size and sh_link fields. */
- shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
if (shdr == NULL)
ERROR (gettext ("cannot get section header of zeroth section\n"));
else
@@ -1978,17 +3404,22 @@ zeroth section has nonzero size value while ELF header has nonzero shnum value\n
zeroth section has nonzero link value while ELF header does not signal overflow in shstrndx\n"));
}
- for (cnt = 1; cnt < shnum; ++cnt)
- {
- Elf_Scn *scn;
+ int *segment_flags = xcalloc (ehdr->e_phnum, sizeof segment_flags[0]);
- scn = elf_getscn (ebl->elf, cnt);
- shdr = gelf_getshdr (scn, &shdr_mem);
+ bool dot_interp_section = false;
+
+ size_t hash_idx = 0;
+ size_t gnu_hash_idx = 0;
+
+ size_t versym_scnndx = 0;
+ for (size_t cnt = 1; cnt < shnum; ++cnt)
+ {
+ shdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &shdr_mem);
if (shdr == NULL)
{
ERROR (gettext ("\
cannot get section header for section [%2zu] '%s': %s\n"),
- cnt, section_name (ebl, ehdr, cnt), elf_errmsg (-1));
+ cnt, section_name (ebl, cnt), elf_errmsg (-1));
continue;
}
@@ -2009,7 +3440,19 @@ cannot get section header for section [%2zu] '%s': %s\n"),
char stbuf2[100];
char stbuf3[100];
- if (shdr->sh_type != special_sections[s].type)
+ GElf_Word good_type = special_sections[s].type;
+ if (IS_KNOWN_SPECIAL (s, ".plt", false)
+ && ebl_bss_plt_p (ebl, ehdr))
+ good_type = SHT_NOBITS;
+
+ /* In a debuginfo file, any normal section can be SHT_NOBITS.
+ This is only invalid for DWARF sections and .shstrtab. */
+ if (shdr->sh_type != good_type
+ && (shdr->sh_type != SHT_NOBITS
+ || !is_debuginfo
+ || IS_KNOWN_SPECIAL (s, ".debug_str", false)
+ || IS_KNOWN_SPECIAL (s, ".debug", true)
+ || IS_KNOWN_SPECIAL (s, ".shstrtab", false)))
ERROR (gettext ("\
section [%2d] '%s' has wrong type: expected %s, is %s\n"),
(int) cnt, scnname,
@@ -2018,12 +3461,14 @@ section [%2d] '%s' has wrong type: expected %s, is %s\n"),
ebl_section_type_name (ebl, shdr->sh_type,
stbuf2, sizeof (stbuf2)));
- if (special_sections[s].attrflag == exact)
+ if (special_sections[s].attrflag == exact
+ || special_sections[s].attrflag == exact_or_gnuld)
{
/* Except for the link order and group bit all the
other bits should match exactly. */
if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP))
- != special_sections[s].attr)
+ != special_sections[s].attr
+ && (special_sections[s].attrflag == exact || !gnuld))
ERROR (gettext ("\
section [%2zu] '%s' has wrong flags: expected %s, is %s\n"),
cnt, scnname,
@@ -2115,33 +3560,51 @@ section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n
if (shdr->sh_entsize != 0 && shdr->sh_size % shdr->sh_entsize)
ERROR (gettext ("\
section [%2zu] '%s': size not multiple of entry size\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
if (elf_strptr (ebl->elf, shstrndx, shdr->sh_name) == NULL)
ERROR (gettext ("cannot get section header\n"));
if (shdr->sh_type >= SHT_NUM
+ && shdr->sh_type != SHT_GNU_ATTRIBUTES
&& shdr->sh_type != SHT_GNU_LIBLIST
&& shdr->sh_type != SHT_CHECKSUM
&& shdr->sh_type != SHT_GNU_verdef
&& shdr->sh_type != SHT_GNU_verneed
- && shdr->sh_type != SHT_GNU_versym)
- ERROR (gettext ("unsupported section type %d\n"), (int) shdr->sh_type);
+ && shdr->sh_type != SHT_GNU_versym
+ && ebl_section_type_name (ebl, shdr->sh_type, NULL, 0) == NULL)
+ ERROR (gettext ("section [%2zu] '%s' has unsupported type %d\n"),
+ cnt, section_name (ebl, cnt),
+ (int) shdr->sh_type);
#define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \
| SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \
| SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS)
- if (shdr->sh_flags & ~ALL_SH_FLAGS)
- ERROR (gettext ("section [%2zu] '%s' contain unknown flag(s) %d\n"),
- cnt, section_name (ebl, ehdr, cnt),
- (int) shdr->sh_flags & ~ALL_SH_FLAGS);
- else if (shdr->sh_flags & SHF_TLS)
+ if (shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS)
+ {
+ GElf_Xword sh_flags = shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS;
+ if (sh_flags & SHF_MASKPROC)
+ {
+ if (!ebl_machine_section_flag_check (ebl,
+ sh_flags & SHF_MASKPROC))
+ ERROR (gettext ("section [%2zu] '%s'"
+ " contains invalid processor-specific flag(s)"
+ " %#" PRIx64 "\n"),
+ cnt, section_name (ebl, cnt), sh_flags & SHF_MASKPROC);
+ sh_flags &= ~(GElf_Xword) SHF_MASKPROC;
+ }
+ if (sh_flags != 0)
+ ERROR (gettext ("section [%2zu] '%s' contains unknown flag(s)"
+ " %#" PRIx64 "\n"),
+ cnt, section_name (ebl, cnt), sh_flags);
+ }
+ if (shdr->sh_flags & SHF_TLS)
{
// XXX Correct?
if (shdr->sh_addr != 0 && !gnuld)
ERROR (gettext ("\
section [%2zu] '%s': thread-local data sections address not zero\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
// XXX TODO more tests!?
}
@@ -2149,27 +3612,52 @@ section [%2zu] '%s': thread-local data sections address not zero\n"),
if (shdr->sh_link >= shnum)
ERROR (gettext ("\
section [%2zu] '%s': invalid section reference in link value\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
if (SH_INFO_LINK_P (shdr) && shdr->sh_info >= shnum)
ERROR (gettext ("\
section [%2zu] '%s': invalid section reference in info value\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
if ((shdr->sh_flags & SHF_MERGE) == 0
&& (shdr->sh_flags & SHF_STRINGS) != 0
&& be_strict)
ERROR (gettext ("\
section [%2zu] '%s': strings flag set without merge flag\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
if ((shdr->sh_flags & SHF_MERGE) != 0 && shdr->sh_entsize == 0)
ERROR (gettext ("\
section [%2zu] '%s': merge flag set but entry size is zero\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
if (shdr->sh_flags & SHF_GROUP)
- check_scn_group (ebl, ehdr, cnt);
+ check_scn_group (ebl, cnt);
+
+ if (shdr->sh_flags & SHF_EXECINSTR)
+ {
+ switch (shdr->sh_type)
+ {
+ case SHT_PROGBITS:
+ break;
+
+ case SHT_NOBITS:
+ if (is_debuginfo)
+ break;
+ default:
+ ERROR (gettext ("\
+section [%2zu] '%s' has unexpected type %d for an executable section\n"),
+ cnt, section_name (ebl, cnt), shdr->sh_type);
+ break;
+ }
+
+ if ((shdr->sh_flags & SHF_WRITE)
+ && !ebl_check_special_section (ebl, cnt, shdr,
+ section_name (ebl, cnt)))
+ ERROR (gettext ("\
+section [%2zu] '%s' is both executable and writable\n"),
+ cnt, section_name (ebl, cnt));
+ }
if (ehdr->e_type != ET_REL && (shdr->sh_flags & SHF_ALLOC) != 0)
{
@@ -2186,28 +3674,55 @@ section [%2zu] '%s': merge flag set but entry size is zero\n"),
|| (phdr->p_type == PT_TLS
&& (shdr->sh_flags & SHF_TLS) != 0))
&& phdr->p_offset <= shdr->sh_offset
- && phdr->p_offset + phdr->p_memsz > shdr->sh_offset)
+ && (phdr->p_offset + phdr->p_filesz > shdr->sh_offset
+ || (phdr->p_offset + phdr->p_memsz > shdr->sh_offset
+ && shdr->sh_type == SHT_NOBITS)))
{
/* Found the segment. */
if (phdr->p_offset + phdr->p_memsz
< shdr->sh_offset + shdr->sh_size)
ERROR (gettext ("\
section [%2zu] '%s' not fully contained in segment of program header entry %d\n"),
- cnt, section_name (ebl, ehdr, cnt), pcnt);
+ cnt, section_name (ebl, cnt), pcnt);
if (shdr->sh_type == SHT_NOBITS)
{
- if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz)
+ if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz
+ && !is_debuginfo)
ERROR (gettext ("\
section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d\n"),
- cnt, section_name (ebl, ehdr, cnt), pcnt);
+ cnt, section_name (ebl, cnt), pcnt);
}
else
{
- if (shdr->sh_offset >= phdr->p_offset + phdr->p_filesz)
+ const GElf_Off end = phdr->p_offset + phdr->p_filesz;
+ if (shdr->sh_offset > end ||
+ (shdr->sh_offset == end && shdr->sh_size != 0))
ERROR (gettext ("\
section [%2zu] '%s' has not type NOBITS but is not read from the file in segment of program header entry %d\n"),
- cnt, section_name (ebl, ehdr, cnt), pcnt);
+ cnt, section_name (ebl, cnt), pcnt);
+ }
+
+ if (shdr->sh_type != SHT_NOBITS)
+ {
+ if ((shdr->sh_flags & SHF_EXECINSTR) != 0)
+ {
+ segment_flags[pcnt] |= PF_X;
+ if ((phdr->p_flags & PF_X) == 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' is executable in nonexecutable segment %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ }
+
+ if ((shdr->sh_flags & SHF_WRITE) != 0)
+ {
+ segment_flags[pcnt] |= PF_W;
+ if (0 /* XXX vdso images have this */
+ && (phdr->p_flags & PF_W) == 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' is writable in unwritable segment %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ }
}
break;
@@ -2216,51 +3731,83 @@ section [%2zu] '%s' has not type NOBITS but is not read from the file in segment
if (pcnt == ehdr->e_phnum)
ERROR (gettext ("\
section [%2zu] '%s': alloc flag set but section not in any loaded segment\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
}
if (cnt == shstrndx && shdr->sh_type != SHT_STRTAB)
ERROR (gettext ("\
section [%2zu] '%s': ELF header says this is the section header string table but type is not SHT_TYPE\n"),
- cnt, section_name (ebl, ehdr, cnt));
+ cnt, section_name (ebl, cnt));
switch (shdr->sh_type)
{
- case SHT_SYMTAB:
case SHT_DYNSYM:
- check_symtab (ebl, ehdr, cnt);
+ if (ehdr->e_type == ET_REL)
+ ERROR (gettext ("\
+section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
+ cnt, section_name (ebl, cnt));
+ /* FALLTHROUGH */
+ case SHT_SYMTAB:
+ check_symtab (ebl, ehdr, shdr, cnt);
break;
case SHT_RELA:
- check_rela (ebl, ehdr, cnt);
+ check_rela (ebl, ehdr, shdr, cnt);
break;
case SHT_REL:
- check_rel (ebl, ehdr, cnt);
+ check_rel (ebl, ehdr, shdr, cnt);
break;
case SHT_DYNAMIC:
- check_dynamic (ebl, ehdr, cnt);
+ check_dynamic (ebl, ehdr, shdr, cnt);
break;
case SHT_SYMTAB_SHNDX:
- check_symtab_shndx (ebl, ehdr, cnt);
+ check_symtab_shndx (ebl, ehdr, shdr, cnt);
break;
case SHT_HASH:
- check_hash (ebl, ehdr, cnt);
+ check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt);
+ hash_idx = cnt;
+ break;
+
+ case SHT_GNU_HASH:
+ check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt);
+ gnu_hash_idx = cnt;
break;
case SHT_NULL:
- check_null (ebl, ehdr, shdr, cnt);
+ check_null (ebl, shdr, cnt);
break;
case SHT_GROUP:
check_group (ebl, ehdr, shdr, cnt);
break;
+ case SHT_NOTE:
+ check_note_section (ebl, ehdr, shdr, cnt);
+ break;
+
case SHT_GNU_versym:
- check_versym (ebl, ehdr, shdr, cnt);
+ /* We cannot process this section now since we have no guarantee
+ that the verneed and verdef sections have already been read.
+ Just remember the section index. */
+ if (versym_scnndx != 0)
+ ERROR (gettext ("more than one version symbol table present\n"));
+ versym_scnndx = cnt;
+ break;
+
+ case SHT_GNU_verneed:
+ check_verneed (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_GNU_verdef:
+ check_verdef (ebl, shdr, cnt);
+ break;
+
+ case SHT_GNU_ATTRIBUTES:
+ check_attributes (ebl, ehdr, shdr, cnt);
break;
default:
@@ -2272,151 +3819,210 @@ section [%2zu] '%s': ELF header says this is the section header string table but
if (has_interp_segment && !dot_interp_section)
ERROR (gettext ("INTERP program header entry but no .interp section\n"));
- free (scnref);
-}
-
-
-static void
-check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt)
-{
- if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
- && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
- ERROR (gettext ("\
-phdr[%d]: no note entries defined for the type of file\n"),
- cnt);
+ if (!is_debuginfo)
+ for (int pcnt = 0; pcnt < ehdr->e_phnum; ++pcnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
+ if (phdr != NULL && (phdr->p_type == PT_LOAD || phdr->p_type == PT_TLS))
+ {
+ if ((phdr->p_flags & PF_X) != 0
+ && (segment_flags[pcnt] & PF_X) == 0)
+ ERROR (gettext ("\
+loadable segment [%u] is executable but contains no executable sections\n"),
+ pcnt);
- char *notemem = gelf_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz);
+ if ((phdr->p_flags & PF_W) != 0
+ && (segment_flags[pcnt] & PF_W) == 0)
+ ERROR (gettext ("\
+loadable segment [%u] is writable but contains no writable sections\n"),
+ pcnt);
+ }
+ }
- /* ELF64 files often use note section entries in the 32-bit format.
- The p_align field is set to 8 in case the 64-bit format is used.
- In case the p_align value is 0 or 4 the 32-bit format is
- used. */
- GElf_Xword align = phdr->p_align == 0 || phdr->p_align == 4 ? 4 : 8;
-#define ALIGNED_LEN(len) (((len) + align - 1) & ~(align - 1))
+ free (segment_flags);
- GElf_Xword idx = 0;
- while (idx < phdr->p_filesz)
+ if (version_namelist != NULL)
{
- uint64_t namesz;
- uint64_t descsz;
- uint64_t type;
- uint32_t namesz32;
- uint32_t descsz32;
+ if (versym_scnndx == 0)
+ ERROR (gettext ("\
+no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\n"));
+ else
+ check_versym (ebl, versym_scnndx);
- if (align == 4)
+ /* Check for duplicate index numbers. */
+ do
{
- uint32_t *ptr = (uint32_t *) (notemem + idx);
-
- if ((__BYTE_ORDER == __LITTLE_ENDIAN
- && ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- || (__BYTE_ORDER == __BIG_ENDIAN
- && ehdr->e_ident[EI_DATA] == ELFDATA2LSB))
- {
- namesz32 = namesz = bswap_32 (*ptr);
- ++ptr;
- descsz32 = descsz = bswap_32 (*ptr);
- ++ptr;
- type = bswap_32 (*ptr);
- }
- else
+ struct version_namelist *runp = version_namelist->next;
+ while (runp != NULL)
{
- namesz32 = namesz = *ptr++;
- descsz32 = descsz = *ptr++;
- type = *ptr;
+ if (version_namelist->ndx == runp->ndx)
+ {
+ ERROR (gettext ("duplicate version index %d\n"),
+ (int) version_namelist->ndx);
+ break;
+ }
+ runp = runp->next;
}
+
+ struct version_namelist *old = version_namelist;
+ version_namelist = version_namelist->next;
+ free (old);
}
- else
- {
- uint64_t *ptr = (uint64_t *) (notemem + idx);
- uint32_t *ptr32 = (uint32_t *) (notemem + idx);
+ while (version_namelist != NULL);
+ }
+ else if (versym_scnndx != 0)
+ ERROR (gettext ("\
+.gnu.versym section present without .gnu.versym_d or .gnu.versym_r\n"));
- if ((__BYTE_ORDER == __LITTLE_ENDIAN
- && ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
- || (__BYTE_ORDER == __BIG_ENDIAN
- && ehdr->e_ident[EI_DATA] == ELFDATA2LSB))
- {
- namesz = bswap_64 (*ptr);
- ++ptr;
- descsz = bswap_64 (*ptr);
- ++ptr;
- type = bswap_64 (*ptr);
-
- namesz32 = bswap_32 (*ptr32);
- ++ptr32;
- descsz32 = bswap_32 (*ptr32);
- }
- else
- {
- namesz = *ptr++;
- descsz = *ptr++;
- type = *ptr;
+ if (hash_idx != 0 && gnu_hash_idx != 0)
+ compare_hash_gnu_hash (ebl, ehdr, hash_idx, gnu_hash_idx);
- namesz32 = *ptr32++;
- descsz32 = *ptr32;
- }
- }
+ free (scnref);
+}
- if (idx + 3 * align > phdr->p_filesz
- || (idx + 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz)
- > phdr->p_filesz))
- {
- if (ehdr->e_ident[EI_CLASS] == ELFCLASS64
- && idx + 3 * 4 <= phdr->p_filesz
- && (idx + 3 * 4 + ALIGNED_LEN (namesz32) + ALIGNED_LEN (descsz32)
- <= phdr->p_filesz))
- ERROR (gettext ("\
-phdr[%d]: note entries probably in form of a 32-bit ELF file\n"), cnt);
- else
- ERROR (gettext ("phdr[%d]: extra %zu bytes after last note\n"),
- cnt, (size_t) (phdr->p_filesz - idx));
- break;
- }
+
+static GElf_Off
+check_note_data (Ebl *ebl, const GElf_Ehdr *ehdr,
+ Elf_Data *data, int shndx, int phndx, GElf_Off start)
+{
+ size_t offset = 0;
+ size_t last_offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < data->d_size
+ && (offset = gelf_getnote (data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ last_offset = offset;
/* Make sure it is one of the note types we know about. */
if (ehdr->e_type == ET_CORE)
- {
- switch (type)
- {
- case NT_PRSTATUS:
- case NT_FPREGSET:
- case NT_PRPSINFO:
- case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */
- case NT_PLATFORM:
- case NT_AUXV:
- case NT_GWINDOWS:
- case NT_ASRS:
- case NT_PSTATUS:
- case NT_PSINFO:
- case NT_PRCRED:
- case NT_UTSNAME:
- case NT_LWPSTATUS:
- case NT_LWPSINFO:
- case NT_PRFPXREG:
- /* Known type. */
+ switch (nhdr.n_type)
+ {
+ case NT_PRSTATUS:
+ case NT_FPREGSET:
+ case NT_PRPSINFO:
+ case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */
+ case NT_PLATFORM:
+ case NT_AUXV:
+ case NT_GWINDOWS:
+ case NT_ASRS:
+ case NT_PSTATUS:
+ case NT_PSINFO:
+ case NT_PRCRED:
+ case NT_UTSNAME:
+ case NT_LWPSTATUS:
+ case NT_LWPSINFO:
+ case NT_PRFPXREG:
+ /* Known type. */
+ break;
+
+ default:
+ if (shndx == 0)
+ ERROR (gettext ("\
+phdr[%d]: unknown core file note type %" PRIu32 " at offset %" PRIu64 "\n"),
+ phndx, (uint32_t) nhdr.n_type, start + offset);
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': unknown core file note type %" PRIu32
+ " at offset %Zu\n"),
+ shndx, section_name (ebl, shndx),
+ (uint32_t) nhdr.n_type, offset);
+ }
+ else
+ switch (nhdr.n_type)
+ {
+ case NT_GNU_ABI_TAG:
+ case NT_GNU_HWCAP:
+ case NT_GNU_BUILD_ID:
+ break;
+
+ case 0:
+ /* Linux vDSOs use a type 0 note for the kernel version word. */
+ if (nhdr.n_namesz == sizeof "Linux"
+ && !memcmp (data->d_buf + name_offset, "Linux", sizeof "Linux"))
break;
- default:
+ default:
+ if (shndx == 0)
ERROR (gettext ("\
-phdr[%d]: unknown core file note type %" PRIu64 " at offset %" PRIu64 "\n"),
- cnt, type, idx);
- }
- }
- else
- {
- if (type != NT_VERSION)
- ERROR (gettext ("\
-phdr[%d]: unknown object file note type %" PRIu64 " at offset %" PRIu64 "\n"),
- cnt, type, idx);
- }
+phdr[%d]: unknown object file note type %" PRIu32 " at offset %Zu\n"),
+ phndx, (uint32_t) nhdr.n_type, offset);
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': unknown object file note type %" PRIu32
+ " at offset %Zu\n"),
+ shndx, section_name (ebl, shndx),
+ (uint32_t) nhdr.n_type, offset);
+ }
+ }
- /* Move to the next entry. */
- idx += 3 * align + ALIGNED_LEN (namesz) + ALIGNED_LEN (descsz);
+ return last_offset;
+}
- }
+static void
+check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt)
+{
+ if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
+ && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ ERROR (gettext ("\
+phdr[%d]: no note entries defined for the type of file\n"),
+ cnt);
+
+ if (is_debuginfo)
+ /* The p_offset values in a separate debug file are bogus. */
+ return;
- gelf_freechunk (ebl->elf, notemem);
+ if (phdr->p_filesz == 0)
+ return;
+
+ GElf_Off notes_size = 0;
+ Elf_Data *data = elf_getdata_rawchunk (ebl->elf,
+ phdr->p_offset, phdr->p_filesz,
+ ELF_T_NHDR);
+ if (data != NULL)
+ notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset);
+
+ if (notes_size == 0)
+ ERROR (gettext ("phdr[%d]: cannot get content of note section: %s\n"),
+ cnt, elf_errmsg (-1));
+ else if (notes_size != phdr->p_filesz)
+ ERROR (gettext ("phdr[%d]: extra %" PRIu64 " bytes after last note\n"),
+ cnt, phdr->p_filesz - notes_size);
}
+static void
+check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (shdr->sh_size == 0)
+ return;
+
+ 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 (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
+ && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ ERROR (gettext ("\
+section [%2d] '%s': no note entries defined for the type of file\n"),
+ idx, section_name (ebl, idx));
+
+ GElf_Off notes_size = check_note_data (ebl, ehdr, data, idx, 0, 0);
+
+ if (notes_size == 0)
+ ERROR (gettext ("section [%2d] '%s': cannot get content of note section\n"),
+ idx, section_name (ebl, idx));
+ else if (notes_size != shdr->sh_size)
+ ERROR (gettext ("section [%2d] '%s': extra %" PRIu64
+ " bytes after last note\n"),
+ idx, section_name (ebl, idx), shdr->sh_size - notes_size);
+}
static void
check_program_header (Ebl *ebl, GElf_Ehdr *ehdr)
@@ -2447,10 +4053,12 @@ only executables, shared objects, and core files can have program headers\n"));
}
if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME
- && phdr->p_type != PT_GNU_STACK)
+ && phdr->p_type != PT_GNU_STACK && phdr->p_type != PT_GNU_RELRO
+ /* Check for a known machine-specific type. */
+ && ebl_segment_type_name (ebl, phdr->p_type, NULL, 0) == NULL)
ERROR (gettext ("\
-program header entry %d: unknown program header entry type\n"),
- cnt);
+program header entry %d: unknown program header entry type %#" PRIx64 "\n"),
+ cnt, (uint64_t) phdr->p_type);
if (phdr->p_type == PT_LOAD)
has_loadable_segment = true;
@@ -2471,9 +4079,33 @@ more than one INTERP entry in program header\n"));
}
else if (phdr->p_type == PT_NOTE)
check_note (ebl, ehdr, phdr, cnt);
- else if (phdr->p_type == PT_DYNAMIC
- && ehdr->e_type == ET_EXEC && ! has_interp_segment)
- ERROR (gettext ("static executable cannot have dynamic sections\n"));
+ else if (phdr->p_type == PT_DYNAMIC)
+ {
+ if (ehdr->e_type == ET_EXEC && ! has_interp_segment)
+ ERROR (gettext ("\
+static executable cannot have dynamic sections\n"));
+ else
+ {
+ /* Check that the .dynamic section, if it exists, has
+ the same address. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)
+ {
+ if (phdr->p_offset != shdr->sh_offset)
+ ERROR (gettext ("\
+dynamic section reference in program header has wrong offset\n"));
+ if (phdr->p_memsz != shdr->sh_size)
+ ERROR (gettext ("\
+dynamic section size mismatch in program and section header\n"));
+ break;
+ }
+ }
+ }
+ }
else if (phdr->p_type == PT_GNU_RELRO)
{
if (++num_pt_relro == 2)
@@ -2488,7 +4120,7 @@ more than one GNU_RELRO entry in program header\n"));
GElf_Phdr phdr2_mem;
GElf_Phdr *phdr2;
- phdr2 = gelf_getphdr (ebl->elf, cnt, &phdr2_mem);
+ phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem);
if (phdr2 == NULL)
continue;
@@ -2500,20 +4132,50 @@ more than one GNU_RELRO entry in program header\n"));
if ((phdr2->p_flags & PF_W) == 0)
ERROR (gettext ("\
loadable segment GNU_RELRO applies to is not writable\n"));
- if ((phdr2->p_flags & PF_X) != 0)
+ if ((phdr2->p_flags &~ PF_W) != (phdr->p_flags &~ PF_W))
ERROR (gettext ("\
-loadable segment GNU_RELRO applies to is executable\n"));
+loadable segment [%u] flags do not match GNU_RELRO [%u] flags\n"),
+ cnt, inner);
break;
}
}
if (inner >= ehdr->e_phnum)
ERROR (gettext ("\
-GNU_RELRO segment not contained in a loaded segment\n"));
+%s segment not contained in a loaded segment\n"), "GNU_RELRO");
}
}
+ else if (phdr->p_type == PT_PHDR)
+ {
+ /* Check that the region is in a writable segment. */
+ int inner;
+ for (inner = 0; inner < ehdr->e_phnum; ++inner)
+ {
+ GElf_Phdr phdr2_mem;
+ GElf_Phdr *phdr2;
+
+ phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem);
+ if (phdr2 != NULL
+ && phdr2->p_type == PT_LOAD
+ && phdr->p_vaddr >= phdr2->p_vaddr
+ && (phdr->p_vaddr + phdr->p_memsz
+ <= phdr2->p_vaddr + phdr2->p_memsz))
+ break;
+ }
- if (phdr->p_filesz > phdr->p_memsz)
+ if (inner >= ehdr->e_phnum)
+ ERROR (gettext ("\
+%s segment not contained in a loaded segment\n"), "PHDR");
+
+ /* Check that offset in segment corresponds to offset in ELF
+ header. */
+ if (phdr->p_offset != ehdr->e_phoff)
+ ERROR (gettext ("\
+program header offset in ELF header and PHDR entry do not match"));
+ }
+
+ if (phdr->p_filesz > phdr->p_memsz
+ && (phdr->p_memsz != 0 || phdr->p_type != PT_NOTE))
ERROR (gettext ("\
program header entry %d: file size greater than memory size\n"),
cnt);
@@ -2538,6 +4200,12 @@ process_elf_file (Elf *elf, const char *prefix, const char *suffix,
{
/* Reset variables. */
ndynamic = 0;
+ nverneed = 0;
+ nverdef = 0;
+ textrel = false;
+ needed_textrel = false;
+ has_loadable_segment = false;
+ has_interp_segment = false;
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
@@ -2573,6 +4241,13 @@ process_elf_file (Elf *elf, const char *prefix, const char *suffix,
headers at all. */
check_sections (ebl, ehdr);
+ /* Report if no relocation section needed the text relocation flag. */
+ if (textrel && !needed_textrel)
+ ERROR (gettext ("text relocation flag set but not needed\n"));
+
/* Free the resources. */
ebl_closebackend (ebl);
}
+
+
+#include "debugpred.h"