linux-kernel-modules.c revision 07d4f2fc1cb53f170a71bc13617bbdd9cb1c3c60
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 if (f == NULL) 377 { 378 if (errno == ENOENT) 379 { 380 /* The .modinfo and .data.percpu sections are never kept 381 loaded in the kernel. If the kernel was compiled without 382 CONFIG_MODULE_UNLOAD, the .exit.* sections are not 383 actually loaded at all. 384 385 Just relocate these bogusly to zero. This part of the 386 debug information will not be of any use. */ 387 388 if (!strcmp (secname, ".modinfo") 389 || !strcmp (secname, ".data.percpu") 390 || !strncmp (secname, ".exit", 5)) 391 { 392 *addr = 0; 393 return DWARF_CB_OK; 394 } 395 } 396 397 return DWARF_CB_ABORT; 398 } 399 400 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 401 402 int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0 403 : ferror_unlocked (f) ? errno : ENOEXEC); 404 fclose (f); 405 406 if (result == 0) 407 return DWARF_CB_OK; 408 409 errno = result; 410 return DWARF_CB_ABORT; 411} 412INTDEF (dwfl_linux_kernel_module_section_address) 413 414int 415dwfl_linux_kernel_report_modules (Dwfl *dwfl) 416{ 417 FILE *f = fopen (MODULELIST, "r"); 418 if (f == NULL) 419 return errno; 420 421 (void) __fsetlocking (f, FSETLOCKING_BYCALLER); 422 423 int result = 0; 424 Dwarf_Addr modaddr; 425 unsigned long int modsz; 426 char modname[128]; 427 while (fscanf (f, "%128s %lu %*s %*s %*s %" PRIx64 "\n", 428 modname, &modsz, &modaddr) == 3) 429 if (INTUSE(dwfl_report_module) (dwfl, modname, 430 modaddr, modaddr + modsz) == NULL) 431 { 432 result = -1; 433 break; 434 } 435 436 if (result == 0) 437 result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC; 438 439 fclose (f); 440 441 return result; 442} 443INTDEF (dwfl_linux_kernel_report_modules) 444