summaryrefslogtreecommitdiffstats
path: root/libdwfl/linux-kernel-modules.c
diff options
context:
space:
mode:
authorRoland McGrath <[email protected]>2005-08-23 08:20:21 +0000
committerRoland McGrath <[email protected]>2005-08-23 08:20:21 +0000
commitd17fac7e89666b47811581b10b5ca0d253a3a82d (patch)
tree31a39f5bbf15cdaa7740ba91302651ad40c3fc1d /libdwfl/linux-kernel-modules.c
parent3712b288d1407cf6551d359cdaacb31bbf2b2a75 (diff)
2005-08-23 Roland McGrath <[email protected]>
* dwarf_attr_integrate.c (dwarf_attr_integrate): Treat DW_AT_specification the same as DW_AT_abstract_origin.
Diffstat (limited to 'libdwfl/linux-kernel-modules.c')
-rw-r--r--libdwfl/linux-kernel-modules.c210
1 files changed, 187 insertions, 23 deletions
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
index 5b75e418..ba65c599 100644
--- a/libdwfl/linux-kernel-modules.c
+++ b/libdwfl/linux-kernel-modules.c
@@ -33,6 +33,28 @@
#define SECADDRFMT "/sys/module/%s/sections/%s"
+/* Try to open the given file as it is or under the debuginfo directory. */
+static int
+try_kernel_name (Dwfl *dwfl, char **fname)
+{
+ if (*fname == NULL)
+ return -1;
+
+ int fd = TEMP_FAILURE_RETRY (open64 (*fname, O_RDONLY));
+ if (fd < 0)
+ {
+ char *debugfname = NULL;
+ Dwfl_Module fakemod = { .dwfl = dwfl };
+ fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
+ *fname, basename (*fname), 0,
+ &debugfname);
+ free (*fname);
+ *fname = debugfname;
+ }
+
+ return fd;
+}
+
static inline const char *
kernel_release (void)
{
@@ -43,47 +65,183 @@ kernel_release (void)
return utsname.release;
}
-/* Find the ELF file for the running kernel and dwfl_report_elf it. */
-int
-dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
+static int
+report_kernel (Dwfl *dwfl, const char *release,
+ int (*predicate) (const char *module, const char *file))
{
if (dwfl == NULL)
return -1;
- const char *release = kernel_release ();
- if (release == NULL)
- return errno;
-
char *fname = NULL;
- asprintf (&fname, "/boot/vmlinux-%s", release);
- if (fname == NULL)
- return -1;
- int fd = open64 (fname, O_RDONLY);
- if (fd < 0)
+ if (release[0] == '/')
+ asprintf (&fname, "%s/vmlinux", release);
+ else
+ asprintf (&fname, "/boot/vmlinux-%s", release);
+ int fd = try_kernel_name (dwfl, &fname);
+ if (fd < 0 && release[0] != '/')
{
free (fname);
fname = NULL;
- asprintf (&fname, "/usr/lib/debug" MODULEDIRFMT "/vmlinux", release);
- if (fname == NULL)
- return -1;
- fd = open64 (fname, O_RDONLY);
+ asprintf (&fname, MODULEDIRFMT "/vmlinux", release);
+ fd = try_kernel_name (dwfl, &fname);
}
int result = 0;
if (fd < 0)
- result = errno;
- else if (INTUSE(dwfl_report_elf) (dwfl, "kernel", fname, fd, 0) == NULL)
+ result = (predicate != NULL && !(*predicate) ("kernel", NULL)) ? 0 : errno;
+ else
{
- close (fd);
- result = -1;
+ bool report = true;
+
+ if (predicate != NULL)
+ {
+ /* Let the predicate decide whether to use this one. */
+ int want = (*predicate) ("kernel", fname);
+ if (want < 0)
+ result = errno;
+ report = want > 0;
+ }
+
+ if (report
+ && INTUSE(dwfl_report_elf) (dwfl, "kernel", fname, fd, 0) == NULL)
+ {
+ close (fd);
+ result = -1;
+ }
}
free (fname);
return result;
}
+
+/* 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))
+{
+ if (release == NULL)
+ {
+ release = kernel_release ();
+ if (release == NULL)
+ return errno;
+ }
+
+ /* First report the kernel. */
+ int result = report_kernel (dwfl, release, predicate);
+ if (result == 0)
+ {
+ /* Do "find /lib/modules/RELEASE/kernel -name *.ko". */
+
+ char *modulesdir[] = { NULL, NULL };
+ if (release[0] == '/')
+ modulesdir[0] = (char *) release;
+ else
+ {
+ asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release);
+ if (modulesdir[0] == NULL)
+ return errno;
+ }
+
+ FTS *fts = fts_open (modulesdir, FTS_LOGICAL | FTS_NOSTAT, 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)
+ {
+ switch (f->fts_info)
+ {
+ case FTS_F:
+ case FTS_NSOK:
+ /* See if this file name matches "*.ko". */
+ if (f->fts_namelen > 3
+ && !memcmp (f->fts_name + f->fts_namelen - 3, ".ko", 4))
+ {
+ /* 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 - 3 + 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 - 3] = '\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;
+
+ default:
+ continue;
+ }
+
+ /* We only get here in error cases. */
+ break;
+ }
+ fts_close (fts);
+ free (modulesdir[0]);
+ }
+
+ return result;
+}
+INTDEF (dwfl_linux_kernel_report_offline)
+
+
+/* Find the ELF file for the running kernel and dwfl_report_elf it. */
+int
+dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
+{
+ const char *release = kernel_release ();
+ if (release == NULL)
+ return errno;
+
+ return report_kernel (dwfl, release, NULL);
+}
INTDEF (dwfl_linux_kernel_report_kernel)
+
/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */
int
@@ -96,9 +254,9 @@ dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
{
const char *release = kernel_release ();
if (release == NULL)
- return -1;
+ return errno;
- /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */
+ /* Do "find /lib/modules/`uname -r`/kernel -name MODULE_NAME.ko". */
char *modulesdir[] = { NULL, NULL };
asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release);
@@ -165,6 +323,7 @@ dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
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)
@@ -187,11 +346,14 @@ dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
}
}
+ 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. */
@@ -200,7 +362,9 @@ 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, Dwarf_Addr *addr)
+ const char *secname, Elf32_Word shndx __attribute__ ((unused)),
+ const GElf_Shdr *shdr __attribute__ ((unused)),
+ Dwarf_Addr *addr)
{
char *sysfile = NULL;
asprintf (&sysfile, SECADDRFMT, modname, secname);