1da0c48c4Sopenharmony_ci/* Print the strings of printable characters in files.
2da0c48c4Sopenharmony_ci   Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5da0c48c4Sopenharmony_ci
6da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
7da0c48c4Sopenharmony_ci   it under the terms of the GNU General Public License as published by
8da0c48c4Sopenharmony_ci   the Free Software Foundation; either version 3 of the License, or
9da0c48c4Sopenharmony_ci   (at your option) any later version.
10da0c48c4Sopenharmony_ci
11da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
12da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
13da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14da0c48c4Sopenharmony_ci   GNU General Public License for more details.
15da0c48c4Sopenharmony_ci
16da0c48c4Sopenharmony_ci   You should have received a copy of the GNU General Public License
17da0c48c4Sopenharmony_ci   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18da0c48c4Sopenharmony_ci
19da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
20da0c48c4Sopenharmony_ci# include <config.h>
21da0c48c4Sopenharmony_ci#endif
22da0c48c4Sopenharmony_ci
23da0c48c4Sopenharmony_ci#include <argp.h>
24da0c48c4Sopenharmony_ci#include <assert.h>
25da0c48c4Sopenharmony_ci#include <ctype.h>
26da0c48c4Sopenharmony_ci#include <endian.h>
27da0c48c4Sopenharmony_ci#include <errno.h>
28da0c48c4Sopenharmony_ci#include <fcntl.h>
29da0c48c4Sopenharmony_ci#include <gelf.h>
30da0c48c4Sopenharmony_ci#include <inttypes.h>
31da0c48c4Sopenharmony_ci#include <locale.h>
32da0c48c4Sopenharmony_ci#include <stdbool.h>
33da0c48c4Sopenharmony_ci#include <stdio.h>
34da0c48c4Sopenharmony_ci#include <stdio_ext.h>
35da0c48c4Sopenharmony_ci#include <stdlib.h>
36da0c48c4Sopenharmony_ci#include <string.h>
37da0c48c4Sopenharmony_ci#include <unistd.h>
38da0c48c4Sopenharmony_ci#include <sys/mman.h>
39da0c48c4Sopenharmony_ci#include <sys/stat.h>
40da0c48c4Sopenharmony_ci
41da0c48c4Sopenharmony_ci#include <libeu.h>
42da0c48c4Sopenharmony_ci#include <system.h>
43da0c48c4Sopenharmony_ci#include <printversion.h>
44da0c48c4Sopenharmony_ci
45da0c48c4Sopenharmony_ci#ifndef MAP_POPULATE
46da0c48c4Sopenharmony_ci# define MAP_POPULATE 0
47da0c48c4Sopenharmony_ci#endif
48da0c48c4Sopenharmony_ci
49da0c48c4Sopenharmony_ci
50da0c48c4Sopenharmony_ci/* Prototypes of local functions.  */
51da0c48c4Sopenharmony_cistatic int read_fd (int fd, const char *fname, off_t fdlen);
52da0c48c4Sopenharmony_cistatic int read_elf (Elf *elf, int fd, const char *fname, off_t fdlen);
53da0c48c4Sopenharmony_ci
54da0c48c4Sopenharmony_ci
55da0c48c4Sopenharmony_ci/* Name and version of program.  */
56da0c48c4Sopenharmony_ciARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
57da0c48c4Sopenharmony_ci
58da0c48c4Sopenharmony_ci/* Bug report address.  */
59da0c48c4Sopenharmony_ciARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
60da0c48c4Sopenharmony_ci
61da0c48c4Sopenharmony_ci/* Definitions of arguments for argp functions.  */
62da0c48c4Sopenharmony_cistatic const struct argp_option options[] =
63da0c48c4Sopenharmony_ci{
64da0c48c4Sopenharmony_ci  { NULL, 0, NULL, 0, N_("Output Selection:"), 0 },
65da0c48c4Sopenharmony_ci  { "all", 'a', NULL, 0, N_("Scan entire file, not only loaded sections"), 0 },
66da0c48c4Sopenharmony_ci  { "bytes", 'n', "MIN-LEN", 0,
67da0c48c4Sopenharmony_ci    N_("Only NUL-terminated sequences of MIN-LEN characters or more are printed"), 0 },
68da0c48c4Sopenharmony_ci  { "encoding", 'e', "SELECTOR", 0, N_("\
69da0c48c4Sopenharmony_ciSelect character size and endianness: s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit"),
70da0c48c4Sopenharmony_ci    0},
71da0c48c4Sopenharmony_ci  { "print-file-name", 'f', NULL, 0,
72da0c48c4Sopenharmony_ci    N_("Print name of the file before each string."), 0 },
73da0c48c4Sopenharmony_ci  { "radix", 't', "{o,d,x}", 0,
74da0c48c4Sopenharmony_ci    N_("Print location of the string in base 8, 10, or 16 respectively."), 0 },
75da0c48c4Sopenharmony_ci  { NULL, 'o', NULL, 0, N_("Alias for --radix=o"), 0 },
76da0c48c4Sopenharmony_ci
77da0c48c4Sopenharmony_ci  { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
78da0c48c4Sopenharmony_ci  { NULL, 0, NULL, 0, NULL, 0 }
79da0c48c4Sopenharmony_ci};
80da0c48c4Sopenharmony_ci
81da0c48c4Sopenharmony_ci/* Short description of program.  */
82da0c48c4Sopenharmony_cistatic const char doc[] = N_("\
83da0c48c4Sopenharmony_ciPrint the strings of printable characters in files.");
84da0c48c4Sopenharmony_ci
85da0c48c4Sopenharmony_ci/* Strings for arguments in help texts.  */
86da0c48c4Sopenharmony_cistatic const char args_doc[] = N_("[FILE...]");
87da0c48c4Sopenharmony_ci
88da0c48c4Sopenharmony_ci/* Prototype for option handler.  */
89da0c48c4Sopenharmony_cistatic error_t parse_opt (int key, char *arg, struct argp_state *state);
90da0c48c4Sopenharmony_ci
91da0c48c4Sopenharmony_ci/* Data structure to communicate with argp functions.  */
92da0c48c4Sopenharmony_cistatic struct argp argp =
93da0c48c4Sopenharmony_ci{
94da0c48c4Sopenharmony_ci  options, parse_opt, args_doc, doc, NULL, NULL, NULL
95da0c48c4Sopenharmony_ci};
96da0c48c4Sopenharmony_ci
97da0c48c4Sopenharmony_ci
98da0c48c4Sopenharmony_ci/* Global variables.  */
99da0c48c4Sopenharmony_ci
100da0c48c4Sopenharmony_ci/* True if whole file and not only loaded sections are looked at.  */
101da0c48c4Sopenharmony_cistatic bool entire_file;
102da0c48c4Sopenharmony_ci
103da0c48c4Sopenharmony_ci/* Minimum length of any sequence reported.  */
104da0c48c4Sopenharmony_cistatic size_t min_len = 4;
105da0c48c4Sopenharmony_ci
106da0c48c4Sopenharmony_ci/* Number of bytes per character.  */
107da0c48c4Sopenharmony_cistatic size_t bytes_per_char = 1;
108da0c48c4Sopenharmony_ci
109da0c48c4Sopenharmony_ci/* Minimum length of any sequence reported in bytes.  */
110da0c48c4Sopenharmony_cistatic size_t min_len_bytes;
111da0c48c4Sopenharmony_ci
112da0c48c4Sopenharmony_ci/* True if multibyte characters are in big-endian order.  */
113da0c48c4Sopenharmony_cistatic bool big_endian;
114da0c48c4Sopenharmony_ci
115da0c48c4Sopenharmony_ci/* True unless 7-bit ASCII are expected.  */
116da0c48c4Sopenharmony_cistatic bool char_7bit;
117da0c48c4Sopenharmony_ci
118da0c48c4Sopenharmony_ci/* True if file names should be printed before strings.  */
119da0c48c4Sopenharmony_cistatic bool print_file_name;
120da0c48c4Sopenharmony_ci
121da0c48c4Sopenharmony_ci/* Radix for printed numbers.  */
122da0c48c4Sopenharmony_cistatic enum
123da0c48c4Sopenharmony_ci{
124da0c48c4Sopenharmony_ci  radix_none = 0,
125da0c48c4Sopenharmony_ci  radix_decimal,
126da0c48c4Sopenharmony_ci  radix_hex,
127da0c48c4Sopenharmony_ci  radix_octal
128da0c48c4Sopenharmony_ci} radix = radix_none;
129da0c48c4Sopenharmony_ci
130da0c48c4Sopenharmony_ci
131da0c48c4Sopenharmony_ci/* Page size in use.  */
132da0c48c4Sopenharmony_cistatic size_t ps;
133da0c48c4Sopenharmony_ci
134da0c48c4Sopenharmony_ci
135da0c48c4Sopenharmony_ci/* Mapped parts of the ELF file.  */
136da0c48c4Sopenharmony_cistatic unsigned char *elfmap;
137da0c48c4Sopenharmony_cistatic unsigned char *elfmap_base;
138da0c48c4Sopenharmony_cistatic size_t elfmap_size;
139da0c48c4Sopenharmony_cistatic off_t elfmap_off;
140da0c48c4Sopenharmony_ci
141da0c48c4Sopenharmony_ci
142da0c48c4Sopenharmony_ciint
143da0c48c4Sopenharmony_cimain (int argc, char *argv[])
144da0c48c4Sopenharmony_ci{
145da0c48c4Sopenharmony_ci  /* We use no threads.  */
146da0c48c4Sopenharmony_ci  __fsetlocking (stdin, FSETLOCKING_BYCALLER);
147da0c48c4Sopenharmony_ci  __fsetlocking (stdout, FSETLOCKING_BYCALLER);
148da0c48c4Sopenharmony_ci
149da0c48c4Sopenharmony_ci  /* Set locale.  */
150da0c48c4Sopenharmony_ci  (void) setlocale (LC_ALL, "");
151da0c48c4Sopenharmony_ci
152da0c48c4Sopenharmony_ci  /* Make sure the message catalog can be found.  */
153da0c48c4Sopenharmony_ci  (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
154da0c48c4Sopenharmony_ci
155da0c48c4Sopenharmony_ci  /* Initialize the message catalog.  */
156da0c48c4Sopenharmony_ci  (void) textdomain (PACKAGE_TARNAME);
157da0c48c4Sopenharmony_ci
158da0c48c4Sopenharmony_ci  /* Parse and process arguments.  */
159da0c48c4Sopenharmony_ci  int remaining;
160da0c48c4Sopenharmony_ci  (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
161da0c48c4Sopenharmony_ci
162da0c48c4Sopenharmony_ci  /* Tell the library which version we are expecting.  */
163da0c48c4Sopenharmony_ci  elf_version (EV_CURRENT);
164da0c48c4Sopenharmony_ci
165da0c48c4Sopenharmony_ci  /* Determine the page size.  We will likely need it a couple of times.  */
166da0c48c4Sopenharmony_ci  ps = sysconf (_SC_PAGESIZE);
167da0c48c4Sopenharmony_ci
168da0c48c4Sopenharmony_ci  struct stat st;
169da0c48c4Sopenharmony_ci  int result = 0;
170da0c48c4Sopenharmony_ci  if (remaining == argc)
171da0c48c4Sopenharmony_ci    /* We read from standard input.  This we cannot do for a
172da0c48c4Sopenharmony_ci       structured file.  */
173da0c48c4Sopenharmony_ci    result = read_fd (STDIN_FILENO,
174da0c48c4Sopenharmony_ci		      print_file_name ? "{standard input}" : NULL,
175da0c48c4Sopenharmony_ci		      (fstat (STDIN_FILENO, &st) == 0 && S_ISREG (st.st_mode))
176da0c48c4Sopenharmony_ci		      ? st.st_size : INT64_C (0x7fffffffffffffff));
177da0c48c4Sopenharmony_ci  else
178da0c48c4Sopenharmony_ci    do
179da0c48c4Sopenharmony_ci      {
180da0c48c4Sopenharmony_ci	int fd = (strcmp (argv[remaining], "-") == 0
181da0c48c4Sopenharmony_ci		  ? STDIN_FILENO : open (argv[remaining], O_RDONLY));
182da0c48c4Sopenharmony_ci	if (unlikely (fd == -1))
183da0c48c4Sopenharmony_ci	  {
184da0c48c4Sopenharmony_ci	    error (0, errno, _("cannot open '%s'"), argv[remaining]);
185da0c48c4Sopenharmony_ci	    result = 1;
186da0c48c4Sopenharmony_ci	  }
187da0c48c4Sopenharmony_ci	else
188da0c48c4Sopenharmony_ci	  {
189da0c48c4Sopenharmony_ci	    const char *fname = print_file_name ? argv[remaining] : NULL;
190da0c48c4Sopenharmony_ci	    int fstat_fail = fstat (fd, &st);
191da0c48c4Sopenharmony_ci	    off_t fdlen = (fstat_fail
192da0c48c4Sopenharmony_ci			     ? INT64_C (0x7fffffffffffffff) : st.st_size);
193da0c48c4Sopenharmony_ci	    if (fdlen > (off_t) min_len_bytes)
194da0c48c4Sopenharmony_ci	      {
195da0c48c4Sopenharmony_ci		Elf *elf = NULL;
196da0c48c4Sopenharmony_ci		if (entire_file
197da0c48c4Sopenharmony_ci		    || fstat_fail
198da0c48c4Sopenharmony_ci		    || !S_ISREG (st.st_mode)
199da0c48c4Sopenharmony_ci		    || (elf = elf_begin (fd, ELF_C_READ, NULL)) == NULL
200da0c48c4Sopenharmony_ci		    || elf_kind (elf) != ELF_K_ELF)
201da0c48c4Sopenharmony_ci		  result |= read_fd (fd, fname, fdlen);
202da0c48c4Sopenharmony_ci		else
203da0c48c4Sopenharmony_ci		  result |= read_elf (elf, fd, fname, fdlen);
204da0c48c4Sopenharmony_ci
205da0c48c4Sopenharmony_ci		/* This call will succeed even if ELF is NULL.  */
206da0c48c4Sopenharmony_ci		elf_end (elf);
207da0c48c4Sopenharmony_ci	      }
208da0c48c4Sopenharmony_ci
209da0c48c4Sopenharmony_ci	    if (strcmp (argv[remaining], "-") != 0)
210da0c48c4Sopenharmony_ci	      close (fd);
211da0c48c4Sopenharmony_ci	  }
212da0c48c4Sopenharmony_ci
213da0c48c4Sopenharmony_ci	if (elfmap != NULL && elfmap != MAP_FAILED)
214da0c48c4Sopenharmony_ci	  munmap (elfmap, elfmap_size);
215da0c48c4Sopenharmony_ci	elfmap = NULL;
216da0c48c4Sopenharmony_ci      }
217da0c48c4Sopenharmony_ci    while (++remaining < argc);
218da0c48c4Sopenharmony_ci
219da0c48c4Sopenharmony_ci  return result;
220da0c48c4Sopenharmony_ci}
221da0c48c4Sopenharmony_ci
222da0c48c4Sopenharmony_ci
223da0c48c4Sopenharmony_ci/* Handle program arguments.  */
224da0c48c4Sopenharmony_cistatic error_t
225da0c48c4Sopenharmony_ciparse_opt (int key, char *arg,
226da0c48c4Sopenharmony_ci	   struct argp_state *state __attribute__ ((unused)))
227da0c48c4Sopenharmony_ci{
228da0c48c4Sopenharmony_ci  switch (key)
229da0c48c4Sopenharmony_ci    {
230da0c48c4Sopenharmony_ci    case 'a':
231da0c48c4Sopenharmony_ci      entire_file = true;
232da0c48c4Sopenharmony_ci      break;
233da0c48c4Sopenharmony_ci
234da0c48c4Sopenharmony_ci    case 'e':
235da0c48c4Sopenharmony_ci      /* We expect a string of one character.  */
236da0c48c4Sopenharmony_ci      switch (arg[1] != '\0' ? '\0' : arg[0])
237da0c48c4Sopenharmony_ci	{
238da0c48c4Sopenharmony_ci	case 's':
239da0c48c4Sopenharmony_ci	case 'S':
240da0c48c4Sopenharmony_ci	  char_7bit = arg[0] == 's';
241da0c48c4Sopenharmony_ci	  bytes_per_char = 1;
242da0c48c4Sopenharmony_ci	  break;
243da0c48c4Sopenharmony_ci
244da0c48c4Sopenharmony_ci	case 'b':
245da0c48c4Sopenharmony_ci	case 'B':
246da0c48c4Sopenharmony_ci	  big_endian = true;
247da0c48c4Sopenharmony_ci	  FALLTHROUGH;
248da0c48c4Sopenharmony_ci
249da0c48c4Sopenharmony_ci	case 'l':
250da0c48c4Sopenharmony_ci	case 'L':
251da0c48c4Sopenharmony_ci	  bytes_per_char = isupper (arg[0]) ? 4 : 2;
252da0c48c4Sopenharmony_ci	  break;
253da0c48c4Sopenharmony_ci
254da0c48c4Sopenharmony_ci	default:
255da0c48c4Sopenharmony_ci	  error (0, 0, _("invalid value '%s' for %s parameter"),
256da0c48c4Sopenharmony_ci		 arg, "-e");
257da0c48c4Sopenharmony_ci	  argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
258da0c48c4Sopenharmony_ci	  return ARGP_ERR_UNKNOWN;
259da0c48c4Sopenharmony_ci	}
260da0c48c4Sopenharmony_ci      break;
261da0c48c4Sopenharmony_ci
262da0c48c4Sopenharmony_ci    case 'f':
263da0c48c4Sopenharmony_ci      print_file_name = true;
264da0c48c4Sopenharmony_ci      break;
265da0c48c4Sopenharmony_ci
266da0c48c4Sopenharmony_ci    case 'n':
267da0c48c4Sopenharmony_ci      min_len = atoi (arg);
268da0c48c4Sopenharmony_ci      break;
269da0c48c4Sopenharmony_ci
270da0c48c4Sopenharmony_ci    case 'o':
271da0c48c4Sopenharmony_ci      goto octfmt;
272da0c48c4Sopenharmony_ci
273da0c48c4Sopenharmony_ci    case 't':
274da0c48c4Sopenharmony_ci      switch (arg[0])
275da0c48c4Sopenharmony_ci	{
276da0c48c4Sopenharmony_ci	case 'd':
277da0c48c4Sopenharmony_ci	  radix = radix_decimal;
278da0c48c4Sopenharmony_ci	  break;
279da0c48c4Sopenharmony_ci
280da0c48c4Sopenharmony_ci	case 'o':
281da0c48c4Sopenharmony_ci	octfmt:
282da0c48c4Sopenharmony_ci	  radix = radix_octal;
283da0c48c4Sopenharmony_ci	  break;
284da0c48c4Sopenharmony_ci
285da0c48c4Sopenharmony_ci	case 'x':
286da0c48c4Sopenharmony_ci	  radix = radix_hex;
287da0c48c4Sopenharmony_ci	  break;
288da0c48c4Sopenharmony_ci
289da0c48c4Sopenharmony_ci	default:
290da0c48c4Sopenharmony_ci	  error (0, 0, _("invalid value '%s' for %s parameter"),
291da0c48c4Sopenharmony_ci		 arg, "-t");
292da0c48c4Sopenharmony_ci	  argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
293da0c48c4Sopenharmony_ci	  return ARGP_ERR_UNKNOWN;
294da0c48c4Sopenharmony_ci	}
295da0c48c4Sopenharmony_ci      break;
296da0c48c4Sopenharmony_ci
297da0c48c4Sopenharmony_ci    case ARGP_KEY_FINI:
298da0c48c4Sopenharmony_ci      /* Compute the length in bytes of any match.  */
299da0c48c4Sopenharmony_ci      if (min_len <= 0 || min_len > INT_MAX / bytes_per_char)
300da0c48c4Sopenharmony_ci	error_exit (0, _("invalid minimum length of matched string size"));
301da0c48c4Sopenharmony_ci      min_len_bytes = min_len * bytes_per_char;
302da0c48c4Sopenharmony_ci      break;
303da0c48c4Sopenharmony_ci
304da0c48c4Sopenharmony_ci    default:
305da0c48c4Sopenharmony_ci      return ARGP_ERR_UNKNOWN;
306da0c48c4Sopenharmony_ci    }
307da0c48c4Sopenharmony_ci  return 0;
308da0c48c4Sopenharmony_ci}
309da0c48c4Sopenharmony_ci
310da0c48c4Sopenharmony_ci
311da0c48c4Sopenharmony_cistatic void
312da0c48c4Sopenharmony_ciprocess_chunk_mb (const char *fname, const unsigned char *buf, off_t to,
313da0c48c4Sopenharmony_ci		  size_t len, char **unprinted)
314da0c48c4Sopenharmony_ci{
315da0c48c4Sopenharmony_ci  size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
316da0c48c4Sopenharmony_ci  const unsigned char *start = buf;
317da0c48c4Sopenharmony_ci  while (len >= bytes_per_char)
318da0c48c4Sopenharmony_ci    {
319da0c48c4Sopenharmony_ci      uint32_t ch;
320da0c48c4Sopenharmony_ci
321da0c48c4Sopenharmony_ci      if (bytes_per_char == 2)
322da0c48c4Sopenharmony_ci	{
323da0c48c4Sopenharmony_ci	  if (big_endian)
324da0c48c4Sopenharmony_ci	    ch = buf[0] << 8 | buf[1];
325da0c48c4Sopenharmony_ci	  else
326da0c48c4Sopenharmony_ci	    ch = buf[1] << 8 | buf[0];
327da0c48c4Sopenharmony_ci	}
328da0c48c4Sopenharmony_ci      else
329da0c48c4Sopenharmony_ci	{
330da0c48c4Sopenharmony_ci	  if (big_endian)
331da0c48c4Sopenharmony_ci	    ch = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
332da0c48c4Sopenharmony_ci	  else
333da0c48c4Sopenharmony_ci	    ch = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
334da0c48c4Sopenharmony_ci	}
335da0c48c4Sopenharmony_ci
336da0c48c4Sopenharmony_ci      if (ch <= 255 && (isprint (ch) || ch == '\t'))
337da0c48c4Sopenharmony_ci	{
338da0c48c4Sopenharmony_ci	  ++buf;
339da0c48c4Sopenharmony_ci	  ++curlen;
340da0c48c4Sopenharmony_ci	}
341da0c48c4Sopenharmony_ci      else
342da0c48c4Sopenharmony_ci	{
343da0c48c4Sopenharmony_ci	  if (curlen >= min_len)
344da0c48c4Sopenharmony_ci	    {
345da0c48c4Sopenharmony_ci	      /* We found a match.  */
346da0c48c4Sopenharmony_ci	      if (unlikely (fname != NULL))
347da0c48c4Sopenharmony_ci		{
348da0c48c4Sopenharmony_ci		  fputs_unlocked (fname, stdout);
349da0c48c4Sopenharmony_ci		  fputs_unlocked (": ", stdout);
350da0c48c4Sopenharmony_ci		}
351da0c48c4Sopenharmony_ci
352da0c48c4Sopenharmony_ci	      if (unlikely (radix != radix_none))
353da0c48c4Sopenharmony_ci		printf ((radix == radix_octal ? "%7" PRIo64 " "
354da0c48c4Sopenharmony_ci			 : (radix == radix_decimal ? "%7" PRId64 " "
355da0c48c4Sopenharmony_ci			    : "%7" PRIx64 " ")),
356da0c48c4Sopenharmony_ci			(int64_t) to - len - (buf - start));
357da0c48c4Sopenharmony_ci
358da0c48c4Sopenharmony_ci	      if (unlikely (*unprinted != NULL))
359da0c48c4Sopenharmony_ci		{
360da0c48c4Sopenharmony_ci		  fputs_unlocked (*unprinted, stdout);
361da0c48c4Sopenharmony_ci		  free (*unprinted);
362da0c48c4Sopenharmony_ci		  *unprinted = NULL;
363da0c48c4Sopenharmony_ci		}
364da0c48c4Sopenharmony_ci
365da0c48c4Sopenharmony_ci	      /* There is no sane way of printing the string.  If we
366da0c48c4Sopenharmony_ci		 assume the file data is encoded in UCS-2/UTF-16 or
367da0c48c4Sopenharmony_ci		 UCS-4/UTF-32 respectively we could covert the string.
368da0c48c4Sopenharmony_ci		 But there is no such guarantee.  */
369da0c48c4Sopenharmony_ci	      fwrite_unlocked (start, 1, buf - start, stdout);
370da0c48c4Sopenharmony_ci	      putc_unlocked ('\n', stdout);
371da0c48c4Sopenharmony_ci	    }
372da0c48c4Sopenharmony_ci
373da0c48c4Sopenharmony_ci	  start = ++buf;
374da0c48c4Sopenharmony_ci	  curlen =  0;
375da0c48c4Sopenharmony_ci
376da0c48c4Sopenharmony_ci	  if (len <= min_len)
377da0c48c4Sopenharmony_ci	    break;
378da0c48c4Sopenharmony_ci	}
379da0c48c4Sopenharmony_ci
380da0c48c4Sopenharmony_ci      --len;
381da0c48c4Sopenharmony_ci    }
382da0c48c4Sopenharmony_ci
383da0c48c4Sopenharmony_ci  if (curlen != 0)
384da0c48c4Sopenharmony_ci    *unprinted = xstrndup ((const char *) start, curlen);
385da0c48c4Sopenharmony_ci}
386da0c48c4Sopenharmony_ci
387da0c48c4Sopenharmony_ci
388da0c48c4Sopenharmony_cistatic void
389da0c48c4Sopenharmony_ciprocess_chunk (const char *fname, const unsigned char *buf, off_t to,
390da0c48c4Sopenharmony_ci	       size_t len, char **unprinted)
391da0c48c4Sopenharmony_ci{
392da0c48c4Sopenharmony_ci  /* We are not going to slow the check down for the 2- and 4-byte
393da0c48c4Sopenharmony_ci     encodings.  Handle them special.  */
394da0c48c4Sopenharmony_ci  if (unlikely (bytes_per_char != 1))
395da0c48c4Sopenharmony_ci    {
396da0c48c4Sopenharmony_ci      process_chunk_mb (fname, buf, to, len, unprinted);
397da0c48c4Sopenharmony_ci      return;
398da0c48c4Sopenharmony_ci    }
399da0c48c4Sopenharmony_ci
400da0c48c4Sopenharmony_ci  size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
401da0c48c4Sopenharmony_ci  const unsigned char *start = buf;
402da0c48c4Sopenharmony_ci  while (len > 0)
403da0c48c4Sopenharmony_ci    {
404da0c48c4Sopenharmony_ci      if ((isprint (*buf) || *buf == '\t') && (! char_7bit || *buf <= 127))
405da0c48c4Sopenharmony_ci	{
406da0c48c4Sopenharmony_ci	  ++buf;
407da0c48c4Sopenharmony_ci	  ++curlen;
408da0c48c4Sopenharmony_ci	}
409da0c48c4Sopenharmony_ci      else
410da0c48c4Sopenharmony_ci	{
411da0c48c4Sopenharmony_ci	  if (curlen >= min_len)
412da0c48c4Sopenharmony_ci	    {
413da0c48c4Sopenharmony_ci	      /* We found a match.  */
414da0c48c4Sopenharmony_ci	      if (likely (fname != NULL))
415da0c48c4Sopenharmony_ci		{
416da0c48c4Sopenharmony_ci		  fputs_unlocked (fname, stdout);
417da0c48c4Sopenharmony_ci		  fputs_unlocked (": ", stdout);
418da0c48c4Sopenharmony_ci		}
419da0c48c4Sopenharmony_ci
420da0c48c4Sopenharmony_ci	      if (likely (radix != radix_none))
421da0c48c4Sopenharmony_ci		printf ((radix == radix_octal ? "%7" PRIo64 " "
422da0c48c4Sopenharmony_ci			 : (radix == radix_decimal ? "%7" PRId64 " "
423da0c48c4Sopenharmony_ci			    : "%7" PRIx64 " ")),
424da0c48c4Sopenharmony_ci			(int64_t) to - len - (buf - start));
425da0c48c4Sopenharmony_ci
426da0c48c4Sopenharmony_ci	      if (unlikely (*unprinted != NULL))
427da0c48c4Sopenharmony_ci		{
428da0c48c4Sopenharmony_ci		  fputs_unlocked (*unprinted, stdout);
429da0c48c4Sopenharmony_ci		  free (*unprinted);
430da0c48c4Sopenharmony_ci		  *unprinted = NULL;
431da0c48c4Sopenharmony_ci		}
432da0c48c4Sopenharmony_ci	      fwrite_unlocked (start, 1, buf - start, stdout);
433da0c48c4Sopenharmony_ci	      putc_unlocked ('\n', stdout);
434da0c48c4Sopenharmony_ci	    }
435da0c48c4Sopenharmony_ci
436da0c48c4Sopenharmony_ci	  start = ++buf;
437da0c48c4Sopenharmony_ci	  curlen =  0;
438da0c48c4Sopenharmony_ci
439da0c48c4Sopenharmony_ci	  if (len <= min_len)
440da0c48c4Sopenharmony_ci	    break;
441da0c48c4Sopenharmony_ci	}
442da0c48c4Sopenharmony_ci
443da0c48c4Sopenharmony_ci      --len;
444da0c48c4Sopenharmony_ci    }
445da0c48c4Sopenharmony_ci
446da0c48c4Sopenharmony_ci  if (curlen != 0)
447da0c48c4Sopenharmony_ci    *unprinted = xstrndup ((const char *) start, curlen);
448da0c48c4Sopenharmony_ci}
449da0c48c4Sopenharmony_ci
450da0c48c4Sopenharmony_ci
451da0c48c4Sopenharmony_ci/* Map a file in as large chunks as possible.  */
452da0c48c4Sopenharmony_cistatic void *
453da0c48c4Sopenharmony_cimap_file (int fd, off_t start_off, off_t fdlen, size_t *map_sizep)
454da0c48c4Sopenharmony_ci{
455da0c48c4Sopenharmony_ci  /* Maximum size we mmap.  We use an #ifdef to avoid overflows on
456da0c48c4Sopenharmony_ci     32-bit machines.  64-bit machines these days do not have usable
457da0c48c4Sopenharmony_ci     address spaces larger than about 43 bits.  Not that any file
458da0c48c4Sopenharmony_ci     should be that large.  */
459da0c48c4Sopenharmony_ci# if SIZE_MAX > 0xffffffff
460da0c48c4Sopenharmony_ci  const size_t mmap_max = 0x4000000000lu;
461da0c48c4Sopenharmony_ci# else
462da0c48c4Sopenharmony_ci  const size_t mmap_max = 0x40000000lu;
463da0c48c4Sopenharmony_ci# endif
464da0c48c4Sopenharmony_ci
465da0c48c4Sopenharmony_ci  /* Try to mmap the file.  */
466da0c48c4Sopenharmony_ci  size_t map_size = MIN ((off_t) mmap_max, fdlen);
467da0c48c4Sopenharmony_ci  const size_t map_size_min = MAX (MAX (SIZE_MAX / 16, 2 * ps),
468da0c48c4Sopenharmony_ci				   roundup (2 * min_len_bytes + 1, ps));
469da0c48c4Sopenharmony_ci  void *mem;
470da0c48c4Sopenharmony_ci  while (1)
471da0c48c4Sopenharmony_ci    {
472da0c48c4Sopenharmony_ci      /* We map the memory for reading only here.  Since we will
473da0c48c4Sopenharmony_ci	 always look at every byte of the file it makes sense to
474da0c48c4Sopenharmony_ci	 use MAP_POPULATE.  */
475da0c48c4Sopenharmony_ci      mem = mmap (NULL, map_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
476da0c48c4Sopenharmony_ci		  fd, start_off);
477da0c48c4Sopenharmony_ci      if (mem != MAP_FAILED)
478da0c48c4Sopenharmony_ci	{
479da0c48c4Sopenharmony_ci	  /* We will go through the mapping sequentially.  */
480da0c48c4Sopenharmony_ci	  (void) posix_madvise (mem, map_size, POSIX_MADV_SEQUENTIAL);
481da0c48c4Sopenharmony_ci	  break;
482da0c48c4Sopenharmony_ci	}
483da0c48c4Sopenharmony_ci      if (errno != EINVAL && errno != ENOMEM)
484da0c48c4Sopenharmony_ci	/* This is an error other than the lack of address space.  */
485da0c48c4Sopenharmony_ci	break;
486da0c48c4Sopenharmony_ci
487da0c48c4Sopenharmony_ci      /* Maybe the size of the mapping is too big.  Try again.  */
488da0c48c4Sopenharmony_ci      map_size /= 2;
489da0c48c4Sopenharmony_ci      if (map_size < map_size_min)
490da0c48c4Sopenharmony_ci	/* That size should have fit.  */
491da0c48c4Sopenharmony_ci	break;
492da0c48c4Sopenharmony_ci    }
493da0c48c4Sopenharmony_ci
494da0c48c4Sopenharmony_ci  *map_sizep = map_size;
495da0c48c4Sopenharmony_ci  return mem;
496da0c48c4Sopenharmony_ci}
497da0c48c4Sopenharmony_ci
498da0c48c4Sopenharmony_ci
499da0c48c4Sopenharmony_ci/* Read the file without mapping.  */
500da0c48c4Sopenharmony_cistatic int
501da0c48c4Sopenharmony_ciread_block_no_mmap (int fd, const char *fname, off_t from, off_t fdlen)
502da0c48c4Sopenharmony_ci{
503da0c48c4Sopenharmony_ci  char *unprinted = NULL;
504da0c48c4Sopenharmony_ci#define CHUNKSIZE 65536
505da0c48c4Sopenharmony_ci  unsigned char *buf = xmalloc (CHUNKSIZE + min_len_bytes
506da0c48c4Sopenharmony_ci				+ bytes_per_char - 1);
507da0c48c4Sopenharmony_ci  size_t ntrailer = 0;
508da0c48c4Sopenharmony_ci  int result = 0;
509da0c48c4Sopenharmony_ci  while (fdlen > 0)
510da0c48c4Sopenharmony_ci    {
511da0c48c4Sopenharmony_ci      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + ntrailer,
512da0c48c4Sopenharmony_ci					    MIN (fdlen, CHUNKSIZE)));
513da0c48c4Sopenharmony_ci      if (n == 0)
514da0c48c4Sopenharmony_ci	{
515da0c48c4Sopenharmony_ci	  /* There are less than MIN_LEN+1 bytes left so there cannot be
516da0c48c4Sopenharmony_ci	     another match.  */
517da0c48c4Sopenharmony_ci	  assert (unprinted == NULL || ntrailer == 0);
518da0c48c4Sopenharmony_ci	  break;
519da0c48c4Sopenharmony_ci	}
520da0c48c4Sopenharmony_ci      if (unlikely (n < 0))
521da0c48c4Sopenharmony_ci	{
522da0c48c4Sopenharmony_ci	  /* Something went wrong.  */
523da0c48c4Sopenharmony_ci	  result = 1;
524da0c48c4Sopenharmony_ci	  break;
525da0c48c4Sopenharmony_ci	}
526da0c48c4Sopenharmony_ci
527da0c48c4Sopenharmony_ci      /* Account for the number of bytes read in this round.  */
528da0c48c4Sopenharmony_ci      fdlen -= n;
529da0c48c4Sopenharmony_ci
530da0c48c4Sopenharmony_ci      /* Do not use the signed N value.  Note that the addition cannot
531da0c48c4Sopenharmony_ci	 overflow.  */
532da0c48c4Sopenharmony_ci      size_t nb = (size_t) n + ntrailer;
533da0c48c4Sopenharmony_ci      if (nb >= min_len_bytes)
534da0c48c4Sopenharmony_ci	{
535da0c48c4Sopenharmony_ci	  /* We only use complete characters.  */
536da0c48c4Sopenharmony_ci	  nb &= ~(bytes_per_char - 1);
537da0c48c4Sopenharmony_ci
538da0c48c4Sopenharmony_ci	  process_chunk (fname, buf, from + nb, nb, &unprinted);
539da0c48c4Sopenharmony_ci
540da0c48c4Sopenharmony_ci	  /* If the last bytes of the buffer (modulo the character
541da0c48c4Sopenharmony_ci	     size) have been printed we are not copying them.  */
542da0c48c4Sopenharmony_ci	  size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
543da0c48c4Sopenharmony_ci
544da0c48c4Sopenharmony_ci	  memmove (buf, buf + nb - to_keep, to_keep);
545da0c48c4Sopenharmony_ci	  ntrailer = to_keep;
546da0c48c4Sopenharmony_ci	  from += nb;
547da0c48c4Sopenharmony_ci	}
548da0c48c4Sopenharmony_ci      else
549da0c48c4Sopenharmony_ci	ntrailer = nb;
550da0c48c4Sopenharmony_ci    }
551da0c48c4Sopenharmony_ci
552da0c48c4Sopenharmony_ci  free (buf);
553da0c48c4Sopenharmony_ci
554da0c48c4Sopenharmony_ci  /* Don't print anything we collected so far.  There is no
555da0c48c4Sopenharmony_ci     terminating NUL byte.  */
556da0c48c4Sopenharmony_ci  free (unprinted);
557da0c48c4Sopenharmony_ci
558da0c48c4Sopenharmony_ci  return result;
559da0c48c4Sopenharmony_ci}
560da0c48c4Sopenharmony_ci
561da0c48c4Sopenharmony_ci
562da0c48c4Sopenharmony_cistatic int
563da0c48c4Sopenharmony_ciread_block (int fd, const char *fname, off_t fdlen, off_t from, off_t to)
564da0c48c4Sopenharmony_ci{
565da0c48c4Sopenharmony_ci  if (elfmap == NULL)
566da0c48c4Sopenharmony_ci    {
567da0c48c4Sopenharmony_ci      /* We need a completely new mapping.  */
568da0c48c4Sopenharmony_ci      elfmap_off = from & ~(ps - 1);
569da0c48c4Sopenharmony_ci      elfmap_base = elfmap = map_file (fd, elfmap_off, fdlen, &elfmap_size);
570da0c48c4Sopenharmony_ci
571da0c48c4Sopenharmony_ci      if (unlikely (elfmap == MAP_FAILED))
572da0c48c4Sopenharmony_ci	/* Let the kernel know we are going to read everything in sequence.  */
573da0c48c4Sopenharmony_ci	(void) posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
574da0c48c4Sopenharmony_ci    }
575da0c48c4Sopenharmony_ci
576da0c48c4Sopenharmony_ci  if (unlikely (elfmap == MAP_FAILED))
577da0c48c4Sopenharmony_ci    {
578da0c48c4Sopenharmony_ci      /* Read from the file descriptor.  For this we must position the
579da0c48c4Sopenharmony_ci	 read pointer.  */
580da0c48c4Sopenharmony_ci      // XXX Eventually add flag which avoids this if the position
581da0c48c4Sopenharmony_ci      // XXX is known to match.
582da0c48c4Sopenharmony_ci      if (from != 0 && lseek (fd, from, SEEK_SET) != from)
583da0c48c4Sopenharmony_ci	error_exit (errno, _("lseek failed"));
584da0c48c4Sopenharmony_ci
585da0c48c4Sopenharmony_ci      return read_block_no_mmap (fd, fname, from, to - from);
586da0c48c4Sopenharmony_ci    }
587da0c48c4Sopenharmony_ci
588da0c48c4Sopenharmony_ci  assert ((off_t) min_len_bytes < fdlen);
589da0c48c4Sopenharmony_ci
590da0c48c4Sopenharmony_ci  if (to < (off_t) elfmap_off || from > (off_t) (elfmap_off + elfmap_size))
591da0c48c4Sopenharmony_ci    {
592da0c48c4Sopenharmony_ci      /* The existing mapping cannot fit at all.  Map the new area.
593da0c48c4Sopenharmony_ci	 We always map the full range of ELFMAP_SIZE bytes even if
594da0c48c4Sopenharmony_ci	 this extend beyond the end of the file.  The Linux kernel
595da0c48c4Sopenharmony_ci	 handles this OK if the access pages are not touched.  */
596da0c48c4Sopenharmony_ci      elfmap_off = from & ~(ps - 1);
597da0c48c4Sopenharmony_ci      if (mmap (elfmap, elfmap_size, PROT_READ,
598da0c48c4Sopenharmony_ci		MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, from)
599da0c48c4Sopenharmony_ci	  == MAP_FAILED)
600da0c48c4Sopenharmony_ci	error_exit (errno, _("re-mmap failed"));
601da0c48c4Sopenharmony_ci      elfmap_base = elfmap;
602da0c48c4Sopenharmony_ci    }
603da0c48c4Sopenharmony_ci
604da0c48c4Sopenharmony_ci  char *unprinted = NULL;
605da0c48c4Sopenharmony_ci
606da0c48c4Sopenharmony_ci  /* Use the existing mapping as much as possible.  If necessary, map
607da0c48c4Sopenharmony_ci     new pages.  */
608da0c48c4Sopenharmony_ci  if (from >= (off_t) elfmap_off
609da0c48c4Sopenharmony_ci      && from < (off_t) (elfmap_off + elfmap_size))
610da0c48c4Sopenharmony_ci    /* There are at least a few bytes in this mapping which we can
611da0c48c4Sopenharmony_ci       use.  */
612da0c48c4Sopenharmony_ci    process_chunk (fname, elfmap_base + (from - elfmap_off),
613da0c48c4Sopenharmony_ci		   MIN (to, (off_t) (elfmap_off + elfmap_size)),
614da0c48c4Sopenharmony_ci		   MIN (to, (off_t) (elfmap_off + elfmap_size)) - from,
615da0c48c4Sopenharmony_ci		   &unprinted);
616da0c48c4Sopenharmony_ci
617da0c48c4Sopenharmony_ci  if (to > (off_t) (elfmap_off + elfmap_size))
618da0c48c4Sopenharmony_ci    {
619da0c48c4Sopenharmony_ci      unsigned char *remap_base = elfmap_base;
620da0c48c4Sopenharmony_ci      size_t read_now = elfmap_size - (elfmap_base - elfmap);
621da0c48c4Sopenharmony_ci
622da0c48c4Sopenharmony_ci      assert (from >= (off_t) elfmap_off
623da0c48c4Sopenharmony_ci	      && from < (off_t) (elfmap_off + elfmap_size));
624da0c48c4Sopenharmony_ci      off_t handled_to = elfmap_off + elfmap_size;
625da0c48c4Sopenharmony_ci      assert (elfmap == elfmap_base
626da0c48c4Sopenharmony_ci	      || (elfmap_base - elfmap
627da0c48c4Sopenharmony_ci		  == (ptrdiff_t) ((min_len_bytes + ps - 1) & ~(ps - 1))));
628da0c48c4Sopenharmony_ci      if (elfmap == elfmap_base)
629da0c48c4Sopenharmony_ci	{
630da0c48c4Sopenharmony_ci	  size_t keep_area = (min_len_bytes + ps - 1) & ~(ps - 1);
631da0c48c4Sopenharmony_ci	  assert (elfmap_size >= keep_area + ps);
632da0c48c4Sopenharmony_ci	  /* The keep area is used for the content of the previous
633da0c48c4Sopenharmony_ci	     buffer we have to keep.  This means copying those bytes
634da0c48c4Sopenharmony_ci	     and for this we have to make the data writable.  */
635da0c48c4Sopenharmony_ci	  if (unlikely (mprotect (elfmap, keep_area, PROT_READ | PROT_WRITE)
636da0c48c4Sopenharmony_ci			!= 0))
637da0c48c4Sopenharmony_ci	    error_exit (errno, _("mprotect failed"));
638da0c48c4Sopenharmony_ci
639da0c48c4Sopenharmony_ci	  elfmap_base = elfmap + keep_area;
640da0c48c4Sopenharmony_ci	}
641da0c48c4Sopenharmony_ci
642da0c48c4Sopenharmony_ci      while (1)
643da0c48c4Sopenharmony_ci	{
644da0c48c4Sopenharmony_ci	  /* Map the rest of the file, eventually again in pieces.
645da0c48c4Sopenharmony_ci	     We speed things up with a nice Linux feature.  Note
646da0c48c4Sopenharmony_ci	     that we have at least two pages mapped.  */
647da0c48c4Sopenharmony_ci	  size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
648da0c48c4Sopenharmony_ci
649da0c48c4Sopenharmony_ci	  assert (read_now >= to_keep);
650da0c48c4Sopenharmony_ci	  memmove (elfmap_base - to_keep,
651da0c48c4Sopenharmony_ci		   remap_base + read_now - to_keep, to_keep);
652da0c48c4Sopenharmony_ci	  remap_base = elfmap_base;
653da0c48c4Sopenharmony_ci
654da0c48c4Sopenharmony_ci	  assert ((elfmap_size - (elfmap_base - elfmap)) % bytes_per_char
655da0c48c4Sopenharmony_ci		  == 0);
656da0c48c4Sopenharmony_ci	  read_now = MIN (to - handled_to,
657da0c48c4Sopenharmony_ci			  (ptrdiff_t) elfmap_size - (elfmap_base - elfmap));
658da0c48c4Sopenharmony_ci
659da0c48c4Sopenharmony_ci	  assert (handled_to % ps == 0);
660da0c48c4Sopenharmony_ci	  assert (handled_to % bytes_per_char == 0);
661da0c48c4Sopenharmony_ci	  if (mmap (remap_base, read_now, PROT_READ,
662da0c48c4Sopenharmony_ci		    MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, handled_to)
663da0c48c4Sopenharmony_ci	      == MAP_FAILED)
664da0c48c4Sopenharmony_ci	    error_exit (errno, _("re-mmap failed"));
665da0c48c4Sopenharmony_ci	  elfmap_off = handled_to;
666da0c48c4Sopenharmony_ci
667da0c48c4Sopenharmony_ci	  process_chunk (fname, remap_base - to_keep,
668da0c48c4Sopenharmony_ci			 elfmap_off + (read_now & ~(bytes_per_char - 1)),
669da0c48c4Sopenharmony_ci			 to_keep + (read_now & ~(bytes_per_char - 1)),
670da0c48c4Sopenharmony_ci			 &unprinted);
671da0c48c4Sopenharmony_ci	  handled_to += read_now;
672da0c48c4Sopenharmony_ci	  if (handled_to >= to)
673da0c48c4Sopenharmony_ci	    break;
674da0c48c4Sopenharmony_ci	}
675da0c48c4Sopenharmony_ci    }
676da0c48c4Sopenharmony_ci
677da0c48c4Sopenharmony_ci  /* Don't print anything we collected so far.  There is no
678da0c48c4Sopenharmony_ci     terminating NUL byte.  */
679da0c48c4Sopenharmony_ci  free (unprinted);
680da0c48c4Sopenharmony_ci
681da0c48c4Sopenharmony_ci  return 0;
682da0c48c4Sopenharmony_ci}
683da0c48c4Sopenharmony_ci
684da0c48c4Sopenharmony_ci
685da0c48c4Sopenharmony_cistatic int
686da0c48c4Sopenharmony_ciread_fd (int fd, const char *fname, off_t fdlen)
687da0c48c4Sopenharmony_ci{
688da0c48c4Sopenharmony_ci  return read_block (fd, fname, fdlen, 0, fdlen);
689da0c48c4Sopenharmony_ci}
690da0c48c4Sopenharmony_ci
691da0c48c4Sopenharmony_ci
692da0c48c4Sopenharmony_cistatic int
693da0c48c4Sopenharmony_ciread_elf (Elf *elf, int fd, const char *fname, off_t fdlen)
694da0c48c4Sopenharmony_ci{
695da0c48c4Sopenharmony_ci  assert (fdlen >= 0);
696da0c48c4Sopenharmony_ci
697da0c48c4Sopenharmony_ci  /* We will look at each section separately.  The ELF file is not
698da0c48c4Sopenharmony_ci     mmapped.  The libelf implementation will load the needed parts on
699da0c48c4Sopenharmony_ci     demand.  Since we only iterate over the section header table the
700da0c48c4Sopenharmony_ci     memory consumption at this stage is kept minimal.  */
701da0c48c4Sopenharmony_ci  Elf_Scn *scn = elf_nextscn (elf, NULL);
702da0c48c4Sopenharmony_ci  if (scn == NULL)
703da0c48c4Sopenharmony_ci    return read_fd (fd, fname, fdlen);
704da0c48c4Sopenharmony_ci
705da0c48c4Sopenharmony_ci  int result = 0;
706da0c48c4Sopenharmony_ci  do
707da0c48c4Sopenharmony_ci    {
708da0c48c4Sopenharmony_ci      GElf_Shdr shdr_mem;
709da0c48c4Sopenharmony_ci      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
710da0c48c4Sopenharmony_ci
711da0c48c4Sopenharmony_ci      /* Only look in sections which are loaded at runtime and
712da0c48c4Sopenharmony_ci	 actually have content.  */
713da0c48c4Sopenharmony_ci      if (shdr != NULL && shdr->sh_type != SHT_NOBITS
714da0c48c4Sopenharmony_ci	  && (shdr->sh_flags & SHF_ALLOC) != 0)
715da0c48c4Sopenharmony_ci	{
716da0c48c4Sopenharmony_ci	  if (shdr->sh_offset > (Elf64_Off) fdlen
717da0c48c4Sopenharmony_ci	      || fdlen - shdr->sh_offset < shdr->sh_size)
718da0c48c4Sopenharmony_ci	    {
719da0c48c4Sopenharmony_ci	      size_t strndx = 0;
720da0c48c4Sopenharmony_ci	      const char *sname;
721da0c48c4Sopenharmony_ci	      if (unlikely (elf_getshdrstrndx (elf, &strndx) < 0))
722da0c48c4Sopenharmony_ci		sname = "<unknown>";
723da0c48c4Sopenharmony_ci	      else
724da0c48c4Sopenharmony_ci		sname = elf_strptr (elf, strndx, shdr->sh_name) ?: "<unknown>";
725da0c48c4Sopenharmony_ci	      error (0, 0,
726da0c48c4Sopenharmony_ci		     _("Skipping section %zd '%s' data outside file"),
727da0c48c4Sopenharmony_ci		     elf_ndxscn (scn), sname);
728da0c48c4Sopenharmony_ci	      result = 1;
729da0c48c4Sopenharmony_ci	    }
730da0c48c4Sopenharmony_ci	  else
731da0c48c4Sopenharmony_ci	    result |= read_block (fd, fname, fdlen, shdr->sh_offset,
732da0c48c4Sopenharmony_ci				  shdr->sh_offset + shdr->sh_size);
733da0c48c4Sopenharmony_ci	}
734da0c48c4Sopenharmony_ci    }
735da0c48c4Sopenharmony_ci  while ((scn = elf_nextscn (elf, scn)) != NULL);
736da0c48c4Sopenharmony_ci
737da0c48c4Sopenharmony_ci  if (elfmap != NULL && elfmap != MAP_FAILED)
738da0c48c4Sopenharmony_ci    munmap (elfmap, elfmap_size);
739da0c48c4Sopenharmony_ci  elfmap = NULL;
740da0c48c4Sopenharmony_ci
741da0c48c4Sopenharmony_ci  return result;
742da0c48c4Sopenharmony_ci}
743da0c48c4Sopenharmony_ci
744da0c48c4Sopenharmony_ci
745da0c48c4Sopenharmony_ci#include "debugpred.h"
746