Blame src/strings.c

Packit 032894
/* Print the strings of printable characters in files.
Packit 032894
   Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 032894
   GNU General Public License for more details.
Packit 032894
Packit 032894
   You should have received a copy of the GNU General Public License
Packit 032894
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <argp.h>
Packit 032894
#include <assert.h>
Packit 032894
#include <ctype.h>
Packit 032894
#include <endian.h>
Packit 032894
#include <errno.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <gelf.h>
Packit 032894
#include <inttypes.h>
Packit 032894
#include <libintl.h>
Packit 032894
#include <locale.h>
Packit 032894
#include <stdbool.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <stdio_ext.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
#include <unistd.h>
Packit 032894
#include <sys/mman.h>
Packit 032894
#include <sys/stat.h>
Packit 032894
Packit 032894
#include <libeu.h>
Packit 032894
#include <system.h>
Packit 032894
#include <printversion.h>
Packit 032894
Packit 032894
#ifndef MAP_POPULATE
Packit 032894
# define MAP_POPULATE 0
Packit 032894
#endif
Packit 032894
Packit 032894
Packit 032894
/* Prototypes of local functions.  */
Packit 032894
static int read_fd (int fd, const char *fname, off_t fdlen);
Packit 032894
static int read_elf (Elf *elf, int fd, const char *fname, off_t fdlen);
Packit 032894
Packit 032894
Packit 032894
/* Name and version of program.  */
Packit 032894
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Packit 032894
Packit 032894
/* Bug report address.  */
Packit 032894
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Packit 032894
Packit 032894
/* Definitions of arguments for argp functions.  */
Packit 032894
static const struct argp_option options[] =
Packit 032894
{
Packit 032894
  { NULL, 0, NULL, 0, N_("Output Selection:"), 0 },
Packit 032894
  { "all", 'a', NULL, 0, N_("Scan entire file, not only loaded sections"), 0 },
Packit 032894
  { "bytes", 'n', "MIN-LEN", 0,
Packit 032894
    N_("Only NUL-terminated sequences of MIN-LEN characters or more are printed"), 0 },
Packit 032894
  { "encoding", 'e', "SELECTOR", 0, N_("\
Packit 032894
Select character size and endianess: s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit"),
Packit 032894
    0},
Packit 032894
  { "print-file-name", 'f', NULL, 0,
Packit 032894
    N_("Print name of the file before each string."), 0 },
Packit 032894
  { "radix", 't', "{o,d,x}", 0,
Packit 032894
    N_("Print location of the string in base 8, 10, or 16 respectively."), 0 },
Packit 032894
  { NULL, 'o', NULL, 0, N_("Alias for --radix=o"), 0 },
Packit 032894
Packit 032894
  { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
Packit 032894
  { NULL, 0, NULL, 0, NULL, 0 }
Packit 032894
};
Packit 032894
Packit 032894
/* Short description of program.  */
Packit 032894
static const char doc[] = N_("\
Packit 032894
Print the strings of printable characters in files.");
Packit 032894
Packit 032894
/* Strings for arguments in help texts.  */
Packit 032894
static const char args_doc[] = N_("[FILE...]");
Packit 032894
Packit 032894
/* Prototype for option handler.  */
Packit 032894
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 032894
Packit 032894
/* Data structure to communicate with argp functions.  */
Packit 032894
static struct argp argp =
Packit 032894
{
Packit 032894
  options, parse_opt, args_doc, doc, NULL, NULL, NULL
Packit 032894
};
Packit 032894
Packit 032894
Packit 032894
/* Global variables.  */
Packit 032894
Packit 032894
/* True if whole file and not only loaded sections are looked at.  */
Packit 032894
static bool entire_file;
Packit 032894
Packit 032894
/* Minimum length of any sequence reported.  */
Packit 032894
static size_t min_len = 4;
Packit 032894
Packit 032894
/* Number of bytes per character.  */
Packit 032894
static size_t bytes_per_char = 1;
Packit 032894
Packit 032894
/* Minimum length of any sequence reported in bytes.  */
Packit 032894
static size_t min_len_bytes;
Packit 032894
Packit 032894
/* True if multibyte characters are in big-endian order.  */
Packit 032894
static bool big_endian;
Packit 032894
Packit 032894
/* True unless 7-bit ASCII are expected.  */
Packit 032894
static bool char_7bit;
Packit 032894
Packit 032894
/* True if file names should be printed before strings.  */
Packit 032894
static bool print_file_name;
Packit 032894
Packit 032894
/* Radix for printed numbers.  */
Packit 032894
static enum
Packit 032894
{
Packit 032894
  radix_none = 0,
Packit 032894
  radix_decimal,
Packit 032894
  radix_hex,
Packit 032894
  radix_octal
Packit 032894
} radix = radix_none;
Packit 032894
Packit 032894
Packit 032894
/* Page size in use.  */
Packit 032894
static size_t ps;
Packit 032894
Packit 032894
Packit 032894
/* Mapped parts of the ELF file.  */
Packit 032894
static unsigned char *elfmap;
Packit 032894
static unsigned char *elfmap_base;
Packit 032894
static size_t elfmap_size;
Packit 032894
static off_t elfmap_off;
Packit 032894
Packit 032894
Packit 032894
int
Packit 032894
main (int argc, char *argv[])
Packit 032894
{
Packit 032894
  /* We use no threads.  */
Packit 032894
  __fsetlocking (stdin, FSETLOCKING_BYCALLER);
Packit 032894
  __fsetlocking (stdout, FSETLOCKING_BYCALLER);
Packit 032894
Packit 032894
  /* Set locale.  */
Packit 032894
  (void) setlocale (LC_ALL, "");
Packit 032894
Packit 032894
  /* Make sure the message catalog can be found.  */
Packit 032894
  (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
Packit 032894
Packit 032894
  /* Initialize the message catalog.  */
Packit 032894
  (void) textdomain (PACKAGE_TARNAME);
Packit 032894
Packit 032894
  /* Parse and process arguments.  */
Packit 032894
  int remaining;
Packit 032894
  (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 032894
Packit 032894
  /* Tell the library which version we are expecting.  */
Packit 032894
  elf_version (EV_CURRENT);
Packit 032894
Packit 032894
  /* Determine the page size.  We will likely need it a couple of times.  */
Packit 032894
  ps = sysconf (_SC_PAGESIZE);
Packit 032894
Packit 032894
  struct stat st;
Packit 032894
  int result = 0;
Packit 032894
  if (remaining == argc)
Packit 032894
    /* We read from standard input.  This we cannot do for a
Packit 032894
       structured file.  */
Packit 032894
    result = read_fd (STDIN_FILENO,
Packit 032894
		      print_file_name ? "{standard input}" : NULL,
Packit 032894
		      (fstat (STDIN_FILENO, &st) == 0 && S_ISREG (st.st_mode))
Packit 032894
		      ? st.st_size : INT64_C (0x7fffffffffffffff));
Packit 032894
  else
Packit 032894
    do
Packit 032894
      {
Packit 032894
	int fd = (strcmp (argv[remaining], "-") == 0
Packit 032894
		  ? STDIN_FILENO : open (argv[remaining], O_RDONLY));
Packit 032894
	if (unlikely (fd == -1))
Packit 032894
	  {
Packit 032894
	    error (0, errno, gettext ("cannot open '%s'"), argv[remaining]);
Packit 032894
	    result = 1;
Packit 032894
	  }
Packit 032894
	else
Packit 032894
	  {
Packit 032894
	    const char *fname = print_file_name ? argv[remaining] : NULL;
Packit 032894
	    int fstat_fail = fstat (fd, &st);
Packit 032894
	    off_t fdlen = (fstat_fail
Packit 032894
			     ? INT64_C (0x7fffffffffffffff) : st.st_size);
Packit 032894
	    if (fdlen > (off_t) min_len_bytes)
Packit 032894
	      {
Packit 032894
		Elf *elf = NULL;
Packit 032894
		if (entire_file
Packit 032894
		    || fstat_fail
Packit 032894
		    || !S_ISREG (st.st_mode)
Packit 032894
		    || (elf = elf_begin (fd, ELF_C_READ, NULL)) == NULL
Packit 032894
		    || elf_kind (elf) != ELF_K_ELF)
Packit 032894
		  result |= read_fd (fd, fname, fdlen);
Packit 032894
		else
Packit 032894
		  result |= read_elf (elf, fd, fname, fdlen);
Packit 032894
Packit 032894
		/* This call will succeed even if ELF is NULL.  */
Packit 032894
		elf_end (elf);
Packit 032894
	      }
Packit 032894
Packit 032894
	    if (strcmp (argv[remaining], "-") != 0)
Packit 032894
	      close (fd);
Packit 032894
	  }
Packit 032894
Packit 032894
	if (elfmap != NULL && elfmap != MAP_FAILED)
Packit 032894
	  munmap (elfmap, elfmap_size);
Packit 032894
	elfmap = NULL;
Packit 032894
      }
Packit 032894
    while (++remaining < argc);
Packit 032894
Packit 032894
  return result;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Handle program arguments.  */
Packit 032894
static error_t
Packit 032894
parse_opt (int key, char *arg,
Packit 032894
	   struct argp_state *state __attribute__ ((unused)))
Packit 032894
{
Packit 032894
  switch (key)
Packit 032894
    {
Packit 032894
    case 'a':
Packit 032894
      entire_file = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'e':
Packit 032894
      /* We expect a string of one character.  */
Packit 032894
      switch (arg[1] != '\0' ? '\0' : arg[0])
Packit 032894
	{
Packit 032894
	case 's':
Packit 032894
	case 'S':
Packit 032894
	  char_7bit = arg[0] == 's';
Packit 032894
	  bytes_per_char = 1;
Packit 032894
	  break;
Packit 032894
Packit 032894
	case 'b':
Packit 032894
	case 'B':
Packit 032894
	  big_endian = true;
Packit 032894
	  FALLTHROUGH;
Packit 032894
Packit 032894
	case 'l':
Packit 032894
	case 'L':
Packit 032894
	  bytes_per_char = isupper (arg[0]) ? 4 : 2;
Packit 032894
	  break;
Packit 032894
Packit 032894
	default:
Packit 032894
	  error (0, 0, gettext ("invalid value '%s' for %s parameter"),
Packit 032894
		 arg, "-e");
Packit 032894
	  argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
Packit 032894
	  return ARGP_ERR_UNKNOWN;
Packit 032894
	}
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'f':
Packit 032894
      print_file_name = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'n':
Packit 032894
      min_len = atoi (arg);
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'o':
Packit 032894
      goto octfmt;
Packit 032894
Packit 032894
    case 't':
Packit 032894
      switch (arg[0])
Packit 032894
	{
Packit 032894
	case 'd':
Packit 032894
	  radix = radix_decimal;
Packit 032894
	  break;
Packit 032894
Packit 032894
	case 'o':
Packit 032894
	octfmt:
Packit 032894
	  radix = radix_octal;
Packit 032894
	  break;
Packit 032894
Packit 032894
	case 'x':
Packit 032894
	  radix = radix_hex;
Packit 032894
	  break;
Packit 032894
Packit 032894
	default:
Packit 032894
	  error (0, 0, gettext ("invalid value '%s' for %s parameter"),
Packit 032894
		 arg, "-t");
Packit 032894
	  argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
Packit 032894
	  return ARGP_ERR_UNKNOWN;
Packit 032894
	}
Packit 032894
      break;
Packit 032894
Packit 032894
    case ARGP_KEY_FINI:
Packit 032894
      /* Compute the length in bytes of any match.  */
Packit 032894
      if (min_len <= 0 || min_len > INT_MAX / bytes_per_char)
Packit 032894
	error (EXIT_FAILURE, 0,
Packit 032894
	       gettext ("invalid minimum length of matched string size"));
Packit 032894
      min_len_bytes = min_len * bytes_per_char;
Packit 032894
      break;
Packit 032894
Packit 032894
    default:
Packit 032894
      return ARGP_ERR_UNKNOWN;
Packit 032894
    }
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static void
Packit 032894
process_chunk_mb (const char *fname, const unsigned char *buf, off_t to,
Packit 032894
		  size_t len, char **unprinted)
Packit 032894
{
Packit 032894
  size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
Packit 032894
  const unsigned char *start = buf;
Packit 032894
  while (len >= bytes_per_char)
Packit 032894
    {
Packit 032894
      uint32_t ch;
Packit 032894
Packit 032894
      if (bytes_per_char == 2)
Packit 032894
	{
Packit 032894
	  if (big_endian)
Packit 032894
	    ch = buf[0] << 8 | buf[1];
Packit 032894
	  else
Packit 032894
	    ch = buf[1] << 8 | buf[0];
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  if (big_endian)
Packit 032894
	    ch = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
Packit 032894
	  else
Packit 032894
	    ch = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
Packit 032894
	}
Packit 032894
Packit 032894
      if (ch <= 255 && (isprint (ch) || ch == '\t'))
Packit 032894
	{
Packit 032894
	  ++buf;
Packit 032894
	  ++curlen;
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  if (curlen >= min_len)
Packit 032894
	    {
Packit 032894
	      /* We found a match.  */
Packit 032894
	      if (unlikely (fname != NULL))
Packit 032894
		{
Packit 032894
		  fputs_unlocked (fname, stdout);
Packit 032894
		  fputs_unlocked (": ", stdout);
Packit 032894
		}
Packit 032894
Packit 032894
	      if (unlikely (radix != radix_none))
Packit 032894
		printf ((radix == radix_octal ? "%7" PRIo64 " "
Packit 032894
			 : (radix == radix_decimal ? "%7" PRId64 " "
Packit 032894
			    : "%7" PRIx64 " ")),
Packit 032894
			(int64_t) to - len - (buf - start));
Packit 032894
Packit 032894
	      if (unlikely (*unprinted != NULL))
Packit 032894
		{
Packit 032894
		  fputs_unlocked (*unprinted, stdout);
Packit 032894
		  free (*unprinted);
Packit 032894
		  *unprinted = NULL;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* There is no sane way of printing the string.  If we
Packit 032894
		 assume the file data is encoded in UCS-2/UTF-16 or
Packit 032894
		 UCS-4/UTF-32 respectively we could covert the string.
Packit 032894
		 But there is no such guarantee.  */
Packit 032894
	      fwrite_unlocked (start, 1, buf - start, stdout);
Packit 032894
	      putc_unlocked ('\n', stdout);
Packit 032894
	    }
Packit 032894
Packit 032894
	  start = ++buf;
Packit 032894
	  curlen =  0;
Packit 032894
Packit 032894
	  if (len <= min_len)
Packit 032894
	    break;
Packit 032894
	}
Packit 032894
Packit 032894
      --len;
Packit 032894
    }
Packit 032894
Packit 032894
  if (curlen != 0)
Packit 032894
    *unprinted = xstrndup ((const char *) start, curlen);
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static void
Packit 032894
process_chunk (const char *fname, const unsigned char *buf, off_t to,
Packit 032894
	       size_t len, char **unprinted)
Packit 032894
{
Packit 032894
  /* We are not going to slow the check down for the 2- and 4-byte
Packit 032894
     encodings.  Handle them special.  */
Packit 032894
  if (unlikely (bytes_per_char != 1))
Packit 032894
    {
Packit 032894
      process_chunk_mb (fname, buf, to, len, unprinted);
Packit 032894
      return;
Packit 032894
    }
Packit 032894
Packit 032894
  size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
Packit 032894
  const unsigned char *start = buf;
Packit 032894
  while (len > 0)
Packit 032894
    {
Packit 032894
      if ((isprint (*buf) || *buf == '\t') && (! char_7bit || *buf <= 127))
Packit 032894
	{
Packit 032894
	  ++buf;
Packit 032894
	  ++curlen;
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  if (curlen >= min_len)
Packit 032894
	    {
Packit 032894
	      /* We found a match.  */
Packit 032894
	      if (likely (fname != NULL))
Packit 032894
		{
Packit 032894
		  fputs_unlocked (fname, stdout);
Packit 032894
		  fputs_unlocked (": ", stdout);
Packit 032894
		}
Packit 032894
Packit 032894
	      if (likely (radix != radix_none))
Packit 032894
		printf ((radix == radix_octal ? "%7" PRIo64 " "
Packit 032894
			 : (radix == radix_decimal ? "%7" PRId64 " "
Packit 032894
			    : "%7" PRIx64 " ")),
Packit 032894
			(int64_t) to - len - (buf - start));
Packit 032894
Packit 032894
	      if (unlikely (*unprinted != NULL))
Packit 032894
		{
Packit 032894
		  fputs_unlocked (*unprinted, stdout);
Packit 032894
		  free (*unprinted);
Packit 032894
		  *unprinted = NULL;
Packit 032894
		}
Packit 032894
	      fwrite_unlocked (start, 1, buf - start, stdout);
Packit 032894
	      putc_unlocked ('\n', stdout);
Packit 032894
	    }
Packit 032894
Packit 032894
	  start = ++buf;
Packit 032894
	  curlen =  0;
Packit 032894
Packit 032894
	  if (len <= min_len)
Packit 032894
	    break;
Packit 032894
	}
Packit 032894
Packit 032894
      --len;
Packit 032894
    }
Packit 032894
Packit 032894
  if (curlen != 0)
Packit 032894
    *unprinted = xstrndup ((const char *) start, curlen);
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Map a file in as large chunks as possible.  */
Packit 032894
static void *
Packit 032894
map_file (int fd, off_t start_off, off_t fdlen, size_t *map_sizep)
Packit 032894
{
Packit 032894
  /* Maximum size we mmap.  We use an #ifdef to avoid overflows on
Packit 032894
     32-bit machines.  64-bit machines these days do not have usable
Packit 032894
     address spaces larger than about 43 bits.  Not that any file
Packit 032894
     should be that large.  */
Packit 032894
# if SIZE_MAX > 0xffffffff
Packit 032894
  const size_t mmap_max = 0x4000000000lu;
Packit 032894
# else
Packit 032894
  const size_t mmap_max = 0x40000000lu;
Packit 032894
# endif
Packit 032894
Packit 032894
  /* Try to mmap the file.  */
Packit 032894
  size_t map_size = MIN ((off_t) mmap_max, fdlen);
Packit 032894
  const size_t map_size_min = MAX (MAX (SIZE_MAX / 16, 2 * ps),
Packit 032894
				   roundup (2 * min_len_bytes + 1, ps));
Packit 032894
  void *mem;
Packit 032894
  while (1)
Packit 032894
    {
Packit 032894
      /* We map the memory for reading only here.  Since we will
Packit 032894
	 always look at every byte of the file it makes sense to
Packit 032894
	 use MAP_POPULATE.  */
Packit 032894
      mem = mmap (NULL, map_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
Packit 032894
		  fd, start_off);
Packit 032894
      if (mem != MAP_FAILED)
Packit 032894
	{
Packit 032894
	  /* We will go through the mapping sequentially.  */
Packit 032894
	  (void) posix_madvise (mem, map_size, POSIX_MADV_SEQUENTIAL);
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
      if (errno != EINVAL && errno != ENOMEM)
Packit 032894
	/* This is an error other than the lack of address space.  */
Packit 032894
	break;
Packit 032894
Packit 032894
      /* Maybe the size of the mapping is too big.  Try again.  */
Packit 032894
      map_size /= 2;
Packit 032894
      if (map_size < map_size_min)
Packit 032894
	/* That size should have fit.  */
Packit 032894
	break;
Packit 032894
    }
Packit 032894
Packit 032894
  *map_sizep = map_size;
Packit 032894
  return mem;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Read the file without mapping.  */
Packit 032894
static int
Packit 032894
read_block_no_mmap (int fd, const char *fname, off_t from, off_t fdlen)
Packit 032894
{
Packit 032894
  char *unprinted = NULL;
Packit 032894
#define CHUNKSIZE 65536
Packit 032894
  unsigned char *buf = xmalloc (CHUNKSIZE + min_len_bytes
Packit 032894
				+ bytes_per_char - 1);
Packit 032894
  size_t ntrailer = 0;
Packit 032894
  int result = 0;
Packit 032894
  while (fdlen > 0)
Packit 032894
    {
Packit 032894
      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + ntrailer,
Packit 032894
					    MIN (fdlen, CHUNKSIZE)));
Packit 032894
      if (n == 0)
Packit 032894
	{
Packit 032894
	  /* There are less than MIN_LEN+1 bytes left so there cannot be
Packit 032894
	     another match.  */
Packit 032894
	  assert (unprinted == NULL || ntrailer == 0);
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
      if (unlikely (n < 0))
Packit 032894
	{
Packit 032894
	  /* Something went wrong.  */
Packit 032894
	  result = 1;
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
Packit 032894
      /* Account for the number of bytes read in this round.  */
Packit 032894
      fdlen -= n;
Packit 032894
Packit 032894
      /* Do not use the signed N value.  Note that the addition cannot
Packit 032894
	 overflow.  */
Packit 032894
      size_t nb = (size_t) n + ntrailer;
Packit 032894
      if (nb >= min_len_bytes)
Packit 032894
	{
Packit 032894
	  /* We only use complete characters.  */
Packit 032894
	  nb &= ~(bytes_per_char - 1);
Packit 032894
Packit 032894
	  process_chunk (fname, buf, from + nb, nb, &unprinted);
Packit 032894
Packit 032894
	  /* If the last bytes of the buffer (modulo the character
Packit 032894
	     size) have been printed we are not copying them.  */
Packit 032894
	  size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
Packit 032894
Packit 032894
	  memmove (buf, buf + nb - to_keep, to_keep);
Packit 032894
	  ntrailer = to_keep;
Packit 032894
	  from += nb;
Packit 032894
	}
Packit 032894
      else
Packit 032894
	ntrailer = nb;
Packit 032894
    }
Packit 032894
Packit 032894
  free (buf);
Packit 032894
Packit 032894
  /* Don't print anything we collected so far.  There is no
Packit 032894
     terminating NUL byte.  */
Packit 032894
  free (unprinted);
Packit 032894
Packit 032894
  return result;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static int
Packit 032894
read_block (int fd, const char *fname, off_t fdlen, off_t from, off_t to)
Packit 032894
{
Packit 032894
  if (elfmap == NULL)
Packit 032894
    {
Packit 032894
      /* We need a completely new mapping.  */
Packit 032894
      elfmap_off = from & ~(ps - 1);
Packit 032894
      elfmap_base = elfmap = map_file (fd, elfmap_off, fdlen, &elfmap_size);
Packit 032894
Packit 032894
      if (unlikely (elfmap == MAP_FAILED))
Packit 032894
	/* Let the kernel know we are going to read everything in sequence.  */
Packit 032894
	(void) posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
Packit 032894
    }
Packit 032894
Packit 032894
  if (unlikely (elfmap == MAP_FAILED))
Packit 032894
    {
Packit 032894
      /* Read from the file descriptor.  For this we must position the
Packit 032894
	 read pointer.  */
Packit 032894
      // XXX Eventually add flag which avoids this if the position
Packit 032894
      // XXX is known to match.
Packit 032894
      if (from != 0 && lseek (fd, from, SEEK_SET) != from)
Packit 032894
	error (EXIT_FAILURE, errno, gettext ("lseek failed"));
Packit 032894
Packit 032894
      return read_block_no_mmap (fd, fname, from, to - from);
Packit 032894
    }
Packit 032894
Packit 032894
  assert ((off_t) min_len_bytes < fdlen);
Packit 032894
Packit 032894
  if (to < (off_t) elfmap_off || from > (off_t) (elfmap_off + elfmap_size))
Packit 032894
    {
Packit 032894
      /* The existing mapping cannot fit at all.  Map the new area.
Packit 032894
	 We always map the full range of ELFMAP_SIZE bytes even if
Packit 032894
	 this extend beyond the end of the file.  The Linux kernel
Packit 032894
	 handles this OK if the access pages are not touched.  */
Packit 032894
      elfmap_off = from & ~(ps - 1);
Packit 032894
      if (mmap (elfmap, elfmap_size, PROT_READ,
Packit 032894
		MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, from)
Packit 032894
	  == MAP_FAILED)
Packit 032894
	error (EXIT_FAILURE, errno, gettext ("re-mmap failed"));
Packit 032894
      elfmap_base = elfmap;
Packit 032894
    }
Packit 032894
Packit 032894
  char *unprinted = NULL;
Packit 032894
Packit 032894
  /* Use the existing mapping as much as possible.  If necessary, map
Packit 032894
     new pages.  */
Packit 032894
  if (from >= (off_t) elfmap_off
Packit 032894
      && from < (off_t) (elfmap_off + elfmap_size))
Packit 032894
    /* There are at least a few bytes in this mapping which we can
Packit 032894
       use.  */
Packit 032894
    process_chunk (fname, elfmap_base + (from - elfmap_off),
Packit 032894
		   MIN (to, (off_t) (elfmap_off + elfmap_size)),
Packit 032894
		   MIN (to, (off_t) (elfmap_off + elfmap_size)) - from,
Packit 032894
		   &unprinted);
Packit 032894
Packit 032894
  if (to > (off_t) (elfmap_off + elfmap_size))
Packit 032894
    {
Packit 032894
      unsigned char *remap_base = elfmap_base;
Packit 032894
      size_t read_now = elfmap_size - (elfmap_base - elfmap);
Packit 032894
Packit 032894
      assert (from >= (off_t) elfmap_off
Packit 032894
	      && from < (off_t) (elfmap_off + elfmap_size));
Packit 032894
      off_t handled_to = elfmap_off + elfmap_size;
Packit 032894
      assert (elfmap == elfmap_base
Packit 032894
	      || (elfmap_base - elfmap
Packit 032894
		  == (ptrdiff_t) ((min_len_bytes + ps - 1) & ~(ps - 1))));
Packit 032894
      if (elfmap == elfmap_base)
Packit 032894
	{
Packit 032894
	  size_t keep_area = (min_len_bytes + ps - 1) & ~(ps - 1);
Packit 032894
	  assert (elfmap_size >= keep_area + ps);
Packit 032894
	  /* The keep area is used for the content of the previous
Packit 032894
	     buffer we have to keep.  This means copying those bytes
Packit 032894
	     and for this we have to make the data writable.  */
Packit 032894
	  if (unlikely (mprotect (elfmap, keep_area, PROT_READ | PROT_WRITE)
Packit 032894
			!= 0))
Packit 032894
	    error (EXIT_FAILURE, errno, gettext ("mprotect failed"));
Packit 032894
Packit 032894
	  elfmap_base = elfmap + keep_area;
Packit 032894
	}
Packit 032894
Packit 032894
      while (1)
Packit 032894
	{
Packit 032894
	  /* Map the rest of the file, eventually again in pieces.
Packit 032894
	     We speed things up with a nice Linux feature.  Note
Packit 032894
	     that we have at least two pages mapped.  */
Packit 032894
	  size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
Packit 032894
Packit 032894
	  assert (read_now >= to_keep);
Packit 032894
	  memmove (elfmap_base - to_keep,
Packit 032894
		   remap_base + read_now - to_keep, to_keep);
Packit 032894
	  remap_base = elfmap_base;
Packit 032894
Packit 032894
	  assert ((elfmap_size - (elfmap_base - elfmap)) % bytes_per_char
Packit 032894
		  == 0);
Packit 032894
	  read_now = MIN (to - handled_to,
Packit 032894
			  (ptrdiff_t) elfmap_size - (elfmap_base - elfmap));
Packit 032894
Packit 032894
	  assert (handled_to % ps == 0);
Packit 032894
	  assert (handled_to % bytes_per_char == 0);
Packit 032894
	  if (mmap (remap_base, read_now, PROT_READ,
Packit 032894
		    MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, handled_to)
Packit 032894
	      == MAP_FAILED)
Packit 032894
	    error (EXIT_FAILURE, errno, gettext ("re-mmap failed"));
Packit 032894
	  elfmap_off = handled_to;
Packit 032894
Packit 032894
	  process_chunk (fname, remap_base - to_keep,
Packit 032894
			 elfmap_off + (read_now & ~(bytes_per_char - 1)),
Packit 032894
			 to_keep + (read_now & ~(bytes_per_char - 1)),
Packit 032894
			 &unprinted);
Packit 032894
	  handled_to += read_now;
Packit 032894
	  if (handled_to >= to)
Packit 032894
	    break;
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  /* Don't print anything we collected so far.  There is no
Packit 032894
     terminating NUL byte.  */
Packit 032894
  free (unprinted);
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static int
Packit 032894
read_fd (int fd, const char *fname, off_t fdlen)
Packit 032894
{
Packit 032894
  return read_block (fd, fname, fdlen, 0, fdlen);
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static int
Packit 032894
read_elf (Elf *elf, int fd, const char *fname, off_t fdlen)
Packit 032894
{
Packit 032894
  assert (fdlen >= 0);
Packit 032894
Packit 032894
  /* We will look at each section separately.  The ELF file is not
Packit 032894
     mmapped.  The libelf implementation will load the needed parts on
Packit 032894
     demand.  Since we only interate over the section header table the
Packit 032894
     memory consumption at this stage is kept minimal.  */
Packit 032894
  Elf_Scn *scn = elf_nextscn (elf, NULL);
Packit 032894
  if (scn == NULL)
Packit 032894
    return read_fd (fd, fname, fdlen);
Packit 032894
Packit 032894
  int result = 0;
Packit 032894
  do
Packit 032894
    {
Packit 032894
      GElf_Shdr shdr_mem;
Packit 032894
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
Packit 032894
      /* Only look in sections which are loaded at runtime and
Packit 032894
	 actually have content.  */
Packit 032894
      if (shdr != NULL && shdr->sh_type != SHT_NOBITS
Packit 032894
	  && (shdr->sh_flags & SHF_ALLOC) != 0)
Packit 032894
	{
Packit 032894
	  if (shdr->sh_offset > (Elf64_Off) fdlen
Packit 032894
	      || fdlen - shdr->sh_offset < shdr->sh_size)
Packit 032894
	    {
Packit 032894
	      size_t strndx = 0;
Packit 032894
	      const char *sname;
Packit 032894
	      if (unlikely (elf_getshdrstrndx (elf, &strndx) < 0))
Packit 032894
		sname = "<unknown>";
Packit 032894
	      else
Packit 032894
		sname = elf_strptr (elf, strndx, shdr->sh_name) ?: "<unknown>";
Packit 032894
	      error (0, 0,
Packit 032894
		     gettext ("Skipping section %zd '%s' data outside file"),
Packit 032894
		     elf_ndxscn (scn), sname);
Packit 032894
	      result = 1;
Packit 032894
	    }
Packit 032894
	  else
Packit 032894
	    result |= read_block (fd, fname, fdlen, shdr->sh_offset,
Packit 032894
				  shdr->sh_offset + shdr->sh_size);
Packit 032894
	}
Packit 032894
    }
Packit 032894
  while ((scn = elf_nextscn (elf, scn)) != NULL);
Packit 032894
Packit 032894
  if (elfmap != NULL && elfmap != MAP_FAILED)
Packit 032894
    munmap (elfmap, elfmap_size);
Packit 032894
  elfmap = NULL;
Packit 032894
Packit 032894
  return result;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
#include "debugpred.h"