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 (at) 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 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 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 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 194 noop (void *arg __attribute__ ((unused))) 195 { 196 } 197 198 199 static int 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 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 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