Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175734392
D60633.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D60633.diff
View Options
diff --git a/sbin/Makefile b/sbin/Makefile
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -29,6 +29,7 @@
.WAIT \
init \
kldconfig \
+ kldinfo \
kldload \
kldstat \
kldunload \
diff --git a/sbin/kldinfo/Makefile b/sbin/kldinfo/Makefile
new file mode 100644
--- /dev/null
+++ b/sbin/kldinfo/Makefile
@@ -0,0 +1,36 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Ruslan Zalata <rz@fabmicro.ru>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+PACKAGE=runtime
+PROG= kldinfo
+MAN= kldinfo.8
+
+LIBADD= elf
+
+.include <bsd.prog.mk>
diff --git a/sbin/kldinfo/kldinfo.8 b/sbin/kldinfo/kldinfo.8
new file mode 100644
--- /dev/null
+++ b/sbin/kldinfo/kldinfo.8
@@ -0,0 +1,108 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Ruslan Zalata <rz@fabmicro.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd September 25, 2026
+.Dt KLDINFO 8
+.Os
+.Sh NAME
+.Nm kldinfo
+.Nd display dependency and version information about kernel loadable objects
+held in KLD module (.ko) file
+.Sh SYNOPSIS
+.Nm
+.Op Fl rv
+.\".Ar
+file
+.Sh DESCRIPTION
+The
+.Nm
+utility parses ELF header of KLD
+.Ar file Ns Pa .ko ,
+then searches for available Kernel Loadable objects and shows their dependency
+and version information. If path to the file is given the utility tries to
+open it directly, it quits if open fails. Otherwise, it searches for
+.Va file.ko
+in directories set by
+.Va kern.module_path
+sysctl variable.
+The
+.Pa .ko
+extension name is not mandatory, it will be added while searching for the
+.Va file .
+It does not hurt to specify it though.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl r
+Extract version information of found objects. Otherwise print only dependencies.
+.It Fl v
+Be more verbose when parsing ELF.
+.El
+.Sh NOTES
+The kernel file
+.Va /boot/kernel/kernel
+can also be used as parameter. In this case
+.Nm
+will show all modules builtin into the kernel along with their corresponding
+information. Note, the output can be very excessive.
+.El
+.Sh FILES
+.Bl -tag -width /boot/kernel -compact
+.It Pa /boot/kernel
+default directory containing loadable modules.
+Modules must have an extension of
+.Pa .ko .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+To show info about module by module name:
+.Bd -literal -offset indent
+\*[Gt] kldinfo foo
+.Ed
+.Pp
+To show info about module by file name within the module path:
+.Bd -literal -offset indent
+\*[Gt] kldinfo foo.ko
+.Ed
+.Pp
+To show info about module by relative path:
+.Bd -literal -offset indent
+\*[Gt] kldinfo ./foo.ko
+.Ed
+.Pp
+Or by full path:
+.Bd -literal -offset indent
+\*[Gt] kldinfo /boot/kernel/foo.ko
+.Ed
+.Pp
+.Sh SEE ALSO
+.Xr kldload 8 ,
+.Xr kldstat 8 ,
+.Xr sysctl 8
+.Sh AUTHORS
+.An Ruslan Zalata Aq Mt rz@fabmicro.ru
diff --git a/sbin/kldinfo/kldinfo.c b/sbin/kldinfo/kldinfo.c
new file mode 100644
--- /dev/null
+++ b/sbin/kldinfo/kldinfo.c
@@ -0,0 +1,381 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Ruslan Zalata <rz@fabmicro.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <gelf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define DEPEND_ON "_depend_on_"
+#define VERSION "_version"
+#define METADATA "_metadata"
+#define MODULE_PATH "kern.module_path"
+
+static int verbose = 0;
+static const char *progname = NULL;
+static char mod_file[MAXPATHLEN] = {0};
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s: [-rv] file\n", progname);
+ exit(1);
+}
+
+static void
+fineprint_depend(char *sym_name, char *subs,
+ uint32_t ver1, uint32_t ver2, uint32_t ver3)
+{
+ int mod_name_len = subs - sym_name;
+ char mod_name[mod_name_len + 1];
+ mod_name[0] = '\000';
+ strncat(mod_name, sym_name + 1, mod_name_len - 1);
+
+ char *dep_name = subs + sizeof(DEPEND_ON) - 1;
+
+ if (verbose)
+ printf("\n");
+
+ printf("MODULE: %-28s DEPENDS: %-28s VERSION: %7u %7u %7u\n",
+ mod_name, dep_name, ver1, ver2, ver3);
+}
+
+static void
+fineprint_version(char *sym_name, char *subs, uint32_t ver1)
+{
+ int obj_name_len = subs - sym_name;
+ char obj_name[obj_name_len + 1];
+ obj_name[0] = '\000';
+
+ if (sym_name[0] == '_') {
+ sym_name++;
+ obj_name_len--;
+ }
+
+ strncat(obj_name, sym_name, obj_name_len);
+
+ printf("OBJECT: %-28s VERSION: %-7u\n", obj_name, ver1);
+}
+
+static char *
+find_module_file(char *name)
+{
+ if (index(name, '/') != NULL)
+ return (name); /* name is a file path name, use it! */
+
+ char *module_path;
+ size_t module_path_len = 0;
+
+ /* If getting module_path fails we quit returning NULL */
+
+ if (sysctlbyname(MODULE_PATH, NULL,
+ &module_path_len, NULL, 0) != 0) {
+ fprintf(stderr, "%s: can't get sysctl %s size: %s\n", progname,
+ MODULE_PATH, strerror(errno));
+ return (NULL);
+ }
+
+ if ((module_path = (char *) malloc(module_path_len)) == NULL) {
+ fprintf(stderr, "%s: can't malloc %zu bytes: %s\n", progname,
+ module_path_len, strerror(errno));
+ return (NULL);
+ }
+
+ if (sysctlbyname(MODULE_PATH, module_path,
+ &module_path_len, NULL, 0) != 0) {
+ fprintf(stderr, "%s: can't get sysctl %s value: %s\n", progname,
+ MODULE_PATH, strerror(errno));
+ return (NULL);
+ }
+
+ if (verbose)
+ printf("Searching module name %s in module_path: %s\n",
+ name, module_path);
+
+ char *path;
+ int found = 0;
+ struct stat sb;
+
+ while ((path = strsep(&module_path, ";")) != NULL) {
+
+ if (strstr(name, ".ko") == NULL)
+ snprintf(mod_file, MAXPATHLEN, "%s/%s.ko", path, name);
+ else
+ snprintf(mod_file, MAXPATHLEN, "%s/%s", path, name);
+
+ if (verbose)
+ printf("PATH: %s, mod_file: %s\n", path, mod_file);
+
+ if (stat(mod_file, &sb) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ free(module_path);
+
+ if (found) {
+ if (verbose)
+ printf("FOUND: %s\n", mod_file);
+ return (mod_file);
+ }
+
+ if (verbose)
+ printf("Module %s not found in module_path!\n", name);
+
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ int ch;
+ int print_ver = 0;
+ int deps_count = 0;
+ char *filename = NULL;
+ Elf_Scn *elf_scn = NULL;
+ Elf *elf;
+
+ progname = getprogname();
+
+ while ((ch = getopt(argc, argv, "vr")) != -1) {
+ switch (ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'r':
+ print_ver = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if ((filename = find_module_file(argv[0])) == NULL) {
+ fprintf(stderr, "%s: can't find file for %s\n", progname,
+ argv[0]);
+ return (1);
+ }
+
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ fprintf(stderr, "%s: can't open ELF file %s: %s\n", progname,
+ filename, strerror(errno));
+ return (2);
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ fprintf(stderr, "%s: ELF library too old\n", progname);
+ return (3);
+ }
+
+ if ((elf = elf_begin(fd, ELF_C_READ, (Elf *) 0)) == 0) {
+ fprintf(stderr, "%s: ELF read error: %s\n", progname,
+ elf_errmsg(elf_errno()));
+ return (4);
+ }
+
+ size_t scn_stab = 0;
+
+ if (elf_getshdrstrndx(elf, &scn_stab) < 0) {
+ fprintf(stderr, "%s: ELF has no string table: %s\n", progname,
+ elf_errmsg(elf_errno()));
+ return (5);
+ }
+
+ if (verbose)
+ printf("Section names are in section #%lu\n", scn_stab);
+
+ GElf_Shdr sh;
+
+ while ((elf_scn = elf_nextscn(elf, elf_scn))) {
+
+ if (gelf_getshdr(elf_scn, &sh) == NULL) {
+ fprintf(stderr, "%s: no section header for %p\n",
+ progname, elf_scn);
+ continue;
+ }
+
+ char *name = elf_strptr(elf, scn_stab, sh.sh_name);
+ size_t stab = sh.sh_link;
+
+ if (verbose)
+ printf("Elf Section: name = %s, addr = 0x%lx"
+ ", stab = #%zu\n", name, sh.sh_addr, stab);
+
+ Elf_Data *elf_data = NULL;
+
+ while ((elf_data = elf_getdata(elf_scn, elf_data))) {
+ if (verbose)
+ printf(" Elf Data: off = 0x%lx, size = 0x%lu,"
+ " type = %u\n",
+ elf_data->d_off, elf_data->d_size,
+ elf_data->d_type);
+
+ if (strncmp(name, ".symtab", 7) != 0)
+ continue;
+
+ /* Extract symbols from .symtab section */
+
+ int i = 0;
+ GElf_Sym sym;
+
+ while (gelf_getsym(elf_data, i++, &sym) == &sym) {
+
+ /* Symbol had been read */
+
+ if (GELF_ST_TYPE(sym.st_info) != STT_OBJECT)
+ continue; /* we look for data objects */
+
+ char *sym_name =
+ elf_strptr(elf, stab, sym.st_name);
+
+ if (verbose)
+ printf(" Symbol = %s, info = 0x%02X,"
+ " sect = #%u, size = %lu,"
+ " addr = 0x%lx\n",
+ sym_name, sym.st_info, sym.st_shndx,
+ sym.st_size, sym.st_value);
+
+
+ /* Access section of symbol data */
+
+ Elf_Scn *scn = elf_getscn(elf, sym.st_shndx);
+
+ if (scn == NULL) {
+ if (verbose)
+ printf(" "
+ "Cannot get section #%u\n",
+ sym.st_shndx);
+ continue;
+ }
+
+ if (gelf_getshdr(scn, &sh) == NULL) {
+ if (verbose)
+ printf("\t\t"
+ "Cannot get SH for #%u\n",
+ sym.st_shndx);
+ continue;
+ }
+
+ Elf_Data *data = elf_getdata(scn, NULL);
+
+ if (data == NULL) {
+ if (verbose)
+ printf(" "
+ "Cannot get data for #%u\n",
+ sym.st_shndx);
+ continue;
+ }
+
+ if (data->d_buf == NULL) {
+ if (verbose)
+ printf(" "
+ "No data buf for sec #%u\n",
+ sym.st_shndx);
+ continue;
+ }
+
+ uint32_t ver1;
+ memcpy(&ver1, ((uint8_t *)data->d_buf +
+ 0 + sym.st_value - sh.sh_addr), sizeof(ver1));
+
+ uint32_t ver2;
+ memcpy(&ver2, ((uint8_t *)data->d_buf +
+ 4 + sym.st_value - sh.sh_addr), sizeof(ver2));
+
+ uint32_t ver3;
+ memcpy(&ver3, ((uint8_t *)data->d_buf +
+ 8 + sym.st_value - sh.sh_addr), sizeof(ver3));
+
+ if (verbose)
+ printf(" "
+ "ver1 = 0x%0x (%u), "
+ "ver2 = 0x%0x (%u), "
+ "ver3 = 0x%0x (%u)\n",
+ ver1, ver1, ver2, ver2, ver3, ver3);
+
+ if (strstr(sym_name, "firmware_list") != NULL) {
+ uint8_t *c = (uint8_t *)data->d_buf +
+ sym.st_value - sh.sh_addr;
+ if (verbose) {
+ printf(" Firmware list: ");
+ for (unsigned int j = 0;
+ j < sym.st_size; j++)
+ printf("%02X ", c[j]);
+ printf("\n");
+ }
+ }
+
+ char *subs;
+
+ if ((subs = strstr(sym_name, METADATA)) != NULL)
+ continue; /* hide medatata symbols */
+
+ if ((subs = strstr(sym_name, DEPEND_ON)) != NULL) {
+ fineprint_depend(sym_name, subs,
+ ver1, ver2, ver3);
+ deps_count++;
+ } else
+ if ((subs = strstr(sym_name, VERSION)) != NULL) {
+ if (print_ver)
+ fineprint_version(sym_name,
+ subs, ver1);
+ }
+ }
+ }
+ }
+
+ elf_end(elf);
+
+ if (verbose)
+ printf("\n");
+
+ if (deps_count)
+ fprintf(stderr, "Found %u dependencies in file %s\n",
+ deps_count, filename);
+ else
+ fprintf(stderr, "ELF file %s is not a KLD object!\n",
+ filename);
+
+ return (0);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Oct 13, 9:42 PM (16 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40726785
Default Alt Text
D60633.diff (14 KB)
Attached To
Mode
D60633: Add kldinfo(8), a simple utility to view module dependencies and their version
Attached
Detach File
Event Timeline
Log In to Comment