Hi,
On 9/25/26 3:13 AM, Ruslan Zalata wrote:
Hello All!
Playing with FreeBSD kernels, hacking driver code and debugging issues
on laptops sometimes require knowledge of what KObjs are stored in
kernel module file, their dependencies and, what's more important, for
what version of kernel a particular module is built! Remember recent
vboxdrv.ko version mismatch issue ?
Getting FreeBSD kernel module version info is a bit hacky. One of the
ways involves using "nm" or "readelf" to get a list of special
symbols, namely "_depend_on_" and "_version", then using "objdump" to
get to .data section, then using "hexdump" to extract and fine-print
version info. One also needs to write a script to glue these tools
together, otherwise it's too easy make a mistake and get wrong data.
After some time of struggling with this issue, I wrote mine in C,
called "kldinfo". :-)
This as simple as a nail "kldinfo" utility behaves like "kldload", but
instead of loading module it dumps list of objects it finds in module
file along with their corresponding version and dependency info.
I propose to include "kldinfo" in FreeBSD base along with the other
"kld*" utils. Note, a similar utility called "modinfo" has been
present in most Linux distros for decades. I think we should have it
in FreeBSD too. ;-) If that is ok, I will upload source code for
review to Phabricator.
Repo: https://github.com/pointcheck/kldinfo.git
PS:
The code is fully written by hand, no LLMs have been involved.
This is very nice tool - thank You for writing it :)
I have played with it a little and introduced different formatting -
proposal for new look below - along with the patch.
Regards,
vermaden
#./kldinfo /boot/kernel/kernel | head -30
MODULE: xpt DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: xpt DEPENDS: cam VERSION:
1 1 1
MODULE: cam DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: aprobe DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: aprobe DEPENDS: cam VERSION:
1 1 1
MODULE: pmp DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: pmp DEPENDS: cam VERSION:
1 1 1
MODULE: nda DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: nda DEPENDS: cam VERSION:
1 1 1
MODULE: nvme_probe DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: nvme_probe DEPENDS: cam VERSION:
1 1 1
MODULE: probe DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: probe DEPENDS: cam VERSION:
1 1 1
MODULE: cd DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: cd DEPENDS: cam VERSION:
1 1 1
MODULE: ch DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: ch DEPENDS: cam VERSION:
1 1 1
MODULE: ada DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: ada DEPENDS: cam VERSION:
1 1 1
MODULE: da DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: da DEPENDS: cam VERSION:
1 1 1
MODULE: pass DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: pass DEPENDS: cam VERSION:
1 1 1
MODULE: sa DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: sa DEPENDS: cam VERSION:
1 1 1
MODULE: enc DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: enc DEPENDS: cam VERSION:
1 1 1
MODULE: xz DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: aacp_aac DEPENDS: kernel VERSION:
1404000 1404000 1499999
MODULE: aacp DEPENDS: cam VERSION:
1 1 1
# ./kldinfo -v /boot/kernel/acpi_ibm.ko
Section names are in section #19
Elf Section: name = .text, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x12288, type = 1
Elf Section: name = .rodata, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x4096, type = 1
Elf Section: name = .rela.text, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x9120, type = 13
Elf Section: name = .rela.rodata, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x2496, type = 13
Elf Section: name = .data, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x440, type = 1
Elf Section: name = .rela.data, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x672, type = 13
Elf Section: name = set_sysinit_set, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x16, type = 1
Elf Section: name = .relaset_sysinit_set, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x48, type = 13
Elf Section: name = set_sysuninit_set, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x8, type = 1
Elf Section: name = .relaset_sysuninit_set, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x24, type = 13
Elf Section: name = set_modmetadata_set, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x32, type = 1
Elf Section: name = .relaset_modmetadata_set, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x96, type = 13
Elf Section: name = .bss, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x32, type = 1
Elf Section: name = .comment, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x104, type = 1
Elf Section: name = .llvm_addrsig, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x57, type = 1
Elf Section: name = .note.gnu.build-id, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x36, type = 9
Elf Section: name = .note.GNU-stack, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x0, type = 1
Elf Section: name = .gnu_debuglink, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x24, type = 1
Elf Section: name = .shstrtab, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x294, type = 1
Elf Section: name = .SUNW_ctf, addr = 0x0, stab = #21
Elf Data: off = 0x0, size = 0x3883, type = 1
Elf Section: name = .symtab, addr = 0x0, stab = #22
Elf Data: off = 0x0, size = 0x3288, type = 18
Symbol = ibm_ids, info = 0x01, sect = #5, size = 32, addr = 0x180
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = acpi_ibm_sysctls, info = 0x01, sect = #2, size = 480, addr =
0x270
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x1 (1)
Symbol = ibm_sxlock, info = 0x01, sect = #13, size = 32, addr = 0x0
No data buf for sec #13
Symbol = ibm_sxlock_sx_sysinit_sys_init, info = 0x01, sect = #5, size
= 32, addr = 0x18
ver1 = 0x1b00000 (28311552), ver2 = 0x1000000 (16777216), ver3 =
0x0 (0)
Symbol = ibm_sxlock_args, info = 0x01, sect = #5, size = 24, addr = 0xd8
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = __set_sysinit_set_sym_ibm_sxlock_sx_sysinit_sys_init, info =
0x01, sect = #7, size = 8, addr = 0x0
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = ibm_sxlock_sx_sysuninit_sys_uninit, info = 0x01, sect = #5,
size = 32, addr = 0x38
ver1 = 0x1b00000 (28311552), ver2 = 0x1000000 (16777216), ver3 =
0x0 (0)
Symbol = __set_sysuninit_set_sym_ibm_sxlock_sx_sysuninit_sys_uninit,
info = 0x01, sect = #9, size = 8, addr = 0x0
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = _mod_metadata_md_acpi_ibm_acpi_on_kernel, info = 0x01, sect
= #5, size = 24, addr = 0x58
ver1 = 0x1 (1), ver2 = 0x1 (1), ver3 = 0x0 (0)
Symbol = _acpi_ibm_acpi_depend_on_kernel, info = 0x01, sect = #5,
size = 12, addr = 0xf0
ver1 = 0x156c60 (1404000), ver2 = 0x156c60 (1404000), ver3 =
0x156c60 (1404000)
MODULE: acpi_ibm_acpi DEPENDS: kernel VERSION:
1404000 1404000 1404000
Symbol =
__set_modmetadata_set_sym__mod_metadata_md_acpi_ibm_acpi_on_kernel, info
= 0x01, sect = #11, size = 8, addr = 0x0
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = _mod_metadata_md_acpi_ibm_acpi, info = 0x01, sect = #5, size
= 24, addr = 0x70
ver1 = 0x1 (1), ver2 = 0x2 (2), ver3 = 0x0 (0)
Symbol = acpi_ibm_acpi_mod, info = 0x01, sect = #5, size = 24, addr =
0x100
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = __set_modmetadata_set_sym__mod_metadata_md_acpi_ibm_acpi,
info = 0x01, sect = #11, size = 8, addr = 0x8
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = acpi_ibm_acpimodule_sys_init, info = 0x01, sect = #5, size =
32, addr = 0x88
ver1 = 0x3100000 (51380224), ver2 = 0x1000000 (16777216), ver3 =
0x0 (0)
Symbol = __set_sysinit_set_sym_acpi_ibm_acpimodule_sys_init, info =
0x01, sect = #7, size = 8, addr = 0x8
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = _mod_metadata_md_acpi_ibm_on_acpi, info = 0x01, sect = #5,
size = 24, addr = 0xa8
ver1 = 0x1 (1), ver2 = 0x1 (1), ver3 = 0x0 (0)
Symbol = _acpi_ibm_depend_on_acpi, info = 0x01, sect = #5, size = 12,
addr = 0x1a0
ver1 = 0x1 (1), ver2 = 0x1 (1), ver3 = 0x1 (1)
MODULE: acpi_ibm DEPENDS: acpi VERSION:
1 1 1
Symbol = __set_modmetadata_set_sym__mod_metadata_md_acpi_ibm_on_acpi,
info = 0x01, sect = #11, size = 8, addr = 0x10
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = _mod_metadata_md_acpi_ibm_on_evdev, info = 0x01, sect = #5,
size = 24, addr = 0xc0
ver1 = 0x1 (1), ver2 = 0x1 (1), ver3 = 0x0 (0)
Symbol = _acpi_ibm_depend_on_evdev, info = 0x01, sect = #5, size =
12, addr = 0x1ac
ver1 = 0x1 (1), ver2 = 0x1 (1), ver3 = 0x1 (1)
MODULE: acpi_ibm DEPENDS: evdev VERSION:
1 1 1
Symbol =
__set_modmetadata_set_sym__mod_metadata_md_acpi_ibm_on_evdev, info =
0x01, sect = #11, size = 8, addr = 0x18
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x69706361 (1768973153)
Symbol = acpi_ibm_acpi_driver_mod, info = 0x01, sect = #5, size = 48,
addr = 0x118
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = acpi_ibm_driver, info = 0x01, sect = #5, size = 48, addr = 0x148
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = acpi_ibm_methods, info = 0x01, sect = #2, size = 80, addr =
0x220
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Symbol = acpi_ibm_models, info = 0x01, sect = #5, size = 24, addr = 0x0
ver1 = 0x0 (0), ver2 = 0x0 (0), ver3 = 0x0 (0)
Elf Section: name = .strtab, addr = 0x0, stab = #0
Elf Data: off = 0x0, size = 0x2485, type = 1
Found 3 dependencies in file /boot/kernel/acpi_ibm.ko
# diff -u kldinfo.c.OLD.geany kldinfo.c
--- kldinfo.c.OLD.geany 2026-09-25 13:55:55.352795000 +0200
+++ kldinfo.c 2026-09-25 13:54:02.349799000 +0200
@@ -67,7 +67,10 @@
int dep_name_len = strlen(sym_name - mod_name_len -
sizeof(DEPEND_ON));
- printf("MODULE: %s\tDEPENDS: %s\tVERSION: %u %u %u\n",
+ if (verbose)
+ printf("\n");
+
+ printf("MODULE: %-28s DEPENDS: %-28s VERSION: %7u %7u %7u\n",
mod_name, dep_name, ver1, ver2, ver3);
}
@@ -86,7 +89,7 @@
strncat(obj_name, sym_name, obj_name_len);
- printf("OBJECT: %s\tVERSION: %u\n", obj_name, ver1);
+ printf("OBJECT: %-10s VERSION: %-10u\n", obj_name, ver1);
}
char *
@@ -246,7 +249,7 @@
while ((elf_data = elf_getdata(elf_scn, elf_data))) {
if (verbose)
- printf("\tElf Data: off = 0x%lx, size = 0x%lu,"
+ printf(" Elf Data: off = 0x%lx, size = 0x%lu,"
" type = %u\n",
elf_data->d_off, elf_data->d_size,
elf_data->d_type);
@@ -270,7 +273,7 @@
elf_strptr(elf, stab, sym.st_name);
if (verbose)
- printf("\t\tSymbol = %s, info = 0x%02X,"
+ printf(" Symbol = %s, info = 0x%02X,"
" sect = #%u, size = %lu,"
" addr = 0x%lx\n",
sym_name, sym.st_info, sym.st_shndx,
@@ -283,7 +286,7 @@
if (scn == NULL) {
if (verbose)
- printf("\t\t"
+ printf(" "
"Cannot get section #%u\n",
sym.st_shndx);
continue;
@@ -291,7 +294,7 @@
if (gelf_getshdr(scn, &sh) == NULL) {
if (verbose)
- printf("\t\t"
+ printf(" "
"Cannot get SH for #%u\n",
sym.st_shndx);
continue;
@@ -301,7 +304,7 @@
if (data == NULL) {
if (verbose)
- printf("\t\t"
+ printf(" "
"Cannot get data for #%u\n",
sym.st_shndx);
continue;
@@ -309,7 +312,7 @@
if (data->d_buf == NULL) {
if (verbose)
- printf("\t\t"
+ printf(" "
"No data buf for sec #%u\n",
sym.st_shndx);
continue;
@@ -328,7 +331,7 @@
8 + sym.st_value - sh.sh_addr);
if (verbose)
- printf("\t\t"
+ printf(" "
"ver1 = 0x%0x (%u), "
"ver2 = 0x%0x (%u), "
"ver3 = 0x%0x (%u)\n",
@@ -338,7 +341,7 @@
uint8_t *c = (uint8_t *)data->d_buf +
sym.st_value - sh.sh_addr;
if (verbose) {
- printf("\t\tFirmware list: ");
+ printf(" Firmware list: ");
for (int i = 0;
i < sym.st_size; i++)
printf("%02X ", c[i]);
@@ -364,6 +367,9 @@
}
elf_end(elf);
+
+ if (verbose)
+ printf("\n");
if (deps_count)
fprintf(stderr, "Found %u dependencies in file %s\n",