1/* Locate source files or functions which caused text relocations.
2   Copyright (C) 2005-2010, 2012 Red Hat, Inc.
3   This file is part of Red Hat elfutils.
4   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5
6   Red Hat elfutils is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 2 of the License.
9
10   Red Hat elfutils is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14
15   You should have received a copy of the GNU General Public License along
16   with Red Hat elfutils; if not, write to the Free Software Foundation,
17   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18
19   Red Hat elfutils is an included package of the Open Invention Network.
20   An included package of the Open Invention Network is a package for which
21   Open Invention Network licensees cross-license their patents.  No patent
22   license is granted, either expressly or impliedly, by designation as an
23   included package.  Should you wish to participate in the Open Invention
24   Network licensing program, please visit www.openinventionnetwork.com
25   <http://www.openinventionnetwork.com>.  */
26
27#ifdef HAVE_CONFIG_H
28# include <config.h>
29#endif
30
31#include <argp.h>
32#include <assert.h>
33#include <errno.h>
34#include <error.h>
35#include <fcntl.h>
36#include <gelf.h>
37#include <libdw.h>
38#include <libintl.h>
39#include <locale.h>
40#include <search.h>
41#include <stdbool.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include <system.h>
48
49
50struct segments
51{
52  GElf_Addr from;
53  GElf_Addr to;
54};
55
56
57/* Name and version of program.  */
58static void print_version (FILE *stream, struct argp_state *state);
59ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
60
61/* Bug report address.  */
62ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
63
64/* Values for the parameters which have no short form.  */
65#define OPT_DEBUGINFO 0x100
66
67/* Definitions of arguments for argp functions.  */
68static const struct argp_option options[] =
69{
70  { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
71  { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
72  { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
73    N_("Use PATH as root of debuginfo hierarchy"), 0 },
74
75  { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
76  { NULL, 0, NULL, 0, NULL, 0 }
77};
78
79/* Short description of program.  */
80static const char doc[] = N_("\
81Locate source of text relocations in FILEs (a.out by default).");
82
83/* Strings for arguments in help texts.  */
84static const char args_doc[] = N_("[FILE...]");
85
86/* Prototype for option handler.  */
87static error_t parse_opt (int key, char *arg, struct argp_state *state);
88
89/* Data structure to communicate with argp functions.  */
90static struct argp argp =
91{
92  options, parse_opt, args_doc, doc, NULL, NULL, NULL
93};
94
95
96/* Print symbols in file named FNAME.  */
97static int process_file (const char *fname, bool more_than_one);
98
99/* Check for text relocations in the given file.  The segment
100   information is known.  */
101static void check_rel (size_t nsegments, struct segments segments[nsegments],
102		       GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
103		       const char *fname, bool more_than_one,
104		       void **knownsrcs);
105
106
107
108/* User-provided root directory.  */
109static const char *rootdir = "/";
110
111/* Root of debuginfo directory hierarchy.  */
112static const char *debuginfo_root;
113
114
115int
116main (int argc, char *argv[])
117{
118  int remaining;
119  int result = 0;
120
121  /* Set locale.  */
122  (void) setlocale (LC_ALL, "");
123
124  /* Make sure the message catalog can be found.  */
125  (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
126
127  /* Initialize the message catalog.  */
128  (void) textdomain (PACKAGE_TARNAME);
129
130  /* Parse and process arguments.  */
131  (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
132
133  /* Tell the library which version we are expecting.  */
134  elf_version (EV_CURRENT);
135
136  /* If the user has not specified the root directory for the
137     debuginfo hierarchy, we have to determine it ourselves.  */
138  if (debuginfo_root == NULL)
139    {
140      // XXX The runtime should provide this information.
141#if defined __ia64__ || defined __alpha__
142      debuginfo_root = "/usr/lib/debug";
143#else
144      debuginfo_root = (sizeof (long int) == 4
145			? "/usr/lib/debug" : "/usr/lib64/debug");
146#endif
147    }
148
149  if (remaining == argc)
150    result = process_file ("a.out", false);
151  else
152    {
153      /* Process all the remaining files.  */
154      const bool more_than_one = remaining + 1 < argc;
155
156      do
157	result |= process_file (argv[remaining], more_than_one);
158      while (++remaining < argc);
159    }
160
161  return result;
162}
163
164
165/* Print the version information.  */
166static void
167print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
168{
169  fprintf (stream, "findtextrel (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
170  fprintf (stream, gettext ("\
171Copyright (C) %s Red Hat, Inc.\n\
172This is free software; see the source for copying conditions.  There is NO\n\
173warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
174"), "2012");
175  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
176}
177
178
179/* Handle program arguments.  */
180static error_t
181parse_opt (int key, char *arg,
182	   struct argp_state *state __attribute__ ((unused)))
183{
184  switch (key)
185    {
186    case 'r':
187      rootdir = arg;
188      break;
189
190    case OPT_DEBUGINFO:
191      debuginfo_root = arg;
192      break;
193
194    default:
195      return ARGP_ERR_UNKNOWN;
196    }
197  return 0;
198}
199
200
201static void
202noop (void *arg __attribute__ ((unused)))
203{
204}
205
206
207static int
208process_file (const char *fname, bool more_than_one)
209{
210  int result = 0;
211  void *knownsrcs = NULL;
212
213  size_t fname_len = strlen (fname);
214  size_t rootdir_len = strlen (rootdir);
215  const char *real_fname = fname;
216  if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
217    {
218      /* Prepend the user-provided root directory.  */
219      char *new_fname = alloca (rootdir_len + fname_len + 2);
220      *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
221				  "/"),
222			  fname, fname_len)) = '\0';
223      real_fname = new_fname;
224    }
225
226  int fd = open64 (real_fname, O_RDONLY);
227  if (fd == -1)
228    {
229      error (0, errno, gettext ("cannot open '%s'"), fname);
230      return 1;
231    }
232
233  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
234  if (elf == NULL)
235    {
236      error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
237	     fname, elf_errmsg (-1));
238      goto err_close;
239    }
240
241  /* Make sure the file is a DSO.  */
242  GElf_Ehdr ehdr_mem;
243  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
244  if (ehdr == NULL)
245    {
246      error (0, 0, gettext ("cannot get ELF header '%s': %s"),
247	     fname, elf_errmsg (-1));
248    err_elf_close:
249      elf_end (elf);
250    err_close:
251      close (fd);
252      return 1;
253    }
254
255  if (ehdr->e_type != ET_DYN)
256    {
257      error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname);
258      goto err_elf_close;
259    }
260
261  /* Determine whether the DSO has text relocations at all and locate
262     the symbol table.  */
263  Elf_Scn *symscn = NULL;
264  Elf_Scn *scn = NULL;
265  bool seen_dynamic = false;
266  bool have_textrel = false;
267  while ((scn = elf_nextscn (elf, scn)) != NULL
268	 && (!seen_dynamic || symscn == NULL))
269    {
270      /* Handle the section if it is a symbol table.  */
271      GElf_Shdr shdr_mem;
272      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
273
274      if (shdr == NULL)
275	{
276	  error (0, 0,
277		 gettext ("getting get section header of section %zu: %s"),
278		 elf_ndxscn (scn), elf_errmsg (-1));
279	  goto err_elf_close;
280	}
281
282      switch (shdr->sh_type)
283	{
284	case SHT_DYNAMIC:
285	  if (!seen_dynamic)
286	    {
287	      seen_dynamic = true;
288
289	      Elf_Data *data = elf_getdata (scn, NULL);
290
291	      for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize;
292		   ++cnt)
293		{
294		  GElf_Dyn dynmem;
295		  GElf_Dyn *dyn;
296
297		  dyn = gelf_getdyn (data, cnt, &dynmem);
298		  if (dyn == NULL)
299		    {
300		      error (0, 0, gettext ("cannot read dynamic section: %s"),
301			     elf_errmsg (-1));
302		      goto err_elf_close;
303		    }
304
305		  if (dyn->d_tag == DT_TEXTREL
306		      || (dyn->d_tag == DT_FLAGS
307			  && (dyn->d_un.d_val & DF_TEXTREL) != 0))
308		    have_textrel = true;
309		}
310	    }
311	  break;
312
313	case SHT_SYMTAB:
314	  symscn = scn;
315	  break;
316	}
317    }
318
319  if (!have_textrel)
320    {
321      error (0, 0, gettext ("no text relocations reported in '%s'"), fname);
322      return 1;
323    }
324
325  int fd2 = -1;
326  Elf *elf2 = NULL;
327  /* Get the address ranges for the loaded segments.  */
328  size_t nsegments_max = 10;
329  size_t nsegments = 0;
330  struct segments *segments
331    = (struct segments *) malloc (nsegments_max * sizeof (segments[0]));
332  if (segments == NULL)
333    error (1, errno, gettext ("while reading ELF file"));
334
335  for (int i = 0; i < ehdr->e_phnum; ++i)
336    {
337      GElf_Phdr phdr_mem;
338      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
339      if (phdr == NULL)
340	{
341	  error (0, 0,
342		 gettext ("cannot get program header index at offset %d: %s"),
343		 i, elf_errmsg (-1));
344	  result = 1;
345	  goto next;
346	}
347
348      if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
349	{
350	  if (nsegments == nsegments_max)
351	    {
352	      nsegments_max *= 2;
353	      segments
354		= (struct segments *) realloc (segments,
355					       nsegments_max
356					       * sizeof (segments[0]));
357	      if (segments == NULL)
358		{
359		  error (0, 0, gettext ("\
360cannot get program header index at offset %d: %s"),
361			 i, elf_errmsg (-1));
362		  result = 1;
363		  goto next;
364		}
365	    }
366
367	  segments[nsegments].from = phdr->p_vaddr;
368	  segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
369	  ++nsegments;
370	}
371    }
372
373  if (nsegments > 0)
374    {
375
376      Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
377      /* Look for debuginfo files if the information is not the in
378	 opened file itself.  This makes only sense if the input file
379	 is specified with an absolute path.  */
380      if (dw == NULL && fname[0] == '/')
381	{
382	  size_t debuginfo_rootlen = strlen (debuginfo_root);
383	  char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
384					   + fname_len + 8);
385	  strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
386						     rootdir_len),
387					    debuginfo_root,
388					    debuginfo_rootlen),
389				   "/"),
390			   fname, fname_len),
391		  ".debug");
392
393	  fd2 = open64 (difname, O_RDONLY);
394	  if (fd2 != -1
395	      && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
396	    dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
397	}
398
399      /* Look at all relocations and determine which modify
400	 write-protected segments.  */
401      scn = NULL;
402      while ((scn = elf_nextscn (elf, scn)) != NULL)
403	{
404	  /* Handle the section if it is a symbol table.  */
405	  GElf_Shdr shdr_mem;
406	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
407
408	  if (shdr == NULL)
409	    {
410	      error (0, 0,
411		     gettext ("cannot get section header of section %Zu: %s"),
412		     elf_ndxscn (scn), elf_errmsg (-1));
413	      result = 1;
414	      goto next;
415	    }
416
417	  if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
418	      && symscn == NULL)
419	    {
420	      symscn = elf_getscn (elf, shdr->sh_link);
421	      if (symscn == NULL)
422		{
423		  error (0, 0, gettext ("\
424cannot get symbol table section %zu in '%s': %s"),
425			 (size_t) shdr->sh_link, fname, elf_errmsg (-1));
426		  result = 1;
427		  goto next;
428		}
429	    }
430
431	  if (shdr->sh_type == SHT_REL)
432	    {
433	      Elf_Data *data = elf_getdata (scn, NULL);
434
435	      for (int cnt = 0;
436		   (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
437		   ++cnt)
438		{
439		  GElf_Rel rel_mem;
440		  GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
441		  if (rel == NULL)
442		    {
443		      error (0, 0, gettext ("\
444cannot get relocation at index %d in section %zu in '%s': %s"),
445			     cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
446		      result = 1;
447		      goto next;
448		    }
449
450		  check_rel (nsegments, segments, rel->r_offset, elf,
451			     symscn, dw, fname, more_than_one, &knownsrcs);
452		}
453	    }
454	  else if (shdr->sh_type == SHT_RELA)
455	    {
456	      Elf_Data *data = elf_getdata (scn, NULL);
457
458	      for (int cnt = 0;
459		   (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
460		   ++cnt)
461		{
462		  GElf_Rela rela_mem;
463		  GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
464		  if (rela == NULL)
465		    {
466		      error (0, 0, gettext ("\
467cannot get relocation at index %d in section %zu in '%s': %s"),
468			     cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
469		      result = 1;
470		      goto next;
471		    }
472
473		  check_rel (nsegments, segments, rela->r_offset, elf,
474			     symscn, dw, fname, more_than_one, &knownsrcs);
475		}
476	    }
477	}
478
479      dwarf_end (dw);
480    }
481
482 next:
483  elf_end (elf);
484  elf_end (elf2);
485  close (fd);
486  if (fd2 != -1)
487    close (fd2);
488
489  tdestroy (knownsrcs, noop);
490
491  return result;
492}
493
494
495static int
496ptrcompare (const void *p1, const void *p2)
497{
498  if ((uintptr_t) p1 < (uintptr_t) p2)
499    return -1;
500  if ((uintptr_t) p1 > (uintptr_t) p2)
501    return 1;
502  return 0;
503}
504
505
506static void
507check_rel (size_t nsegments, struct segments segments[nsegments],
508	   GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
509	   const char *fname, bool more_than_one, void **knownsrcs)
510{
511  for (size_t cnt = 0; cnt < nsegments; ++cnt)
512    if (segments[cnt].from <= addr && segments[cnt].to > addr)
513      {
514	Dwarf_Die die_mem;
515	Dwarf_Die *die;
516	Dwarf_Line *line;
517	const char *src;
518
519	if (more_than_one)
520	  printf ("%s: ", fname);
521
522	if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
523	    && (line = dwarf_getsrc_die (die, addr)) != NULL
524	    && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
525	  {
526	    /* There can be more than one relocation against one file.
527	       Try to avoid multiple messages.  And yes, the code uses
528	       pointer comparison.  */
529	    if (tfind (src, knownsrcs, ptrcompare) == NULL)
530	      {
531		printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src);
532		tsearch (src, knownsrcs, ptrcompare);
533	      }
534	    return;
535	  }
536	else
537	  {
538	    /* At least look at the symbol table to see which function
539	       the modified address is in.  */
540	    Elf_Data *symdata = elf_getdata (symscn, NULL);
541	    GElf_Shdr shdr_mem;
542	    GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
543	    if (shdr != NULL)
544	      {
545		GElf_Addr lowaddr = 0;
546		int lowidx = -1;
547		GElf_Addr highaddr = ~0ul;
548		int highidx = -1;
549		GElf_Sym sym_mem;
550		GElf_Sym *sym;
551
552		for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize;
553		     ++i)
554		  {
555		    sym = gelf_getsym (symdata, i, &sym_mem);
556		    if (sym == NULL)
557		      continue;
558
559		    if (sym->st_value < addr && sym->st_value > lowaddr)
560		      {
561			lowaddr = sym->st_value;
562			lowidx = i;
563		      }
564		    if (sym->st_value > addr && sym->st_value < highaddr)
565		      {
566			highaddr = sym->st_value;
567			highidx = i;
568		      }
569		  }
570
571		if (lowidx != -1)
572		  {
573		    sym = gelf_getsym (symdata, lowidx, &sym_mem);
574		    assert (sym != NULL);
575
576		    const char *lowstr = elf_strptr (elf, shdr->sh_link,
577						     sym->st_name);
578
579		    if (sym->st_value + sym->st_size > addr)
580		      {
581			/* It is this function.  */
582			if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
583			  {
584			    printf (gettext ("\
585the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
586				    lowstr);
587			    tsearch (lowstr, knownsrcs, ptrcompare);
588			  }
589		      }
590		    else if (highidx == -1)
591		      printf (gettext ("\
592the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
593			      lowstr);
594		    else
595		      {
596			sym = gelf_getsym (symdata, highidx, &sym_mem);
597			assert (sym != NULL);
598
599			printf (gettext ("\
600either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
601				lowstr, elf_strptr (elf, shdr->sh_link,
602						    sym->st_name));
603		      }
604		    return;
605		  }
606		else if (highidx != -1)
607		  {
608		    sym = gelf_getsym (symdata, highidx, &sym_mem);
609		    assert (sym != NULL);
610
611		    printf (gettext ("\
612the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
613			    elf_strptr (elf, shdr->sh_link, sym->st_name));
614		    return;
615		  }
616	      }
617	  }
618
619	printf (gettext ("\
620a relocation modifies memory at offset %llu in a write-protected segment\n"),
621		(unsigned long long int) addr);
622	break;
623      }
624}
625
626
627#include "debugpred.h"
628