Blame src/strings.c

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