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 (at) 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 48 struct segments 49 { 50 GElf_Addr from; 51 GElf_Addr to; 52 }; 53 54 55 /* Name and version of program. */ 56 static void print_version (FILE *stream, struct argp_state *state); 57 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; 58 59 /* Bug report address. */ 60 const 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. */ 66 static 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. */ 78 static const char doc[] = N_("\ 79 Locate source of text relocations in FILEs (a.out by default)."); 80 81 /* Strings for arguments in help texts. */ 82 static const char args_doc[] = N_("[FILE...]"); 83 84 /* Prototype for option handler. */ 85 static error_t parse_opt (int key, char *arg, struct argp_state *state); 86 87 /* Data structure to communicate with argp functions. */ 88 static struct argp argp = 89 { 90 options, parse_opt, args_doc, doc, NULL, NULL, NULL 91 }; 92 93 94 /* Print symbols in file named FNAME. */ 95 static 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. */ 99 static 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. */ 107 static const char *rootdir = "/"; 108 109 /* Root of debuginfo directory hierarchy. */ 110 static const char *debuginfo_root; 111 112 113 int 114 main (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. */ 164 static void 165 print_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 ("\ 169 Copyright (C) %s Red Hat, Inc.\n\ 170 This is free software; see the source for copying conditions. There is NO\n\ 171 warranty; 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. */ 178 static error_t 179 parse_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 199 static void 200 noop (void *arg __attribute__ ((unused))) 201 { 202 } 203 204 205 static int 206 process_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 ("\ 344 cannot 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 ("\ 408 cannot 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 ("\ 428 cannot 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 ("\ 451 cannot 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 479 static int 480 ptrcompare (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 490 static void 491 check_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 ("\ 569 the 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 ("\ 576 the 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 ("\ 584 either 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 ("\ 596 the 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 ("\ 604 a 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