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