linux-kernel-modules.c revision 6258e7486eb3eed6e50005946795c5fbf73aa106
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 KSYMSFILE "/proc/kallsyms" 71#define MODULELIST "/proc/modules" 72#define SECADDRDIRFMT "/sys/module/%s/sections/" 73#define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */ 74 75 76/* Try to open the given file as it is or under the debuginfo directory. */ 77static int 78try_kernel_name (Dwfl *dwfl, char **fname) 79{ 80 if (*fname == NULL) 81 return -1; 82 83 /* Don't bother trying *FNAME itself here if the path will cause it to be 84 tried because we give its own basename as DEBUGLINK_FILE. */ 85 int fd = ((((dwfl->callbacks->debuginfo_path 86 ? *dwfl->callbacks->debuginfo_path : NULL) 87 ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1 88 : TEMP_FAILURE_RETRY (open64 (*fname, O_RDONLY))); 89 if (fd < 0) 90 { 91 char *debugfname = NULL; 92 Dwfl_Module fakemod = { .dwfl = dwfl }; 93 /* First try the file's unadorned basename as DEBUGLINK_FILE, 94 to look for "vmlinux" files. */ 95 fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, 96 *fname, basename (*fname), 0, 97 &debugfname); 98 if (fd < 0) 99 /* Next, let the call use the default of basename + ".debug", 100 to look for "vmlinux.debug" files. */ 101 fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, 102 *fname, NULL, 0, 103 &debugfname); 104 free (*fname); 105 *fname = debugfname; 106 } 107 108 return fd; 109} 110 111static inline const char * 112kernel_release (void) 113{ 114 /* Cache the `uname -r` string we'll use. */ 115 static struct utsname utsname; 116 if (utsname.release[0] == '\0' && uname (&utsname) != 0) 117 return NULL; 118 return utsname.release; 119} 120 121static int 122find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) 123{ 124 if ((release[0] == '/' 125 ? asprintf (fname, "%s/vmlinux", release) 126 : asprintf (fname, "/boot/vmlinux-%s", release)) < 0) 127 return -1; 128 129 int fd = try_kernel_name (dwfl, fname); 130 if (fd < 0 && release[0] != '/') 131 { 132 free (*fname); 133 if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0) 134 return -1; 135 fd = try_kernel_name (dwfl, fname); 136 } 137 138 return fd; 139} 140 141static int 142report_kernel (Dwfl *dwfl, const char *release, 143 int (*predicate) (const char *module, const char *file)) 144{ 145 if (dwfl == NULL) 146 return -1; 147 148 if (release == NULL) 149 { 150 release = kernel_release (); 151 if (release == NULL) 152 return errno; 153 } 154 155 char *fname; 156 int fd = find_kernel_elf (dwfl, release, &fname); 157 158 int result = 0; 159 if (fd < 0) 160 result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL)) 161 ? 0 : errno ?: ENOENT); 162 else 163 { 164 bool report = true; 165 166 if (predicate != NULL) 167 { 168 /* Let the predicate decide whether to use this one. */ 169 int want = (*predicate) (KERNEL_MODNAME, fname); 170 if (want < 0) 171 result = errno; 172 report = want > 0; 173 } 174 175 if (report 176 && INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME, 177 fname, fd, 0) == NULL) 178 { 179 close (fd); 180 result = -1; 181 } 182 } 183 184 free (fname); 185 186 return result; 187} 188 189/* Report a kernel and all its modules found on disk, for offline use. 190 If RELEASE starts with '/', it names a directory to look in; 191 if not, it names a directory to find under /lib/modules/; 192 if null, /lib/modules/`uname -r` is used. 193 Returns zero on success, -1 if dwfl_report_module failed, 194 or an errno code if finding the files on disk failed. */ 195int 196dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release, 197 int (*predicate) (const char *module, 198 const char *file)) 199{ 200 /* First report the kernel. */ 201 int result = report_kernel (dwfl, release, predicate); 202 if (result == 0) 203 { 204 /* Do "find /lib/modules/RELEASE/kernel -name *.ko". */ 205 206 char *modulesdir[] = { NULL, NULL }; 207 if (release[0] == '/') 208 modulesdir[0] = (char *) release; 209 else 210 { 211 if (asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release) < 0) 212 return errno; 213 } 214 215 FTS *fts = fts_open (modulesdir, FTS_LOGICAL | FTS_NOSTAT, NULL); 216 if (modulesdir[0] == (char *) release) 217 modulesdir[0] = NULL; 218 if (fts == NULL) 219 { 220 free (modulesdir[0]); 221 return errno; 222 } 223 224 FTSENT *f; 225 while ((f = fts_read (fts)) != NULL) 226 { 227 switch (f->fts_info) 228 { 229 case FTS_F: 230 case FTS_NSOK: 231 /* See if this file name matches "*.ko". */ 232 if (f->fts_namelen > 3 233 && !memcmp (f->fts_name + f->fts_namelen - 3, ".ko", 4)) 234 { 235 /* We have a .ko file to report. Following the algorithm 236 by which the kernel makefiles set KBUILD_MODNAME, we 237 replace all ',' or '-' with '_' in the file name and 238 call that the module name. Modules could well be 239 built using different embedded names than their file 240 names. To handle that, we would have to look at the 241 __this_module.name contents in the module's text. */ 242 243 char name[f->fts_namelen - 3 + 1]; 244 for (size_t i = 0; i < f->fts_namelen - 3U; ++i) 245 if (f->fts_name[i] == '-' || f->fts_name[i] == ',') 246 name[i] = '_'; 247 else 248 name[i] = f->fts_name[i]; 249 name[f->fts_namelen - 3] = '\0'; 250 251 if (predicate != NULL) 252 { 253 /* Let the predicate decide whether to use this one. */ 254 int want = (*predicate) (name, f->fts_path); 255 if (want < 0) 256 { 257 result = -1; 258 break; 259 } 260 if (!want) 261 continue; 262 } 263 264 if (dwfl_report_offline (dwfl, name, 265 f->fts_path, -1) == NULL) 266 { 267 result = -1; 268 break; 269 } 270 } 271 continue; 272 273 case FTS_ERR: 274 case FTS_DNR: 275 case FTS_NS: 276 result = f->fts_errno; 277 break; 278 279 default: 280 continue; 281 } 282 283 /* We only get here in error cases. */ 284 break; 285 } 286 fts_close (fts); 287 free (modulesdir[0]); 288 } 289 290 return result; 291} 292INTDEF (dwfl_linux_kernel_report_offline) 293 294 295/* Grovel around to guess the bounds of the runtime kernel image. */ 296static int 297intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end) 298{ 299 FILE *f = fopen (KSYMSFILE, "r"); 300 if (f == NULL) 301 return errno; 302 303 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 304 305 char *line = NULL; 306 size_t linesz = 0; 307 size_t n = getline (&line, &linesz, f); 308 Dwarf_Addr first; 309 char *p = NULL; 310 int result = 0; 311 if (n > 0 && (first = strtoull (line, &p, 16)) > 0 && p > line) 312 { 313 Dwarf_Addr last = 0; 314 while ((n = getline (&line, &linesz, f)) > 1 && line[n - 2] != ']') 315 { 316 p = NULL; 317 last = strtoull (line, &p, 16); 318 if (p == NULL || p == line || last == 0) 319 { 320 result = -1; 321 break; 322 } 323 } 324 if ((n == 0 && feof_unlocked (f)) || (n > 1 && line[n - 2] == ']')) 325 { 326 Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE); 327 first &= -(Dwarf_Addr) round_kernel; 328 last += round_kernel - 1; 329 last &= -(Dwarf_Addr) round_kernel; 330 *start = first; 331 *end = last; 332 result = 0; 333 } 334 } 335 free (line); 336 337 if (result == -1) 338 result = ferror_unlocked (f) ? errno : ENOEXEC; 339 340 fclose (f); 341 342 return result; 343} 344 345int 346dwfl_linux_kernel_report_kernel (Dwfl *dwfl) 347{ 348 Dwarf_Addr start; 349 Dwarf_Addr end; 350 inline int report (void) 351 { 352 return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, 353 start, end) == NULL ? -1 : 0; 354 } 355 356 /* This is a bit of a kludge. If we already reported the kernel, 357 don't bother figuring it out again--it never changes. */ 358 for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) 359 if (!strcmp (m->name, KERNEL_MODNAME)) 360 { 361 start = m->low_addr; 362 end = m->high_addr; 363 return report (); 364 } 365 366 /* Try to figure out the bounds of the kernel image without 367 looking for any vmlinux file. */ 368 int result = intuit_kernel_bounds (&start, &end); 369 if (result == 0) 370 return report (); 371 if (result != ENOENT) 372 return result; 373 374 /* Find the ELF file for the running kernel and dwfl_report_elf it. */ 375 return report_kernel (dwfl, NULL, NULL); 376} 377INTDEF (dwfl_linux_kernel_report_kernel) 378 379 380/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */ 381 382int 383dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)), 384 void **userdata __attribute__ ((unused)), 385 const char *module_name, 386 Dwarf_Addr base __attribute__ ((unused)), 387 char **file_name, 388 Elf **elfp __attribute__ ((unused))) 389{ 390 const char *release = kernel_release (); 391 if (release == NULL) 392 return errno; 393 394 if (!strcmp (module_name, KERNEL_MODNAME)) 395 return find_kernel_elf (mod->dwfl, release, file_name); 396 397 /* Do "find /lib/modules/`uname -r`/kernel -name MODULE_NAME.ko". */ 398 399 char *modulesdir[] = { NULL, NULL }; 400 if (asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release) < 0) 401 return -1; 402 403 FTS *fts = fts_open (modulesdir, FTS_LOGICAL | FTS_NOSTAT, NULL); 404 if (fts == NULL) 405 { 406 free (modulesdir[0]); 407 return -1; 408 } 409 410 size_t namelen = strlen (module_name); 411 412 /* This is a kludge. There is no actual necessary relationship between 413 the name of the .ko file installed and the module name the kernel 414 knows it by when it's loaded. The kernel's only idea of the module 415 name comes from the name embedded in the object's magic 416 .gnu.linkonce.this_module section. 417 418 In practice, these module names match the .ko file names except for 419 some using '_' and some using '-'. So our cheap kludge is to look for 420 two files when either a '_' or '-' appears in a module name, one using 421 only '_' and one only using '-'. */ 422 423 char alternate_name[namelen + 1]; 424 inline bool subst_name (char from, char to) 425 { 426 const char *n = memchr (module_name, from, namelen); 427 if (n == NULL) 428 return false; 429 char *a = mempcpy (alternate_name, module_name, n - module_name); 430 *a++ = to; 431 ++n; 432 const char *p; 433 while ((p = memchr (n, from, namelen - (n - module_name))) != NULL) 434 { 435 a = mempcpy (a, n, p - n); 436 *a++ = to; 437 n = p + 1; 438 } 439 memcpy (a, n, namelen - (n - module_name) + 1); 440 return true; 441 } 442 if (!subst_name ('-', '_') && !subst_name ('_', '-')) 443 alternate_name[0] = '\0'; 444 445 FTSENT *f; 446 int error = ENOENT; 447 while ((f = fts_read (fts)) != NULL) 448 { 449 error = ENOENT; 450 switch (f->fts_info) 451 { 452 case FTS_F: 453 case FTS_NSOK: 454 /* See if this file name is "MODULE_NAME.ko". */ 455 if (f->fts_namelen == namelen + 3 456 && !memcmp (f->fts_name + namelen, ".ko", 4) 457 && (!memcmp (f->fts_name, module_name, namelen) 458 || !memcmp (f->fts_name, alternate_name, namelen))) 459 { 460 int fd = open64 (f->fts_accpath, O_RDONLY); 461 *file_name = strdup (f->fts_path); 462 fts_close (fts); 463 free (modulesdir[0]); 464 if (fd < 0) 465 free (*file_name); 466 else if (*file_name == NULL) 467 { 468 close (fd); 469 fd = -1; 470 } 471 return fd; 472 } 473 break; 474 475 case FTS_ERR: 476 case FTS_DNR: 477 case FTS_NS: 478 error = f->fts_errno; 479 break; 480 481 default: 482 break; 483 } 484 } 485 486 fts_close (fts); 487 free (modulesdir[0]); 488 errno = error; 489 return -1; 490} 491INTDEF (dwfl_linux_kernel_find_elf) 492 493 494/* Dwfl_Callbacks.section_address for kernel modules in the running Linux. 495 We read the information from /sys/module directly. */ 496 497int 498dwfl_linux_kernel_module_section_address 499(Dwfl_Module *mod __attribute__ ((unused)), 500 void **userdata __attribute__ ((unused)), 501 const char *modname, Dwarf_Addr base __attribute__ ((unused)), 502 const char *secname, Elf32_Word shndx __attribute__ ((unused)), 503 const GElf_Shdr *shdr __attribute__ ((unused)), 504 Dwarf_Addr *addr) 505{ 506 char *sysfile; 507 if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0) 508 return ENOMEM; 509 510 FILE *f = fopen (sysfile, "r"); 511 free (sysfile); 512 513 if (f == NULL) 514 { 515 if (errno == ENOENT) 516 { 517 /* The .modinfo and .data.percpu sections are never kept 518 loaded in the kernel. If the kernel was compiled without 519 CONFIG_MODULE_UNLOAD, the .exit.* sections are not 520 actually loaded at all. 521 522 Setting *ADDR to -1 tells the caller this section is 523 actually absent from memory. */ 524 525 if (!strcmp (secname, ".modinfo") 526 || !strcmp (secname, ".data.percpu") 527 || !strncmp (secname, ".exit", 5)) 528 { 529 *addr = (Dwarf_Addr) -1l; 530 return DWARF_CB_OK; 531 } 532 533 /* The goofy PPC64 module_frob_arch_sections function tweaks 534 the section names as a way to control other kernel code's 535 behavior, and this cruft leaks out into the /sys information. 536 The file name for ".init*" may actually look like "_init*". */ 537 538 const bool is_init = !strncmp (secname, ".init", 5); 539 if (is_init) 540 { 541 if (asprintf (&sysfile, SECADDRDIRFMT "_%s", 542 modname, &secname[1]) < 0) 543 return ENOMEM; 544 f = fopen (sysfile, "r"); 545 free (sysfile); 546 if (f != NULL) 547 goto ok; 548 } 549 550 /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1. 551 In case that size increases in the future, look for longer 552 truncated names first. */ 553 size_t namelen = strlen (secname); 554 if (namelen >= MODULE_SECT_NAME_LEN) 555 { 556 int len = asprintf (&sysfile, SECADDRDIRFMT "%s", 557 modname, secname); 558 if (len < 0) 559 return ENOMEM; 560 char *end = sysfile + len; 561 do 562 { 563 *--end = '\0'; 564 f = fopen (sysfile, "r"); 565 if (is_init && f == NULL && errno == ENOENT) 566 { 567 sysfile[len - namelen] = '_'; 568 f = fopen (sysfile, "r"); 569 sysfile[len - namelen] = '.'; 570 } 571 } 572 while (f == NULL && errno == ENOENT 573 && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN); 574 free (sysfile); 575 576 if (f != NULL) 577 goto ok; 578 } 579 } 580 581 return DWARF_CB_ABORT; 582 } 583 584 ok: 585 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 586 587 int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0 588 : ferror_unlocked (f) ? errno : ENOEXEC); 589 fclose (f); 590 591 if (result == 0) 592 return DWARF_CB_OK; 593 594 errno = result; 595 return DWARF_CB_ABORT; 596} 597INTDEF (dwfl_linux_kernel_module_section_address) 598 599int 600dwfl_linux_kernel_report_modules (Dwfl *dwfl) 601{ 602 FILE *f = fopen (MODULELIST, "r"); 603 if (f == NULL) 604 return errno; 605 606 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 607 608 int result = 0; 609 Dwarf_Addr modaddr; 610 unsigned long int modsz; 611 char modname[128]; 612 while (fscanf (f, "%128s %lu %*s %*s %*s %" PRIx64 "\n", 613 modname, &modsz, &modaddr) == 3) 614 if (INTUSE(dwfl_report_module) (dwfl, modname, 615 modaddr, modaddr + modsz) == NULL) 616 { 617 result = -1; 618 break; 619 } 620 621 if (result == 0) 622 result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC; 623 624 fclose (f); 625 626 return result; 627} 628INTDEF (dwfl_linux_kernel_report_modules) 629