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