linux-kernel-modules.c revision 705364b2bfc805e0c07ea64ec01e3080e5609bc3
1/* Standard libdwfl callbacks for debugging the running Linux kernel. 2 Copyright (C) 2005-2009 Red Hat, Inc. 3 This file is part of Red Hat elfutils. 4 5 Red Hat elfutils is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by the 7 Free Software Foundation; version 2 of the License. 8 9 Red Hat elfutils is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along 15 with Red Hat elfutils; if not, write to the Free Software Foundation, 16 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. 17 18 In addition, as a special exception, Red Hat, Inc. gives You the 19 additional right to link the code of Red Hat elfutils with code licensed 20 under any Open Source Initiative certified open source license 21 (http://www.opensource.org/licenses/index.php) which requires the 22 distribution of source code with any binary distribution and to 23 distribute linked combinations of the two. Non-GPL Code permitted under 24 this exception must only link to the code of Red Hat elfutils through 25 those well defined interfaces identified in the file named EXCEPTION 26 found in the source code files (the "Approved Interfaces"). The files 27 of Non-GPL Code may instantiate templates or use macros or inline 28 functions from the Approved Interfaces without causing the resulting 29 work to be covered by the GNU General Public License. Only Red Hat, 30 Inc. may make changes or additions to the list of Approved Interfaces. 31 Red Hat's grant of this exception is conditioned upon your not adding 32 any new exceptions. If you wish to add a new Approved Interface or 33 exception, please contact Red Hat. You must obey the GNU General Public 34 License in all respects for all of the Red Hat elfutils code and other 35 code used in conjunction with Red Hat elfutils except the Non-GPL Code 36 covered by this exception. If you modify this file, you may extend this 37 exception to your version of the file, but you are not obligated to do 38 so. If you do not wish to provide this exception without modification, 39 you must delete this exception statement from your version and license 40 this file solely under the GPL without exception. 41 42 Red Hat elfutils is an included package of the Open Invention Network. 43 An included package of the Open Invention Network is a package for which 44 Open Invention Network licensees cross-license their patents. No patent 45 license is granted, either expressly or impliedly, by designation as an 46 included package. Should you wish to participate in the Open Invention 47 Network licensing program, please visit www.openinventionnetwork.com 48 <http://www.openinventionnetwork.com>. */ 49 50/* We include this before config.h because it can't handle _FILE_OFFSET_BITS. 51 Everything we need here is fine if its declarations just come first. */ 52 53#include <fts.h> 54 55#include <config.h> 56 57#include "libdwflP.h" 58#include <inttypes.h> 59#include <errno.h> 60#include <stdio.h> 61#include <stdio_ext.h> 62#include <string.h> 63#include <stdlib.h> 64#include <sys/utsname.h> 65#include <fcntl.h> 66#include <unistd.h> 67 68 69#define KERNEL_MODNAME "kernel" 70 71#define MODULEDIRFMT "/lib/modules/%s" 72 73#define KNOTESFILE "/sys/kernel/notes" 74#define MODNOTESFMT "/sys/module/%s/notes" 75#define KSYMSFILE "/proc/kallsyms" 76#define MODULELIST "/proc/modules" 77#define SECADDRDIRFMT "/sys/module/%s/sections/" 78#define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */ 79 80 81/* Try to open the given file as it is or under the debuginfo directory. */ 82static int 83try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug) 84{ 85 if (*fname == NULL) 86 return -1; 87 88 /* Don't bother trying *FNAME itself here if the path will cause it to be 89 tried because we give its own basename as DEBUGLINK_FILE. */ 90 int fd = ((((dwfl->callbacks->debuginfo_path 91 ? *dwfl->callbacks->debuginfo_path : NULL) 92 ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1 93 : TEMP_FAILURE_RETRY (open64 (*fname, O_RDONLY))); 94 if (fd < 0) 95 { 96 char *debugfname = NULL; 97 Dwfl_Module fakemod = { .dwfl = dwfl }; 98 /* First try the file's unadorned basename as DEBUGLINK_FILE, 99 to look for "vmlinux" files. */ 100 fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, 101 *fname, basename (*fname), 0, 102 &debugfname); 103 if (fd < 0 && try_debug) 104 /* Next, let the call use the default of basename + ".debug", 105 to look for "vmlinux.debug" files. */ 106 fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, 107 *fname, NULL, 0, 108 &debugfname); 109 free (*fname); 110 *fname = debugfname; 111 } 112 113 return fd; 114} 115 116static inline const char * 117kernel_release (void) 118{ 119 /* Cache the `uname -r` string we'll use. */ 120 static struct utsname utsname; 121 if (utsname.release[0] == '\0' && uname (&utsname) != 0) 122 return NULL; 123 return utsname.release; 124} 125 126static int 127find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) 128{ 129 if ((release[0] == '/' 130 ? asprintf (fname, "%s/vmlinux", release) 131 : asprintf (fname, "/boot/vmlinux-%s", release)) < 0) 132 return -1; 133 134 int fd = try_kernel_name (dwfl, fname, true); 135 if (fd < 0 && release[0] != '/') 136 { 137 free (*fname); 138 if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0) 139 return -1; 140 fd = try_kernel_name (dwfl, fname, true); 141 } 142 143 return fd; 144} 145 146static int 147get_release (Dwfl *dwfl, const char **release) 148{ 149 if (dwfl == NULL) 150 return -1; 151 152 const char *release_string = release == NULL ? NULL : *release; 153 if (release_string == NULL) 154 { 155 release_string = kernel_release (); 156 if (release_string == NULL) 157 return errno; 158 if (release != NULL) 159 *release = release_string; 160 } 161 162 return 0; 163} 164 165static int 166report_kernel (Dwfl *dwfl, const char **release, 167 int (*predicate) (const char *module, const char *file)) 168{ 169 int result = get_release (dwfl, release); 170 if (unlikely (result != 0)) 171 return result; 172 173 char *fname; 174 int fd = find_kernel_elf (dwfl, *release, &fname); 175 176 if (fd < 0) 177 result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL)) 178 ? 0 : errno ?: ENOENT); 179 else 180 { 181 bool report = true; 182 183 if (predicate != NULL) 184 { 185 /* Let the predicate decide whether to use this one. */ 186 int want = (*predicate) (KERNEL_MODNAME, fname); 187 if (want < 0) 188 result = errno; 189 report = want > 0; 190 } 191 192 if (report) 193 { 194 Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME, 195 fname, fd, 0); 196 if (mod == NULL) 197 result = -1; 198 else 199 /* The kernel is ET_EXEC, but always treat it as relocatable. */ 200 mod->e_type = ET_DYN; 201 } 202 203 if (!report || result < 0) 204 close (fd); 205 } 206 207 free (fname); 208 209 return result; 210} 211 212/* Look for a kernel debug archive. If we find one, report all its modules. 213 If not, return ENOENT. */ 214static int 215report_kernel_archive (Dwfl *dwfl, const char **release, 216 int (*predicate) (const char *module, const char *file)) 217{ 218 int result = get_release (dwfl, release); 219 if (unlikely (result != 0)) 220 return result; 221 222 char *archive; 223 if (unlikely ((*release)[0] == '/' 224 ? asprintf (&archive, "%s/debug.a", *release) 225 : asprintf (&archive, MODULEDIRFMT "/debug.a", *release)) < 0) 226 return ENOMEM; 227 228 int fd = try_kernel_name (dwfl, &archive, false); 229 if (fd < 0) 230 result = errno ?: ENOENT; 231 else 232 { 233 /* We have the archive file open! */ 234 Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd, 235 true, predicate); 236 if (unlikely (last == NULL)) 237 result = -1; 238 else 239 { 240 /* Find the kernel and move it to the head of the list. */ 241 Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp; 242 for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next)) 243 if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel")) 244 { 245 *prevp = m->next; 246 m->next = *tailp; 247 *tailp = m; 248 break; 249 } 250 } 251 } 252 253 free (archive); 254 return result; 255} 256 257static size_t 258check_suffix (const FTSENT *f, size_t namelen) 259{ 260#define TRY(sfx) \ 261 if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1 \ 262 : f->fts_namelen >= sizeof sfx) \ 263 && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1), \ 264 sfx, sizeof sfx)) \ 265 return sizeof sfx - 1 266 267 TRY (".ko"); 268#if USE_ZLIB 269 TRY (".ko.gz"); 270#endif 271#if USE_BZLIB 272 TRY (".ko.bz2"); 273#endif 274 275 return 0; 276 277#undef TRY 278} 279 280/* Report a kernel and all its modules found on disk, for offline use. 281 If RELEASE starts with '/', it names a directory to look in; 282 if not, it names a directory to find under /lib/modules/; 283 if null, /lib/modules/`uname -r` is used. 284 Returns zero on success, -1 if dwfl_report_module failed, 285 or an errno code if finding the files on disk failed. */ 286int 287dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release, 288 int (*predicate) (const char *module, 289 const char *file)) 290{ 291 int result = report_kernel_archive (dwfl, &release, predicate); 292 if (result != ENOENT) 293 return result; 294 295 /* First report the kernel. */ 296 result = report_kernel (dwfl, &release, predicate); 297 if (result == 0) 298 { 299 /* Do "find /lib/modules/RELEASE -name *.ko". */ 300 301 char *modulesdir[] = { NULL, NULL }; 302 if (release[0] == '/') 303 modulesdir[0] = (char *) release; 304 else 305 { 306 if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0) 307 return errno; 308 } 309 310 FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL); 311 if (modulesdir[0] == (char *) release) 312 modulesdir[0] = NULL; 313 if (fts == NULL) 314 { 315 free (modulesdir[0]); 316 return errno; 317 } 318 319 FTSENT *f; 320 while ((f = fts_read (fts)) != NULL) 321 { 322 switch (f->fts_info) 323 { 324 case FTS_F: 325 case FTS_SL: 326 case FTS_NSOK:; 327 /* See if this file name matches "*.ko". */ 328 const size_t suffix = check_suffix (f, 0); 329 if (suffix) 330 { 331 /* We have a .ko file to report. Following the algorithm 332 by which the kernel makefiles set KBUILD_MODNAME, we 333 replace all ',' or '-' with '_' in the file name and 334 call that the module name. Modules could well be 335 built using different embedded names than their file 336 names. To handle that, we would have to look at the 337 __this_module.name contents in the module's text. */ 338 339 char name[f->fts_namelen - suffix + 1]; 340 for (size_t i = 0; i < f->fts_namelen - 3U; ++i) 341 if (f->fts_name[i] == '-' || f->fts_name[i] == ',') 342 name[i] = '_'; 343 else 344 name[i] = f->fts_name[i]; 345 name[f->fts_namelen - suffix] = '\0'; 346 347 if (predicate != NULL) 348 { 349 /* Let the predicate decide whether to use this one. */ 350 int want = (*predicate) (name, f->fts_path); 351 if (want < 0) 352 { 353 result = -1; 354 break; 355 } 356 if (!want) 357 continue; 358 } 359 360 if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL) 361 { 362 result = -1; 363 break; 364 } 365 } 366 continue; 367 368 case FTS_ERR: 369 case FTS_DNR: 370 case FTS_NS: 371 result = f->fts_errno; 372 break; 373 374 case FTS_SLNONE: 375 default: 376 continue; 377 } 378 379 /* We only get here in error cases. */ 380 break; 381 } 382 fts_close (fts); 383 free (modulesdir[0]); 384 } 385 386 return result; 387} 388INTDEF (dwfl_linux_kernel_report_offline) 389 390 391/* Grovel around to guess the bounds of the runtime kernel image. */ 392static int 393intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes) 394{ 395 FILE *f = fopen (KSYMSFILE, "r"); 396 if (f == NULL) 397 return errno; 398 399 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 400 401 *notes = 0; 402 403 char *line = NULL; 404 size_t linesz = 0; 405 size_t n = getline (&line, &linesz, f); 406 Dwarf_Addr first; 407 char *p = NULL; 408 int result = 0; 409 if (n > 0 && (first = strtoull (line, &p, 16)) > 0 && p > line) 410 { 411 Dwarf_Addr last = 0; 412 while ((n = getline (&line, &linesz, f)) > 1 && line[n - 2] != ']') 413 { 414 p = NULL; 415 last = strtoull (line, &p, 16); 416 if (p == NULL || p == line || last == 0) 417 { 418 result = -1; 419 break; 420 } 421 422 if (*notes == 0) 423 { 424 const char *sym = (strsep (&p, " \t\n") 425 ? strsep (&p, " \t\n") : NULL); 426 if (sym != NULL && !strcmp (sym, "__start_notes")) 427 *notes = last; 428 } 429 } 430 if ((n == 0 && feof_unlocked (f)) || (n > 1 && line[n - 2] == ']')) 431 { 432 Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE); 433 first &= -(Dwarf_Addr) round_kernel; 434 last += round_kernel - 1; 435 last &= -(Dwarf_Addr) round_kernel; 436 *start = first; 437 *end = last; 438 result = 0; 439 } 440 } 441 free (line); 442 443 if (result == -1) 444 result = ferror_unlocked (f) ? errno : ENOEXEC; 445 446 fclose (f); 447 448 return result; 449} 450 451 452/* Look for a build ID note in NOTESFILE and associate the ID with MOD. */ 453static int 454check_notes (Dwfl_Module *mod, const char *notesfile, 455 Dwarf_Addr vaddr, const char *secname) 456{ 457 int fd = open64 (notesfile, O_RDONLY); 458 if (fd < 0) 459 return 1; 460 461 assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr)); 462 assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr)); 463 union 464 { 465 GElf_Nhdr nhdr; 466 unsigned char data[8192]; 467 } buf; 468 469 ssize_t n = read (fd, buf.data, sizeof buf); 470 close (fd); 471 472 if (n <= 0) 473 return 1; 474 475 unsigned char *p = buf.data; 476 while (p < &buf.data[n]) 477 { 478 /* No translation required since we are reading the native kernel. */ 479 GElf_Nhdr *nhdr = (void *) p; 480 p += sizeof *nhdr; 481 unsigned char *name = p; 482 p += (nhdr->n_namesz + 3) & -4U; 483 unsigned char *bits = p; 484 p += (nhdr->n_descsz + 3) & -4U; 485 486 if (p <= &buf.data[n] 487 && nhdr->n_type == NT_GNU_BUILD_ID 488 && nhdr->n_namesz == sizeof "GNU" 489 && !memcmp (name, "GNU", sizeof "GNU")) 490 { 491 /* Found it. For a module we must figure out its VADDR now. */ 492 493 if (secname != NULL 494 && (INTUSE(dwfl_linux_kernel_module_section_address) 495 (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0 496 || vaddr == (GElf_Addr) -1l)) 497 vaddr = 0; 498 499 if (vaddr != 0) 500 vaddr += bits - buf.data; 501 return INTUSE(dwfl_module_report_build_id) (mod, bits, 502 nhdr->n_descsz, vaddr); 503 } 504 } 505 506 return 0; 507} 508 509/* Look for a build ID for the kernel. */ 510static int 511check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr) 512{ 513 return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0; 514} 515 516/* Look for a build ID for a loaded kernel module. */ 517static int 518check_module_notes (Dwfl_Module *mod) 519{ 520 char *dirs[2] = { NULL, NULL }; 521 if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0) 522 return ENOMEM; 523 524 FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL); 525 if (fts == NULL) 526 { 527 free (dirs[0]); 528 return 0; 529 } 530 531 int result = 0; 532 FTSENT *f; 533 while ((f = fts_read (fts)) != NULL) 534 { 535 switch (f->fts_info) 536 { 537 case FTS_F: 538 case FTS_SL: 539 case FTS_NSOK: 540 result = check_notes (mod, f->fts_accpath, 0, f->fts_name); 541 if (result > 0) /* Nothing found. */ 542 { 543 result = 0; 544 continue; 545 } 546 break; 547 548 case FTS_ERR: 549 case FTS_DNR: 550 result = f->fts_errno; 551 break; 552 553 case FTS_NS: 554 case FTS_SLNONE: 555 default: 556 continue; 557 } 558 559 /* We only get here when finished or in error cases. */ 560 break; 561 } 562 fts_close (fts); 563 free (dirs[0]); 564 565 return result; 566} 567 568int 569dwfl_linux_kernel_report_kernel (Dwfl *dwfl) 570{ 571 Dwarf_Addr start; 572 Dwarf_Addr end; 573 inline Dwfl_Module *report (void) 574 { 575 return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end); 576 } 577 578 /* This is a bit of a kludge. If we already reported the kernel, 579 don't bother figuring it out again--it never changes. */ 580 for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) 581 if (!strcmp (m->name, KERNEL_MODNAME)) 582 { 583 start = m->low_addr; 584 end = m->high_addr; 585 return report () == NULL ? -1 : 0; 586 } 587 588 /* Try to figure out the bounds of the kernel image without 589 looking for any vmlinux file. */ 590 Dwarf_Addr notes; 591 /* The compiler cannot deduce that if intuit_kernel_bounds returns 592 zero NOTES will be initialized. Fake the initialization. */ 593 asm ("" : "=m" (notes)); 594 int result = intuit_kernel_bounds (&start, &end, ¬es); 595 if (result == 0) 596 { 597 Dwfl_Module *mod = report (); 598 return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes); 599 } 600 if (result != ENOENT) 601 return result; 602 603 /* Find the ELF file for the running kernel and dwfl_report_elf it. */ 604 return report_kernel (dwfl, NULL, NULL); 605} 606INTDEF (dwfl_linux_kernel_report_kernel) 607 608 609/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */ 610 611int 612dwfl_linux_kernel_find_elf (Dwfl_Module *mod, 613 void **userdata __attribute__ ((unused)), 614 const char *module_name, 615 Dwarf_Addr base __attribute__ ((unused)), 616 char **file_name, Elf **elfp) 617{ 618 if (mod->build_id_len > 0) 619 { 620 int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0, 621 file_name, elfp); 622 if (fd >= 0 || errno != 0) 623 return fd; 624 } 625 626 const char *release = kernel_release (); 627 if (release == NULL) 628 return errno; 629 630 if (!strcmp (module_name, KERNEL_MODNAME)) 631 return find_kernel_elf (mod->dwfl, release, file_name); 632 633 /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */ 634 635 char *modulesdir[] = { NULL, NULL }; 636 if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0) 637 return -1; 638 639 FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL); 640 if (fts == NULL) 641 { 642 free (modulesdir[0]); 643 return -1; 644 } 645 646 size_t namelen = strlen (module_name); 647 648 /* This is a kludge. There is no actual necessary relationship between 649 the name of the .ko file installed and the module name the kernel 650 knows it by when it's loaded. The kernel's only idea of the module 651 name comes from the name embedded in the object's magic 652 .gnu.linkonce.this_module section. 653 654 In practice, these module names match the .ko file names except for 655 some using '_' and some using '-'. So our cheap kludge is to look for 656 two files when either a '_' or '-' appears in a module name, one using 657 only '_' and one only using '-'. */ 658 659 char alternate_name[namelen + 1]; 660 inline bool subst_name (char from, char to) 661 { 662 const char *n = memchr (module_name, from, namelen); 663 if (n == NULL) 664 return false; 665 char *a = mempcpy (alternate_name, module_name, n - module_name); 666 *a++ = to; 667 ++n; 668 const char *p; 669 while ((p = memchr (n, from, namelen - (n - module_name))) != NULL) 670 { 671 a = mempcpy (a, n, p - n); 672 *a++ = to; 673 n = p + 1; 674 } 675 memcpy (a, n, namelen - (n - module_name) + 1); 676 return true; 677 } 678 if (!subst_name ('-', '_') && !subst_name ('_', '-')) 679 alternate_name[0] = '\0'; 680 681 FTSENT *f; 682 int error = ENOENT; 683 while ((f = fts_read (fts)) != NULL) 684 { 685 error = ENOENT; 686 switch (f->fts_info) 687 { 688 case FTS_F: 689 case FTS_SL: 690 case FTS_NSOK: 691 /* See if this file name is "MODULE_NAME.ko". */ 692 if (check_suffix (f, namelen) 693 && (!memcmp (f->fts_name, module_name, namelen) 694 || !memcmp (f->fts_name, alternate_name, namelen))) 695 { 696 int fd = open64 (f->fts_accpath, O_RDONLY); 697 *file_name = strdup (f->fts_path); 698 fts_close (fts); 699 free (modulesdir[0]); 700 if (fd < 0) 701 free (*file_name); 702 else if (*file_name == NULL) 703 { 704 close (fd); 705 fd = -1; 706 } 707 return fd; 708 } 709 break; 710 711 case FTS_ERR: 712 case FTS_DNR: 713 case FTS_NS: 714 error = f->fts_errno; 715 break; 716 717 case FTS_SLNONE: 718 default: 719 break; 720 } 721 } 722 723 fts_close (fts); 724 free (modulesdir[0]); 725 errno = error; 726 return -1; 727} 728INTDEF (dwfl_linux_kernel_find_elf) 729 730 731/* Dwfl_Callbacks.section_address for kernel modules in the running Linux. 732 We read the information from /sys/module directly. */ 733 734int 735dwfl_linux_kernel_module_section_address 736(Dwfl_Module *mod __attribute__ ((unused)), 737 void **userdata __attribute__ ((unused)), 738 const char *modname, Dwarf_Addr base __attribute__ ((unused)), 739 const char *secname, Elf32_Word shndx __attribute__ ((unused)), 740 const GElf_Shdr *shdr __attribute__ ((unused)), 741 Dwarf_Addr *addr) 742{ 743 char *sysfile; 744 if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0) 745 return DWARF_CB_ABORT; 746 747 FILE *f = fopen (sysfile, "r"); 748 free (sysfile); 749 750 if (f == NULL) 751 { 752 if (errno == ENOENT) 753 { 754 /* The .modinfo and .data.percpu sections are never kept 755 loaded in the kernel. If the kernel was compiled without 756 CONFIG_MODULE_UNLOAD, the .exit.* sections are not 757 actually loaded at all. 758 759 Setting *ADDR to -1 tells the caller this section is 760 actually absent from memory. */ 761 762 if (!strcmp (secname, ".modinfo") 763 || !strcmp (secname, ".data.percpu") 764 || !strncmp (secname, ".exit", 5)) 765 { 766 *addr = (Dwarf_Addr) -1l; 767 return DWARF_CB_OK; 768 } 769 770 /* The goofy PPC64 module_frob_arch_sections function tweaks 771 the section names as a way to control other kernel code's 772 behavior, and this cruft leaks out into the /sys information. 773 The file name for ".init*" may actually look like "_init*". */ 774 775 const bool is_init = !strncmp (secname, ".init", 5); 776 if (is_init) 777 { 778 if (asprintf (&sysfile, SECADDRDIRFMT "_%s", 779 modname, &secname[1]) < 0) 780 return ENOMEM; 781 f = fopen (sysfile, "r"); 782 free (sysfile); 783 if (f != NULL) 784 goto ok; 785 } 786 787 /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1. 788 In case that size increases in the future, look for longer 789 truncated names first. */ 790 size_t namelen = strlen (secname); 791 if (namelen >= MODULE_SECT_NAME_LEN) 792 { 793 int len = asprintf (&sysfile, SECADDRDIRFMT "%s", 794 modname, secname); 795 if (len < 0) 796 return DWARF_CB_ABORT; 797 char *end = sysfile + len; 798 do 799 { 800 *--end = '\0'; 801 f = fopen (sysfile, "r"); 802 if (is_init && f == NULL && errno == ENOENT) 803 { 804 sysfile[len - namelen] = '_'; 805 f = fopen (sysfile, "r"); 806 sysfile[len - namelen] = '.'; 807 } 808 } 809 while (f == NULL && errno == ENOENT 810 && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN); 811 free (sysfile); 812 813 if (f != NULL) 814 goto ok; 815 } 816 } 817 818 return DWARF_CB_ABORT; 819 } 820 821 ok: 822 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 823 824 int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0 825 : ferror_unlocked (f) ? errno : ENOEXEC); 826 fclose (f); 827 828 if (result == 0) 829 return DWARF_CB_OK; 830 831 errno = result; 832 return DWARF_CB_ABORT; 833} 834INTDEF (dwfl_linux_kernel_module_section_address) 835 836int 837dwfl_linux_kernel_report_modules (Dwfl *dwfl) 838{ 839 FILE *f = fopen (MODULELIST, "r"); 840 if (f == NULL) 841 return errno; 842 843 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 844 845 int result = 0; 846 Dwarf_Addr modaddr; 847 unsigned long int modsz; 848 char modname[128]; 849 char *line = NULL; 850 size_t linesz = 0; 851 /* We can't just use fscanf here because it's not easy to distinguish \n 852 from other whitespace so as to take the optional word following the 853 address but always stop at the end of the line. */ 854 while (getline (&line, &linesz, f) > 0 855 && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n", 856 modname, &modsz, &modaddr) == 3) 857 { 858 Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname, 859 modaddr, modaddr + modsz); 860 if (mod == NULL) 861 { 862 result = -1; 863 break; 864 } 865 866 result = check_module_notes (mod); 867 } 868 free (line); 869 870 if (result == 0) 871 result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC; 872 873 fclose (f); 874 875 return result; 876} 877INTDEF (dwfl_linux_kernel_report_modules) 878