Page MenuHomeFreeBSD

D60633.id189543.diff
No OneTemporary

D60633.id189543.diff

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

Mime Type
text/plain
Expires
Tue, Oct 13, 11:40 PM (18 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40726785
Default Alt Text
D60633.id189543.diff (14 KB)

Event Timeline