summaryrefslogtreecommitdiffstats
path: root/src/libdwfl/linux-kernel-modules.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libdwfl/linux-kernel-modules.c')
-rw-r--r--src/libdwfl/linux-kernel-modules.c929
1 files changed, 0 insertions, 929 deletions
diff --git a/src/libdwfl/linux-kernel-modules.c b/src/libdwfl/linux-kernel-modules.c
deleted file mode 100644
index 8f2dab3a..00000000
--- a/src/libdwfl/linux-kernel-modules.c
+++ /dev/null
@@ -1,929 +0,0 @@
-/* Standard libdwfl callbacks for debugging the running Linux kernel.
- Copyright (C) 2005-2011, 2013, 2014 Red Hat, Inc.
- This file is part of elfutils.
-
- This file is free software; you can redistribute it and/or modify
- it under the terms of either
-
- * the GNU Lesser General Public License as published by the Free
- Software Foundation; either version 3 of the License, or (at
- your option) any later version
-
- or
-
- * the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at
- your option) any later version
-
- or both in parallel, as here.
-
- elfutils is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received copies of the GNU General Public License and
- the GNU Lesser General Public License along with this program. If
- not, see <http://www.gnu.org/licenses/>. */
-
-/* We include this before config.h because it can't handle _FILE_OFFSET_BITS.
- Everything we need here is fine if its declarations just come first. */
-
-#undef _FILE_OFFSET_BITS // Undo the damage caused by AndroidConfig.h.
-#include <fts.h>
-
-#include <config.h>
-
-#include "libdwflP.h"
-#include <inttypes.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdio_ext.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/utsname.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-
-#define KERNEL_MODNAME "kernel"
-
-#define MODULEDIRFMT "/lib/modules/%s"
-
-#define KNOTESFILE "/sys/kernel/notes"
-#define MODNOTESFMT "/sys/module/%s/notes"
-#define KSYMSFILE "/proc/kallsyms"
-#define MODULELIST "/proc/modules"
-#define SECADDRDIRFMT "/sys/module/%s/sections/"
-#define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */
-
-
-#if defined (USE_ZLIB) || defined (USE_BZLIB) || defined (USE_LZMA)
-static const char *vmlinux_suffixes[] =
- {
-#ifdef USE_ZLIB
- ".gz",
-#endif
-#ifdef USE_BZLIB
- ".bz2",
-#endif
-#ifdef USE_LZMA
- ".xz",
-#endif
- };
-#endif
-
-/* Try to open the given file as it is or under the debuginfo directory. */
-static int
-try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
-{
- if (*fname == NULL)
- return -1;
-
- /* Don't bother trying *FNAME itself here if the path will cause it to be
- tried because we give its own basename as DEBUGLINK_FILE. */
- int fd = ((((dwfl->callbacks->debuginfo_path
- ? *dwfl->callbacks->debuginfo_path : NULL)
- ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1
- : TEMP_FAILURE_RETRY (open64 (*fname, O_RDONLY)));
-
- if (fd < 0)
- {
- Dwfl_Module fakemod = { .dwfl = dwfl };
- /* First try the file's unadorned basename as DEBUGLINK_FILE,
- to look for "vmlinux" files. */
- fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
- *fname, basename (*fname), 0,
- &fakemod.debug.name);
- if (fd < 0 && try_debug)
- /* Next, let the call use the default of basename + ".debug",
- to look for "vmlinux.debug" files. */
- fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
- *fname, NULL, 0,
- &fakemod.debug.name);
- if (fakemod.debug.name != NULL)
- {
- free (*fname);
- *fname = fakemod.debug.name;
- }
- }
-
-#if defined (USE_ZLIB) || defined (USE_BZLIB) || defined (USE_LZMA)
- if (fd < 0)
- for (size_t i = 0;
- i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0];
- ++i)
- {
- char *zname;
- if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0)
- {
- fd = TEMP_FAILURE_RETRY (open64 (zname, O_RDONLY));
- if (fd < 0)
- free (zname);
- else
- {
- free (*fname);
- *fname = zname;
- }
- }
- }
-#endif
-
- if (fd < 0)
- {
- free (*fname);
- *fname = NULL;
- }
-
- return fd;
-}
-
-static inline const char *
-kernel_release (void)
-{
- /* Cache the `uname -r` string we'll use. */
- static struct utsname utsname;
- if (utsname.release[0] == '\0' && uname (&utsname) != 0)
- return NULL;
- return utsname.release;
-}
-
-static int
-find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
-{
- if ((release[0] == '/'
- ? asprintf (fname, "%s/vmlinux", release)
- : asprintf (fname, "/boot/vmlinux-%s", release)) < 0)
- return -1;
-
- int fd = try_kernel_name (dwfl, fname, true);
- if (fd < 0 && release[0] != '/')
- {
- free (*fname);
- if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0)
- return -1;
- fd = try_kernel_name (dwfl, fname, true);
- }
-
- return fd;
-}
-
-static int
-get_release (Dwfl *dwfl, const char **release)
-{
- if (dwfl == NULL)
- return -1;
-
- const char *release_string = release == NULL ? NULL : *release;
- if (release_string == NULL)
- {
- release_string = kernel_release ();
- if (release_string == NULL)
- return errno;
- if (release != NULL)
- *release = release_string;
- }
-
- return 0;
-}
-
-static int
-report_kernel (Dwfl *dwfl, const char **release,
- int (*predicate) (const char *module, const char *file))
-{
- int result = get_release (dwfl, release);
- if (unlikely (result != 0))
- return result;
-
- char *fname;
- int fd = find_kernel_elf (dwfl, *release, &fname);
-
- if (fd < 0)
- result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL))
- ? 0 : errno ?: ENOENT);
- else
- {
- bool report = true;
-
- if (predicate != NULL)
- {
- /* Let the predicate decide whether to use this one. */
- int want = (*predicate) (KERNEL_MODNAME, fname);
- if (want < 0)
- result = errno;
- report = want > 0;
- }
-
- if (report)
- {
- /* Note that on some architectures (e.g. x86_64) the vmlinux
- is ET_EXEC, while on others (e.g. ppc64) it is ET_DYN.
- In both cases the phdr p_vaddr load address will be non-zero.
- We want the image to be placed as if it was ET_DYN, so
- pass true for add_p_vaddr which will do the right thing
- (in combination with a zero base) in either case. */
- Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
- fname, fd, 0, true);
- if (mod == NULL)
- result = -1;
- else
- /* The kernel is ET_EXEC, but always treat it as relocatable. */
- mod->e_type = ET_DYN;
- }
-
- free (fname);
-
- if (!report || result < 0)
- close (fd);
- }
-
- return result;
-}
-
-/* Look for a kernel debug archive. If we find one, report all its modules.
- If not, return ENOENT. */
-static int
-report_kernel_archive (Dwfl *dwfl, const char **release,
- int (*predicate) (const char *module, const char *file))
-{
- int result = get_release (dwfl, release);
- if (unlikely (result != 0))
- return result;
-
- char *archive;
- int res = (((*release)[0] == '/')
- ? asprintf (&archive, "%s/debug.a", *release)
- : asprintf (&archive, MODULEDIRFMT "/debug.a", *release));
- if (unlikely (res < 0))
- return ENOMEM;
-
- int fd = try_kernel_name (dwfl, &archive, false);
- if (fd < 0)
- result = errno ?: ENOENT;
- else
- {
- /* We have the archive file open! */
- Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd,
- true, predicate);
- if (unlikely (last == NULL))
- result = -1;
- else
- {
- /* Find the kernel and move it to the head of the list. */
- Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
- for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
- if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel"))
- {
- *prevp = m->next;
- m->next = *tailp;
- *tailp = m;
- break;
- }
- }
- }
-
- free (archive);
- return result;
-}
-
-static size_t
-check_suffix (const FTSENT *f, size_t namelen)
-{
-#define TRY(sfx) \
- if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1 \
- : f->fts_namelen >= sizeof sfx) \
- && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1), \
- sfx, sizeof sfx)) \
- return sizeof sfx - 1
-
- TRY (".ko");
-#if USE_ZLIB
- TRY (".ko.gz");
-#endif
-#if USE_BZLIB
- TRY (".ko.bz2");
-#endif
-#if USE_LZMA
- TRY (".ko.xz");
-#endif
-
- return 0;
-
-#undef TRY
-}
-
-/* Report a kernel and all its modules found on disk, for offline use.
- If RELEASE starts with '/', it names a directory to look in;
- if not, it names a directory to find under /lib/modules/;
- if null, /lib/modules/`uname -r` is used.
- Returns zero on success, -1 if dwfl_report_module failed,
- or an errno code if finding the files on disk failed. */
-int
-dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
- int (*predicate) (const char *module,
- const char *file))
-{
- int result = report_kernel_archive (dwfl, &release, predicate);
- if (result != ENOENT)
- return result;
-
- /* First report the kernel. */
- result = report_kernel (dwfl, &release, predicate);
- if (result == 0)
- {
- /* Do "find /lib/modules/RELEASE -name *.ko". */
-
- char *modulesdir[] = { NULL, NULL };
- if (release[0] == '/')
- modulesdir[0] = (char *) release;
- else
- {
- if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
- return errno;
- }
-
- FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
- if (modulesdir[0] == (char *) release)
- modulesdir[0] = NULL;
- if (fts == NULL)
- {
- free (modulesdir[0]);
- return errno;
- }
-
- FTSENT *f;
- while ((f = fts_read (fts)) != NULL)
- {
- /* Skip a "source" subtree, which tends to be large.
- This insane hard-coding of names is what depmod does too. */
- if (f->fts_namelen == sizeof "source" - 1
- && !strcmp (f->fts_name, "source"))
- {
- fts_set (fts, f, FTS_SKIP);
- continue;
- }
-
- switch (f->fts_info)
- {
- case FTS_F:
- case FTS_SL:
- case FTS_NSOK:;
- /* See if this file name matches "*.ko". */
- const size_t suffix = check_suffix (f, 0);
- if (suffix)
- {
- /* We have a .ko file to report. Following the algorithm
- by which the kernel makefiles set KBUILD_MODNAME, we
- replace all ',' or '-' with '_' in the file name and
- call that the module name. Modules could well be
- built using different embedded names than their file
- names. To handle that, we would have to look at the
- __this_module.name contents in the module's text. */
-
- char name[f->fts_namelen - suffix + 1];
- for (size_t i = 0; i < f->fts_namelen - 3U; ++i)
- if (f->fts_name[i] == '-' || f->fts_name[i] == ',')
- name[i] = '_';
- else
- name[i] = f->fts_name[i];
- name[f->fts_namelen - suffix] = '\0';
-
- if (predicate != NULL)
- {
- /* Let the predicate decide whether to use this one. */
- int want = (*predicate) (name, f->fts_path);
- if (want < 0)
- {
- result = -1;
- break;
- }
- if (!want)
- continue;
- }
-
- if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL)
- {
- result = -1;
- break;
- }
- }
- continue;
-
- case FTS_ERR:
- case FTS_DNR:
- case FTS_NS:
- result = f->fts_errno;
- break;
-
- case FTS_SLNONE:
- default:
- continue;
- }
-
- /* We only get here in error cases. */
- break;
- }
- fts_close (fts);
- free (modulesdir[0]);
- }
-
- return result;
-}
-INTDEF (dwfl_linux_kernel_report_offline)
-
-
-/* Grovel around to guess the bounds of the runtime kernel image. */
-static int
-intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
-{
- FILE *f = fopen (KSYMSFILE, "r");
- if (f == NULL)
- return errno;
-
- (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
-
- *notes = 0;
-
- char *line = NULL;
- size_t linesz = 0;
- size_t n;
- char *p = NULL;
- const char *type;
-
- inline bool read_address (Dwarf_Addr *addr)
- {
- if ((n = getline (&line, &linesz, f)) < 1 || line[n - 2] == ']')
- return false;
- *addr = strtoull (line, &p, 16);
- p += strspn (p, " \t");
- type = strsep (&p, " \t\n");
- if (type == NULL)
- return false;
- return p != NULL && p != line;
- }
-
- int result;
- do
- result = read_address (start) ? 0 : -1;
- while (result == 0 && strchr ("TtRr", *type) == NULL);
-
- if (result == 0)
- {
- *end = *start;
- while (read_address (end))
- if (*notes == 0 && !strcmp (p, "__start_notes\n"))
- *notes = *end;
-
- Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE);
- *start &= -(Dwarf_Addr) round_kernel;
- *end += round_kernel - 1;
- *end &= -(Dwarf_Addr) round_kernel;
- if (*start >= *end || *end - *start < round_kernel)
- result = -1;
- }
- free (line);
-
- if (result == -1)
- result = ferror_unlocked (f) ? errno : ENOEXEC;
-
- fclose (f);
-
- return result;
-}
-
-
-/* Look for a build ID note in NOTESFILE and associate the ID with MOD. */
-static int
-check_notes (Dwfl_Module *mod, const char *notesfile,
- Dwarf_Addr vaddr, const char *secname)
-{
- int fd = open64 (notesfile, O_RDONLY);
- if (fd < 0)
- return 1;
-
- assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
- assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
- union
- {
- GElf_Nhdr nhdr;
- unsigned char data[8192];
- } buf;
-
- ssize_t n = read (fd, buf.data, sizeof buf);
- close (fd);
-
- if (n <= 0)
- return 1;
-
- unsigned char *p = buf.data;
- while (p < &buf.data[n])
- {
- /* No translation required since we are reading the native kernel. */
- GElf_Nhdr *nhdr = (void *) p;
- p += sizeof *nhdr;
- unsigned char *name = p;
- p += (nhdr->n_namesz + 3) & -4U;
- unsigned char *bits = p;
- p += (nhdr->n_descsz + 3) & -4U;
-
- if (p <= &buf.data[n]
- && nhdr->n_type == NT_GNU_BUILD_ID
- && nhdr->n_namesz == sizeof "GNU"
- && !memcmp (name, "GNU", sizeof "GNU"))
- {
- /* Found it. For a module we must figure out its VADDR now. */
-
- if (secname != NULL
- && (INTUSE(dwfl_linux_kernel_module_section_address)
- (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
- || vaddr == (GElf_Addr) -1l))
- vaddr = 0;
-
- if (vaddr != 0)
- vaddr += bits - buf.data;
- return INTUSE(dwfl_module_report_build_id) (mod, bits,
- nhdr->n_descsz, vaddr);
- }
- }
-
- return 0;
-}
-
-/* Look for a build ID for the kernel. */
-static int
-check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
-{
- return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
-}
-
-/* Look for a build ID for a loaded kernel module. */
-static int
-check_module_notes (Dwfl_Module *mod)
-{
- char *dirs[2] = { NULL, NULL };
- if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
- return ENOMEM;
-
- FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL);
- if (fts == NULL)
- {
- free (dirs[0]);
- return 0;
- }
-
- int result = 0;
- FTSENT *f;
- while ((f = fts_read (fts)) != NULL)
- {
- switch (f->fts_info)
- {
- case FTS_F:
- case FTS_SL:
- case FTS_NSOK:
- result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
- if (result > 0) /* Nothing found. */
- {
- result = 0;
- continue;
- }
- break;
-
- case FTS_ERR:
- case FTS_DNR:
- result = f->fts_errno;
- break;
-
- case FTS_NS:
- case FTS_SLNONE:
- default:
- continue;
- }
-
- /* We only get here when finished or in error cases. */
- break;
- }
- fts_close (fts);
- free (dirs[0]);
-
- return result;
-}
-
-int
-dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
-{
- Dwarf_Addr start;
- Dwarf_Addr end;
- inline Dwfl_Module *report (void)
- {
- return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end);
- }
-
- /* This is a bit of a kludge. If we already reported the kernel,
- don't bother figuring it out again--it never changes. */
- for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
- if (!strcmp (m->name, KERNEL_MODNAME))
- {
- start = m->low_addr;
- end = m->high_addr;
- return report () == NULL ? -1 : 0;
- }
-
- /* Try to figure out the bounds of the kernel image without
- looking for any vmlinux file. */
- Dwarf_Addr notes;
- /* The compiler cannot deduce that if intuit_kernel_bounds returns
- zero NOTES will be initialized. Fake the initialization. */
- asm ("" : "=m" (notes));
- int result = intuit_kernel_bounds (&start, &end, &notes);
- if (result == 0)
- {
- Dwfl_Module *mod = report ();
- return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
- }
- if (result != ENOENT)
- return result;
-
- /* Find the ELF file for the running kernel and dwfl_report_elf it. */
- return report_kernel (dwfl, NULL, NULL);
-}
-INTDEF (dwfl_linux_kernel_report_kernel)
-
-
-/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */
-
-int
-dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
- void **userdata __attribute__ ((unused)),
- const char *module_name,
- Dwarf_Addr base __attribute__ ((unused)),
- char **file_name, Elf **elfp)
-{
- if (mod->build_id_len > 0)
- {
- int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
- file_name, elfp);
- if (fd >= 0 || mod->main.elf != NULL || errno != 0)
- return fd;
- }
-
- const char *release = kernel_release ();
- if (release == NULL)
- return errno;
-
- if (!strcmp (module_name, KERNEL_MODNAME))
- return find_kernel_elf (mod->dwfl, release, file_name);
-
- /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */
-
- char *modulesdir[] = { NULL, NULL };
- if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
- return -1;
-
- FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
- if (fts == NULL)
- {
- free (modulesdir[0]);
- return -1;
- }
-
- size_t namelen = strlen (module_name);
-
- /* This is a kludge. There is no actual necessary relationship between
- the name of the .ko file installed and the module name the kernel
- knows it by when it's loaded. The kernel's only idea of the module
- name comes from the name embedded in the object's magic
- .gnu.linkonce.this_module section.
-
- In practice, these module names match the .ko file names except for
- some using '_' and some using '-'. So our cheap kludge is to look for
- two files when either a '_' or '-' appears in a module name, one using
- only '_' and one only using '-'. */
-
- char alternate_name[namelen + 1];
- inline bool subst_name (char from, char to)
- {
- const char *n = memchr (module_name, from, namelen);
- if (n == NULL)
- return false;
- char *a = mempcpy (alternate_name, module_name, n - module_name);
- *a++ = to;
- ++n;
- const char *p;
- while ((p = memchr (n, from, namelen - (n - module_name))) != NULL)
- {
- a = mempcpy (a, n, p - n);
- *a++ = to;
- n = p + 1;
- }
- memcpy (a, n, namelen - (n - module_name) + 1);
- return true;
- }
- if (!subst_name ('-', '_') && !subst_name ('_', '-'))
- alternate_name[0] = '\0';
-
- FTSENT *f;
- int error = ENOENT;
- while ((f = fts_read (fts)) != NULL)
- {
- /* Skip a "source" subtree, which tends to be large.
- This insane hard-coding of names is what depmod does too. */
- if (f->fts_namelen == sizeof "source" - 1
- && !strcmp (f->fts_name, "source"))
- {
- fts_set (fts, f, FTS_SKIP);
- continue;
- }
-
- error = ENOENT;
- switch (f->fts_info)
- {
- case FTS_F:
- case FTS_SL:
- case FTS_NSOK:
- /* See if this file name is "MODULE_NAME.ko". */
- if (check_suffix (f, namelen)
- && (!memcmp (f->fts_name, module_name, namelen)
- || !memcmp (f->fts_name, alternate_name, namelen)))
- {
- int fd = open64 (f->fts_accpath, O_RDONLY);
- *file_name = strdup (f->fts_path);
- fts_close (fts);
- free (modulesdir[0]);
- if (fd < 0)
- free (*file_name);
- else if (*file_name == NULL)
- {
- close (fd);
- fd = -1;
- }
- return fd;
- }
- break;
-
- case FTS_ERR:
- case FTS_DNR:
- case FTS_NS:
- error = f->fts_errno;
- break;
-
- case FTS_SLNONE:
- default:
- break;
- }
- }
-
- fts_close (fts);
- free (modulesdir[0]);
- errno = error;
- return -1;
-}
-INTDEF (dwfl_linux_kernel_find_elf)
-
-
-/* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
- We read the information from /sys/module directly. */
-
-int
-dwfl_linux_kernel_module_section_address
-(Dwfl_Module *mod __attribute__ ((unused)),
- void **userdata __attribute__ ((unused)),
- const char *modname, Dwarf_Addr base __attribute__ ((unused)),
- const char *secname, Elf32_Word shndx __attribute__ ((unused)),
- const GElf_Shdr *shdr __attribute__ ((unused)),
- Dwarf_Addr *addr)
-{
- char *sysfile;
- if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
- return DWARF_CB_ABORT;
-
- FILE *f = fopen (sysfile, "r");
- free (sysfile);
-
- if (f == NULL)
- {
- if (errno == ENOENT)
- {
- /* The .modinfo and .data.percpu sections are never kept
- loaded in the kernel. If the kernel was compiled without
- CONFIG_MODULE_UNLOAD, the .exit.* sections are not
- actually loaded at all.
-
- Setting *ADDR to -1 tells the caller this section is
- actually absent from memory. */
-
- if (!strcmp (secname, ".modinfo")
- || !strcmp (secname, ".data.percpu")
- || !strncmp (secname, ".exit", 5))
- {
- *addr = (Dwarf_Addr) -1l;
- return DWARF_CB_OK;
- }
-
- /* The goofy PPC64 module_frob_arch_sections function tweaks
- the section names as a way to control other kernel code's
- behavior, and this cruft leaks out into the /sys information.
- The file name for ".init*" may actually look like "_init*". */
-
- const bool is_init = !strncmp (secname, ".init", 5);
- if (is_init)
- {
- if (asprintf (&sysfile, SECADDRDIRFMT "_%s",
- modname, &secname[1]) < 0)
- return ENOMEM;
- f = fopen (sysfile, "r");
- free (sysfile);
- if (f != NULL)
- goto ok;
- }
-
- /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1.
- In case that size increases in the future, look for longer
- truncated names first. */
- size_t namelen = strlen (secname);
- if (namelen >= MODULE_SECT_NAME_LEN)
- {
- int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
- modname, secname);
- if (len < 0)
- return DWARF_CB_ABORT;
- char *end = sysfile + len;
- do
- {
- *--end = '\0';
- f = fopen (sysfile, "r");
- if (is_init && f == NULL && errno == ENOENT)
- {
- sysfile[len - namelen] = '_';
- f = fopen (sysfile, "r");
- sysfile[len - namelen] = '.';
- }
- }
- while (f == NULL && errno == ENOENT
- && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN);
- free (sysfile);
-
- if (f != NULL)
- goto ok;
- }
- }
-
- return DWARF_CB_ABORT;
- }
-
- ok:
- (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
-
- int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0
- : ferror_unlocked (f) ? errno : ENOEXEC);
- fclose (f);
-
- if (result == 0)
- return DWARF_CB_OK;
-
- errno = result;
- return DWARF_CB_ABORT;
-}
-INTDEF (dwfl_linux_kernel_module_section_address)
-
-int
-dwfl_linux_kernel_report_modules (Dwfl *dwfl)
-{
- FILE *f = fopen (MODULELIST, "r");
- if (f == NULL)
- return errno;
-
- (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
-
- int result = 0;
- Dwarf_Addr modaddr;
- unsigned long int modsz;
- char modname[128];
- char *line = NULL;
- size_t linesz = 0;
- /* We can't just use fscanf here because it's not easy to distinguish \n
- from other whitespace so as to take the optional word following the
- address but always stop at the end of the line. */
- while (getline (&line, &linesz, f) > 0
- && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
- modname, &modsz, &modaddr) == 3)
- {
- Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
- modaddr, modaddr + modsz);
- if (mod == NULL)
- {
- result = -1;
- break;
- }
-
- result = check_module_notes (mod);
- }
- free (line);
-
- if (result == 0)
- result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
-
- fclose (f);
-
- return result;
-}
-INTDEF (dwfl_linux_kernel_report_modules)