1/* Locate source files or functions which caused text relocations. 2 Copyright (C) 2005-2010, 2012, 2014, 2018 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 <fcntl.h> 27#include <gelf.h> 28#include <libdw.h> 29#include <locale.h> 30#include <search.h> 31#include <stdbool.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36 37#include <printversion.h> 38#include "libeu.h" 39#include "system.h" 40 41struct segments 42{ 43 GElf_Addr from; 44 GElf_Addr to; 45}; 46 47 48/* Name and version of program. */ 49ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; 50 51/* Bug report address. */ 52ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; 53 54/* Values for the parameters which have no short form. */ 55#define OPT_DEBUGINFO 0x100 56 57/* Definitions of arguments for argp functions. */ 58static const struct argp_option options[] = 59{ 60 { NULL, 0, NULL, 0, N_("Input Selection:"), 0 }, 61 { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 }, 62 { "debuginfo", OPT_DEBUGINFO, "PATH", 0, 63 N_("Use PATH as root of debuginfo hierarchy"), 0 }, 64 65 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, 66 { NULL, 0, NULL, 0, NULL, 0 } 67}; 68 69/* Short description of program. */ 70static const char doc[] = N_("\ 71Locate source of text relocations in FILEs (a.out by default)."); 72 73/* Strings for arguments in help texts. */ 74static const char args_doc[] = N_("[FILE...]"); 75 76/* Prototype for option handler. */ 77static error_t parse_opt (int key, char *arg, struct argp_state *state); 78 79/* Data structure to communicate with argp functions. */ 80static struct argp argp = 81{ 82 options, parse_opt, args_doc, doc, NULL, NULL, NULL 83}; 84 85 86/* Print symbols in file named FNAME. */ 87static int process_file (const char *fname, bool more_than_one); 88 89/* Check for text relocations in the given file. The segment 90 information is known. */ 91static void check_rel (size_t nsegments, struct segments segments[nsegments], 92 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw, 93 const char *fname, bool more_than_one, 94 void **knownsrcs); 95 96 97 98/* User-provided root directory. */ 99static const char *rootdir = "/"; 100 101/* Root of debuginfo directory hierarchy. */ 102static const char *debuginfo_root; 103 104 105int 106main (int argc, char *argv[]) 107{ 108 int remaining; 109 int result = 0; 110 111 /* Set locale. */ 112 (void) setlocale (LC_ALL, ""); 113 114 /* Make sure the message catalog can be found. */ 115 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); 116 117 /* Initialize the message catalog. */ 118 (void) textdomain (PACKAGE_TARNAME); 119 120 /* Parse and process arguments. */ 121 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); 122 123 /* Tell the library which version we are expecting. */ 124 elf_version (EV_CURRENT); 125 126 /* If the user has not specified the root directory for the 127 debuginfo hierarchy, we have to determine it ourselves. */ 128 if (debuginfo_root == NULL) 129 { 130 // XXX The runtime should provide this information. 131#if defined __ia64__ || defined __alpha__ 132 debuginfo_root = "/usr/lib/debug"; 133#else 134 debuginfo_root = (sizeof (long int) == 4 135 ? "/usr/lib/debug" : "/usr/lib64/debug"); 136#endif 137 } 138 139 if (remaining == argc) 140 result = process_file ("a.out", false); 141 else 142 { 143 /* Process all the remaining files. */ 144 const bool more_than_one = remaining + 1 < argc; 145 146 do 147 result |= process_file (argv[remaining], more_than_one); 148 while (++remaining < argc); 149 } 150 151 return result; 152} 153 154 155/* Handle program arguments. */ 156static error_t 157parse_opt (int key, char *arg, 158 struct argp_state *state __attribute__ ((unused))) 159{ 160 switch (key) 161 { 162 case 'r': 163 rootdir = arg; 164 break; 165 166 case OPT_DEBUGINFO: 167 debuginfo_root = arg; 168 break; 169 170 default: 171 return ARGP_ERR_UNKNOWN; 172 } 173 return 0; 174} 175 176 177static void 178noop (void *arg __attribute__ ((unused))) 179{ 180} 181 182 183static int 184open_rootdir_file (const char *fname) 185{ 186 char *new_fname = NULL; 187 const char *real_fname = fname; 188 189 if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0')) 190 real_fname = new_fname = xasprintf ("%s/%s", rootdir, fname); 191 192 int fd = open (real_fname, O_RDONLY); 193 if (fd == -1) 194 error (0, errno, _("cannot open '%s'"), fname); 195 196 free (new_fname); 197 return fd; 198} 199 200 201static int 202process_file (const char *fname, bool more_than_one) 203{ 204 int result = 0; 205 void *knownsrcs = NULL; 206 int fd = open_rootdir_file (fname); 207 if (fd == -1) 208 return 1; 209 210 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); 211 if (elf == NULL) 212 { 213 error (0, 0, _("cannot create ELF descriptor for '%s': %s"), 214 fname, elf_errmsg (-1)); 215 goto err_close; 216 } 217 218 /* Make sure the file is a DSO. */ 219 GElf_Ehdr ehdr_mem; 220 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); 221 if (ehdr == NULL) 222 { 223 error (0, 0, _("cannot get ELF header '%s': %s"), 224 fname, elf_errmsg (-1)); 225 err_elf_close: 226 elf_end (elf); 227 err_close: 228 close (fd); 229 return 1; 230 } 231 232 if (ehdr->e_type != ET_DYN) 233 { 234 error (0, 0, _("'%s' is not a DSO or PIE"), fname); 235 goto err_elf_close; 236 } 237 238 /* Determine whether the DSO has text relocations at all and locate 239 the symbol table. */ 240 Elf_Scn *symscn = NULL; 241 Elf_Scn *scn = NULL; 242 bool seen_dynamic = false; 243 bool have_textrel = false; 244 while ((scn = elf_nextscn (elf, scn)) != NULL 245 && (!seen_dynamic || symscn == NULL)) 246 { 247 /* Handle the section if it is a symbol table. */ 248 GElf_Shdr shdr_mem; 249 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 250 251 if (shdr == NULL) 252 { 253 error (0, 0, 254 _("getting get section header of section %zu: %s"), 255 elf_ndxscn (scn), elf_errmsg (-1)); 256 goto err_elf_close; 257 } 258 259 switch (shdr->sh_type) 260 { 261 case SHT_DYNAMIC: 262 if (!seen_dynamic) 263 { 264 seen_dynamic = true; 265 266 Elf_Data *data = elf_getdata (scn, NULL); 267 size_t entries = (shdr->sh_entsize == 0 268 ? 0 : shdr->sh_size / shdr->sh_entsize); 269 270 for (size_t cnt = 0; cnt < entries; ++cnt) 271 { 272 GElf_Dyn dynmem; 273 GElf_Dyn *dyn; 274 275 dyn = gelf_getdyn (data, cnt, &dynmem); 276 if (dyn == NULL) 277 { 278 error (0, 0, _("cannot read dynamic section: %s"), 279 elf_errmsg (-1)); 280 goto err_elf_close; 281 } 282 283 if (dyn->d_tag == DT_TEXTREL 284 || (dyn->d_tag == DT_FLAGS 285 && (dyn->d_un.d_val & DF_TEXTREL) != 0)) 286 have_textrel = true; 287 } 288 } 289 break; 290 291 case SHT_SYMTAB: 292 symscn = scn; 293 break; 294 } 295 } 296 297 if (!have_textrel) 298 { 299 error (0, 0, _("no text relocations reported in '%s'"), fname); 300 goto err_elf_close; 301 } 302 303 int fd2 = -1; 304 Elf *elf2 = NULL; 305 /* Get the address ranges for the loaded segments. */ 306 size_t nsegments_max = 10; 307 size_t nsegments = 0; 308 struct segments *segments = malloc (nsegments_max * sizeof (segments[0])); 309 if (segments == NULL) 310 error (1, errno, _("while reading ELF file")); 311 312 size_t phnum; 313 if (elf_getphdrnum (elf, &phnum) != 0) 314 error (1, 0, _("cannot get program header count: %s"), 315 elf_errmsg (-1)); 316 317 318 for (size_t i = 0; i < phnum; ++i) 319 { 320 GElf_Phdr phdr_mem; 321 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); 322 if (phdr == NULL) 323 { 324 error (0, 0, 325 _("cannot get program header index at offset %zd: %s"), 326 i, elf_errmsg (-1)); 327 result = 1; 328 goto next; 329 } 330 331 if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0) 332 { 333 if (nsegments == nsegments_max) 334 { 335 nsegments_max *= 2; 336 segments 337 = realloc (segments, nsegments_max * sizeof (segments[0])); 338 if (segments == NULL) 339 { 340 error (0, 0, _("\ 341cannot get program header index at offset %zd: %s"), 342 i, elf_errmsg (-1)); 343 result = 1; 344 goto next; 345 } 346 } 347 348 segments[nsegments].from = phdr->p_vaddr; 349 segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz; 350 ++nsegments; 351 } 352 } 353 354 if (nsegments > 0) 355 { 356 357 Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL); 358 /* Look for debuginfo files if the information is not the in 359 opened file itself. This makes only sense if the input file 360 is specified with an absolute path. */ 361 if (dw == NULL && fname[0] == '/') 362 { 363 char *difname = 364 xasprintf("%s%s/%s.debug", rootdir, debuginfo_root, fname); 365 fd2 = open (difname, O_RDONLY); 366 free (difname); 367 if (fd2 != -1 368 && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL) 369 dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL); 370 } 371 372 /* Look at all relocations and determine which modify 373 write-protected segments. */ 374 scn = NULL; 375 while ((scn = elf_nextscn (elf, scn)) != NULL) 376 { 377 /* Handle the section if it is a symbol table. */ 378 GElf_Shdr shdr_mem; 379 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 380 381 if (shdr == NULL) 382 { 383 error (0, 0, 384 _("cannot get section header of section %zu: %s"), 385 elf_ndxscn (scn), elf_errmsg (-1)); 386 result = 1; 387 goto next; 388 } 389 390 if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) 391 && symscn == NULL) 392 { 393 symscn = elf_getscn (elf, shdr->sh_link); 394 if (symscn == NULL) 395 { 396 error (0, 0, _("\ 397cannot get symbol table section %zu in '%s': %s"), 398 (size_t) shdr->sh_link, fname, elf_errmsg (-1)); 399 result = 1; 400 goto next; 401 } 402 } 403 404 if (shdr->sh_type == SHT_REL) 405 { 406 Elf_Data *data = elf_getdata (scn, NULL); 407 size_t entries = (shdr->sh_entsize == 0 408 ? 0 : shdr->sh_size / shdr->sh_entsize); 409 410 for (int cnt = 0; 411 (size_t) cnt < entries; ++cnt) 412 { 413 GElf_Rel rel_mem; 414 GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem); 415 if (rel == NULL) 416 { 417 error (0, 0, _("\ 418cannot get relocation at index %d in section %zu in '%s': %s"), 419 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1)); 420 result = 1; 421 goto next; 422 } 423 424 check_rel (nsegments, segments, rel->r_offset, elf, 425 symscn, dw, fname, more_than_one, &knownsrcs); 426 } 427 } 428 else if (shdr->sh_type == SHT_RELA) 429 { 430 Elf_Data *data = elf_getdata (scn, NULL); 431 size_t entries = (shdr->sh_entsize == 0 432 ? 0 : shdr->sh_size / shdr->sh_entsize); 433 434 for (int cnt = 0; (size_t) cnt < entries; ++cnt) 435 { 436 GElf_Rela rela_mem; 437 GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem); 438 if (rela == NULL) 439 { 440 error (0, 0, _("\ 441cannot get relocation at index %d in section %zu in '%s': %s"), 442 cnt, elf_ndxscn (scn), fname, elf_errmsg (-1)); 443 result = 1; 444 goto next; 445 } 446 447 check_rel (nsegments, segments, rela->r_offset, elf, 448 symscn, dw, fname, more_than_one, &knownsrcs); 449 } 450 } 451 } 452 453 dwarf_end (dw); 454 } 455 456 next: 457 elf_end (elf); 458 elf_end (elf2); 459 close (fd); 460 if (fd2 != -1) 461 close (fd2); 462 463 free (segments); 464 tdestroy (knownsrcs, noop); 465 466 return result; 467} 468 469 470static int 471ptrcompare (const void *p1, const void *p2) 472{ 473 if ((uintptr_t) p1 < (uintptr_t) p2) 474 return -1; 475 if ((uintptr_t) p1 > (uintptr_t) p2) 476 return 1; 477 return 0; 478} 479 480 481static void 482check_rel (size_t nsegments, struct segments segments[nsegments], 483 GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw, 484 const char *fname, bool more_than_one, void **knownsrcs) 485{ 486 for (size_t cnt = 0; cnt < nsegments; ++cnt) 487 if (segments[cnt].from <= addr && segments[cnt].to > addr) 488 { 489 Dwarf_Die die_mem; 490 Dwarf_Die *die; 491 Dwarf_Line *line; 492 const char *src; 493 494 if (more_than_one) 495 printf ("%s: ", fname); 496 497 if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL 498 && (line = dwarf_getsrc_die (die, addr)) != NULL 499 && (src = dwarf_linesrc (line, NULL, NULL)) != NULL) 500 { 501 /* There can be more than one relocation against one file. 502 Try to avoid multiple messages. And yes, the code uses 503 pointer comparison. */ 504 if (tfind (src, knownsrcs, ptrcompare) == NULL) 505 { 506 printf (_("%s not compiled with -fpic/-fPIC\n"), src); 507 tsearch (src, knownsrcs, ptrcompare); 508 } 509 return; 510 } 511 else 512 { 513 /* At least look at the symbol table to see which function 514 the modified address is in. */ 515 Elf_Data *symdata = elf_getdata (symscn, NULL); 516 GElf_Shdr shdr_mem; 517 GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); 518 if (shdr != NULL) 519 { 520 GElf_Addr lowaddr = 0; 521 int lowidx = -1; 522 GElf_Addr highaddr = ~0ul; 523 int highidx = -1; 524 GElf_Sym sym_mem; 525 GElf_Sym *sym; 526 size_t entries = (shdr->sh_entsize == 0 527 ? 0 : shdr->sh_size / shdr->sh_entsize); 528 529 for (int i = 0; (size_t) i < entries; ++i) 530 { 531 sym = gelf_getsym (symdata, i, &sym_mem); 532 if (sym == NULL) 533 continue; 534 535 if (sym->st_value < addr && sym->st_value > lowaddr) 536 { 537 lowaddr = sym->st_value; 538 lowidx = i; 539 } 540 if (sym->st_value > addr && sym->st_value < highaddr) 541 { 542 highaddr = sym->st_value; 543 highidx = i; 544 } 545 } 546 547 if (lowidx != -1) 548 { 549 sym = gelf_getsym (symdata, lowidx, &sym_mem); 550 assert (sym != NULL); 551 552 const char *lowstr = elf_strptr (elf, shdr->sh_link, 553 sym->st_name); 554 555 if (sym->st_value + sym->st_size > addr) 556 { 557 /* It is this function. */ 558 if (tfind (lowstr, knownsrcs, ptrcompare) == NULL) 559 { 560 printf (_("\ 561the file containing the function '%s' is not compiled with -fpic/-fPIC\n"), 562 lowstr); 563 tsearch (lowstr, knownsrcs, ptrcompare); 564 } 565 } 566 else if (highidx == -1) 567 printf (_("\ 568the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"), 569 lowstr); 570 else 571 { 572 sym = gelf_getsym (symdata, highidx, &sym_mem); 573 assert (sym != NULL); 574 575 printf (_("\ 576either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"), 577 lowstr, elf_strptr (elf, shdr->sh_link, 578 sym->st_name)); 579 } 580 return; 581 } 582 else if (highidx != -1) 583 { 584 sym = gelf_getsym (symdata, highidx, &sym_mem); 585 assert (sym != NULL); 586 587 printf (_("\ 588the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"), 589 elf_strptr (elf, shdr->sh_link, sym->st_name)); 590 return; 591 } 592 } 593 } 594 595 printf (_("\ 596a relocation modifies memory at offset %llu in a write-protected segment\n"), 597 (unsigned long long int) addr); 598 break; 599 } 600} 601 602 603#include "debugpred.h" 604