Page MenuHomeFreeBSD

D58048.id181376.diff
No OneTemporary

D58048.id181376.diff

diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile
--- a/rescue/rescue/Makefile
+++ b/rescue/rescue/Makefile
@@ -57,7 +57,8 @@
ed expr getfacl hostname kenv kill ln ls mkdir mv \
pkill ps pwd realpath rm rmdir setfacl sh sleep stty \
sync test
-CRUNCH_LIBS+= -lcrypt -ledit -ljail -lkvm -lelf -ltermcapw -lutil -lxo
+CRUNCH_LIBS+= -lcrypt -ledit -ljail -lkvm -lprivatekldelf -lelf -ltermcapw \
+ -lutil -lxo
CRUNCH_BUILDTOOLS+= bin/sh
# Additional options for specific programs
diff --git a/sbin/kldstat/Makefile b/sbin/kldstat/Makefile
--- a/sbin/kldstat/Makefile
+++ b/sbin/kldstat/Makefile
@@ -29,6 +29,8 @@
PROG= kldstat
MAN= kldstat.8
-LIBADD= util
+CFLAGS+=-I${SRCTOP}/lib/libkldelf
+
+LIBADD= util elf kldelf
.include <bsd.prog.mk>
diff --git a/sbin/kldstat/kldstat.8 b/sbin/kldstat/kldstat.8
--- a/sbin/kldstat/kldstat.8
+++ b/sbin/kldstat/kldstat.8
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd January 4, 2025
+.Dd July 6, 2026
.Dt KLDSTAT 8
.Os
.Sh NAME
@@ -66,6 +66,15 @@
Quietly check if file is loaded or compiled into the kernel.
.It Fl v
Be more verbose.
+For each file, also list the modules it contains and the module
+dependencies recorded in the file on disk.
+Each dependency is shown with its preferred, minimum, and maximum
+version numbers, followed by the loaded file which currently provides
+the module, or
+.Ql not found
+if no loaded module matches.
+For a module registered by a driver, the name of the parent bus is
+shown in parentheses.
.El
.Sh EXIT STATUS
.Ex -std
@@ -101,6 +110,9 @@
Contains modules:
Id Name
2 linuxelf
+ Dependencies:
+ depends on kernel.1500026 (1500026,1599999) => /boot/kernel/kernel
+ depends on linux_common.1 (1,1) => /boot/kernel/linux_common.ko
.Ed
.Pp
Same as above using the
@@ -113,6 +125,9 @@
Contains modules:
Id Name
2 linuxelf
+ Dependencies:
+ depends on kernel.1500026 (1500026,1599999) => /boot/kernel/kernel
+ depends on linux_common.1 (1,1) => /boot/kernel/linux_common.ko
.Ed
.Pp
Show the status of the
diff --git a/sbin/kldstat/kldstat.c b/sbin/kldstat/kldstat.c
--- a/sbin/kldstat/kldstat.c
+++ b/sbin/kldstat/kldstat.c
@@ -28,11 +28,16 @@
#include <sys/types.h>
#include <sys/param.h>
-#include <sys/module.h>
#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
#include <err.h>
+#include <errno.h>
+#include <gelf.h>
+#include <kldelf.h>
#include <libutil.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -46,6 +51,131 @@
static int showdata = 0;
+struct module_map_entry {
+ char *module_name;
+ char *ko_path; /* shared by all modules from one file */
+ char *prefix;
+ SLIST_ENTRY(module_map_entry) entry;
+};
+static SLIST_HEAD(module_map_list, module_map_entry) module_map =
+ SLIST_HEAD_INITIALIZER(module_map);
+
+static void
+clean_modules_mapping(void)
+{
+ struct module_map_entry *entry, *next;
+
+ SLIST_FOREACH_SAFE(entry, &module_map, entry, next) {
+ /*
+ * Entries from the same file are inserted consecutively
+ * and share one ko_path allocation; free it once, on the
+ * last entry of each run.
+ */
+ if (next == NULL || next->ko_path != entry->ko_path)
+ free(entry->ko_path);
+ free(entry->prefix);
+ free(entry->module_name);
+ free(entry);
+ }
+ SLIST_INIT(&module_map);
+}
+
+static struct module_map_entry *
+new_module_map_entry(char *ko_path)
+{
+ struct module_map_entry *entp;
+
+ entp = calloc(1, sizeof(*entp));
+ if (entp == NULL)
+ err(1, "calloc");
+ entp->ko_path = ko_path;
+ return (entp);
+}
+
+static void
+init_modules_mapping(void)
+{
+ struct module_map_entry *entp;
+ struct kld_file_stat stat;
+ struct module_stat mod_stat;
+ const char *child_name;
+ char *ko_path;
+ int fileid, modid, nmod;
+
+ if (!SLIST_EMPTY(&module_map))
+ return;
+ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
+ stat.version = sizeof(struct kld_file_stat);
+ if (kldstat(fileid, &stat) < 0)
+ continue;
+ /* All modules from this file share one copy of its path. */
+ ko_path = strdup(stat.pathname);
+ if (ko_path == NULL)
+ err(1, "strdup");
+ nmod = 0;
+ if (strcmp(stat.name, "kernel") == 0) {
+ entp = new_module_map_entry(ko_path);
+ entp->module_name = strdup("kernel");
+ if (entp->module_name == NULL)
+ err(1, "strdup");
+ SLIST_INSERT_HEAD(&module_map, entp, entry);
+ nmod++;
+ }
+ for (modid = kldfirstmod(fileid); modid > 0;
+ modid = modfnext(modid)) {
+ mod_stat.version = sizeof(struct module_stat);
+ if (modstat(modid, &mod_stat) < 0)
+ continue;
+ entp = new_module_map_entry(ko_path);
+ if ((child_name = strchr(mod_stat.name, '/')) != NULL) {
+ entp->prefix = strndup(mod_stat.name,
+ child_name - mod_stat.name);
+ entp->module_name = strdup(child_name + 1);
+ if (entp->prefix == NULL)
+ err(1, "strndup");
+ } else
+ entp->module_name = strdup(mod_stat.name);
+ if (entp->module_name == NULL)
+ err(1, "strdup");
+ SLIST_INSERT_HEAD(&module_map, entp, entry);
+ nmod++;
+ }
+ /* No entry references the path if the file had no modules. */
+ if (nmod == 0)
+ free(ko_path);
+ }
+ if (!SLIST_EMPTY(&module_map))
+ atexit(clean_modules_mapping);
+}
+
+static void
+print_module_dependency(const char *modname, const struct Gmod_depend *mdp)
+{
+ struct module_map_entry *entry;
+ bool found = false;
+
+ /*
+ * A module name is not unique: the same driver can attach to
+ * more than one bus (e.g. hidbus on usb and iic), so report
+ * every file that provides the dependency.
+ */
+ SLIST_FOREACH(entry, &module_map, entry) {
+ if (strcmp(entry->module_name, modname) != 0)
+ continue;
+ found = true;
+ printf("\t\t depends on %s.%d (%d,%d) => %s", modname,
+ mdp->md_ver_preferred, mdp->md_ver_minimum,
+ mdp->md_ver_maximum, entry->ko_path);
+ if (entry->prefix != NULL)
+ printf(" (%s)", entry->prefix);
+ printf("\n");
+ }
+ if (!found)
+ printf("\t\t depends on %s.%d (%d,%d) => not found\n", modname,
+ mdp->md_ver_preferred, mdp->md_ver_minimum,
+ mdp->md_ver_maximum);
+}
+
static void
printmod(int modid)
{
@@ -65,6 +195,58 @@
printf("\t\t%3d %s\n", stat.id, stat.name);
}
+static void
+print_file_dependency(const char *full_path)
+{
+ struct elf_file ef;
+ struct Gmod_metadata md;
+ struct Gmod_depend mdp;
+ GElf_Addr *entries;
+ char cval[MAXMODNAME + 1];
+ long i, n;
+ int error;
+
+ init_modules_mapping();
+
+ if (elf_open_file(&ef, full_path, 1) != 0)
+ return;
+ entries = NULL;
+ error = elf_read_linker_set(&ef, MDT_SETNAME, &entries, &n);
+ if (error != 0) {
+ /* A file without module metadata has no dependencies. */
+ if (error != ENOENT && error != ESRCH)
+ warnc(error, "%s: can't read module metadata",
+ full_path);
+ goto out;
+ }
+ for (i = 0; i < n; i++) {
+ error = elf_read_mod_metadata(&ef, entries[i], &md);
+ if (error != 0) {
+ warnc(error, "%s: can't read module metadata",
+ full_path);
+ goto out;
+ }
+ if (md.md_type != MDT_DEPEND)
+ continue;
+ error = elf_read_string(&ef, md.md_cval, cval, sizeof(cval));
+ if (error != 0) {
+ warnc(error, "%s: can't read module dependency name",
+ full_path);
+ goto out;
+ }
+ error = elf_read_mod_depend(&ef, md.md_data, &mdp);
+ if (error != 0) {
+ warnc(error, "%s: can't read module dependency",
+ full_path);
+ goto out;
+ }
+ print_module_dependency(cval, &mdp);
+ }
+out:
+ free(entries);
+ elf_close_file(&ef);
+}
+
static void
printfile(int fileid, int verbose, int humanized)
{
@@ -94,6 +276,8 @@
printf("\t\t Id Name\n");
for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid))
printmod(modid);
+ printf("\tDependencies:\n");
+ print_file_dependency(stat.pathname);
} else
printf("\n");
}
@@ -154,6 +338,9 @@
if (argc != 0)
usage();
+ if (verbose && elf_version(EV_CURRENT) == EV_NONE)
+ errx(1, "unsupported libelf");
+
if (modname != NULL) {
if ((modid = modfind(modname)) < 0) {
if (!quiet)

File Metadata

Mime Type
text/plain
Expires
Mon, Aug 3, 4:38 AM (13 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35893893
Default Alt Text
D58048.id181376.diff (8 KB)

Event Timeline