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