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