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 
42 struct segments
43 {
44   GElf_Addr from;
45   GElf_Addr to;
46 };
47 
48 
49 /* Name and version of program.  */
50 static void print_version (FILE *stream, struct argp_state *state);
51 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
52 
53 /* Bug report address.  */
54 ARGP_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.  */
60 static 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.  */
72 static const char doc[] = N_("\
73 Locate source of text relocations in FILEs (a.out by default).");
74 
75 /* Strings for arguments in help texts.  */
76 static const char args_doc[] = N_("[FILE...]");
77 
78 /* Prototype for option handler.  */
79 static error_t parse_opt (int key, char *arg, struct argp_state *state);
80 
81 /* Data structure to communicate with argp functions.  */
82 static struct argp argp =
83 {
84   options, parse_opt, args_doc, doc, NULL, NULL, NULL
85 };
86 
87 
88 /* Print symbols in file named FNAME.  */
89 static 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.  */
93 static 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.  */
101 static const char *rootdir = "/";
102 
103 /* Root of debuginfo directory hierarchy.  */
104 static const char *debuginfo_root;
105 
106 
107 int
main(int argc,char * argv[])108 main (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.  */
158 static void
print_version(FILE * stream,struct argp_state * state)159 print_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 ("\
163 Copyright (C) %s Red Hat, Inc.\n\
164 This is free software; see the source for copying conditions.  There is NO\n\
165 warranty; 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.  */
172 static error_t
parse_opt(int key,char * arg,struct argp_state * state)173 parse_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 
193 static void
noop(void * arg)194 noop (void *arg __attribute__ ((unused)))
195 {
196 }
197 
198 
199 static int
process_file(const char * fname,bool more_than_one)200 process_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 = open (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 ("\
358 cannot 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 = open (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 ("\
422 cannot 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 ("\
442 cannot 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 ("\
465 cannot 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   free (segments);
488   tdestroy (knownsrcs, noop);
489 
490   return result;
491 }
492 
493 
494 static int
ptrcompare(const void * p1,const void * p2)495 ptrcompare (const void *p1, const void *p2)
496 {
497   if ((uintptr_t) p1 < (uintptr_t) p2)
498     return -1;
499   if ((uintptr_t) p1 > (uintptr_t) p2)
500     return 1;
501   return 0;
502 }
503 
504 
505 static void
check_rel(size_t nsegments,struct segments segments[nsegments],GElf_Addr addr,Elf * elf,Elf_Scn * symscn,Dwarf * dw,const char * fname,bool more_than_one,void ** knownsrcs)506 check_rel (size_t nsegments, struct segments segments[nsegments],
507 	   GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
508 	   const char *fname, bool more_than_one, void **knownsrcs)
509 {
510   for (size_t cnt = 0; cnt < nsegments; ++cnt)
511     if (segments[cnt].from <= addr && segments[cnt].to > addr)
512       {
513 	Dwarf_Die die_mem;
514 	Dwarf_Die *die;
515 	Dwarf_Line *line;
516 	const char *src;
517 
518 	if (more_than_one)
519 	  printf ("%s: ", fname);
520 
521 	if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
522 	    && (line = dwarf_getsrc_die (die, addr)) != NULL
523 	    && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
524 	  {
525 	    /* There can be more than one relocation against one file.
526 	       Try to avoid multiple messages.  And yes, the code uses
527 	       pointer comparison.  */
528 	    if (tfind (src, knownsrcs, ptrcompare) == NULL)
529 	      {
530 		printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src);
531 		tsearch (src, knownsrcs, ptrcompare);
532 	      }
533 	    return;
534 	  }
535 	else
536 	  {
537 	    /* At least look at the symbol table to see which function
538 	       the modified address is in.  */
539 	    Elf_Data *symdata = elf_getdata (symscn, NULL);
540 	    GElf_Shdr shdr_mem;
541 	    GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
542 	    if (shdr != NULL)
543 	      {
544 		GElf_Addr lowaddr = 0;
545 		int lowidx = -1;
546 		GElf_Addr highaddr = ~0ul;
547 		int highidx = -1;
548 		GElf_Sym sym_mem;
549 		GElf_Sym *sym;
550 
551 		for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize;
552 		     ++i)
553 		  {
554 		    sym = gelf_getsym (symdata, i, &sym_mem);
555 		    if (sym == NULL)
556 		      continue;
557 
558 		    if (sym->st_value < addr && sym->st_value > lowaddr)
559 		      {
560 			lowaddr = sym->st_value;
561 			lowidx = i;
562 		      }
563 		    if (sym->st_value > addr && sym->st_value < highaddr)
564 		      {
565 			highaddr = sym->st_value;
566 			highidx = i;
567 		      }
568 		  }
569 
570 		if (lowidx != -1)
571 		  {
572 		    sym = gelf_getsym (symdata, lowidx, &sym_mem);
573 		    assert (sym != NULL);
574 
575 		    const char *lowstr = elf_strptr (elf, shdr->sh_link,
576 						     sym->st_name);
577 
578 		    if (sym->st_value + sym->st_size > addr)
579 		      {
580 			/* It is this function.  */
581 			if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
582 			  {
583 			    printf (gettext ("\
584 the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
585 				    lowstr);
586 			    tsearch (lowstr, knownsrcs, ptrcompare);
587 			  }
588 		      }
589 		    else if (highidx == -1)
590 		      printf (gettext ("\
591 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
592 			      lowstr);
593 		    else
594 		      {
595 			sym = gelf_getsym (symdata, highidx, &sym_mem);
596 			assert (sym != NULL);
597 
598 			printf (gettext ("\
599 either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
600 				lowstr, elf_strptr (elf, shdr->sh_link,
601 						    sym->st_name));
602 		      }
603 		    return;
604 		  }
605 		else if (highidx != -1)
606 		  {
607 		    sym = gelf_getsym (symdata, highidx, &sym_mem);
608 		    assert (sym != NULL);
609 
610 		    printf (gettext ("\
611 the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
612 			    elf_strptr (elf, shdr->sh_link, sym->st_name));
613 		    return;
614 		  }
615 	      }
616 	  }
617 
618 	printf (gettext ("\
619 a relocation modifies memory at offset %llu in a write-protected segment\n"),
620 		(unsigned long long int) addr);
621 	break;
622       }
623 }
624 
625 
626 #include "debugpred.h"
627