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