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