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