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