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