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