444f3a2b59c5391bf8ae3232e68f3a44f1aa6100
[platform/upstream/elfutils.git] / src / findtextrel.c
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
50 struct segments
51 {
52   GElf_Addr from;
53   GElf_Addr to;
54 };
55
56
57 /* Name and version of program.  */
58 static void print_version (FILE *stream, struct argp_state *state);
59 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
60
61 /* Bug report address.  */
62 ARGP_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.  */
68 static 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.  */
80 static const char doc[] = N_("\
81 Locate source of text relocations in FILEs (a.out by default).");
82
83 /* Strings for arguments in help texts.  */
84 static const char args_doc[] = N_("[FILE...]");
85
86 /* Prototype for option handler.  */
87 static error_t parse_opt (int key, char *arg, struct argp_state *state);
88
89 /* Data structure to communicate with argp functions.  */
90 static struct argp argp =
91 {
92   options, parse_opt, args_doc, doc, NULL, NULL, NULL
93 };
94
95
96 /* Print symbols in file named FNAME.  */
97 static 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.  */
101 static 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.  */
109 static const char *rootdir = "/";
110
111 /* Root of debuginfo directory hierarchy.  */
112 static const char *debuginfo_root;
113
114
115 int
116 main (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.  */
166 static void
167 print_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 ("\
171 Copyright (C) %s Red Hat, Inc.\n\
172 This is free software; see the source for copying conditions.  There is NO\n\
173 warranty; 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.  */
180 static error_t
181 parse_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
201 static void
202 noop (void *arg __attribute__ ((unused)))
203 {
204 }
205
206
207 static int
208 process_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 ("\
360 cannot 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 ("\
424 cannot 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 ("\
444 cannot 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 ("\
467 cannot 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
495 static int
496 ptrcompare (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
506 static void
507 check_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 ("\
585 the 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 ("\
592 the 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 ("\
600 either 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 ("\
612 the 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 ("\
620 a 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"