See https://lists.freebsd.org/archives/freebsd-current/2025-November/009500.html for background. To reproduce, set debug.link_elf_leak_locals=0 debug.link_elf_obj_leak_locals=0 then # kldload virtio_p9fs.ko link_elf_obj: symbol p9_init_zones undefined linker_load_file: /boot/kernel/virtio_p9fs.ko - unsupported file type kldload: an error occurred while loading module virtio_p9fs.ko. Please check dmesg(8) for more details. The module's Makefile should have an EXPORT_SYMS= line that lists all of the symbols to be intentionally exported. Another option is to just set EXPORT_SYMS=YES. An explicit export list is preferred over EXPORT_SYMS=YES, unless we actually want all symbols exported.
We can find the symbols needed by virtio_p9fs.ko with something like % nm obj/virtio_p9fs.ko | sed -n 's/^.* U U//p' | sort > undef.syms % nm /boot/kernel/kernel | sed -n 's/^.* T //p' | sort > kernel.syms % comm -23 undef.syms kernel.syms The p9 symbols this reveals are: p9_debug_level p9_destroy_zones p9_init_zones p9_register_trans
I’ve tested the fixes by disabling symbol-leak suppression (debug.link_elf_leak_locals=0 and debug.link_elf_obj_leak_locals=0) to ensure unresolved symbols become visible. With the updated EXPORT_SYMS entries added to sys/modules/p9fs/Makefile, both modules now load successfully without any undefined symbol errors. Patch Used: diff --git a/sys/modules/p9fs/Makefile b/sys/modules/p9fs/Makefile index ae90cb3a46d7..08a5b3ec0fa2 100644 --- a/sys/modules/p9fs/Makefile +++ b/sys/modules/p9fs/Makefile @@ -5,4 +5,10 @@ SRCS= vnode_if.h \ p9_client.c p9_protocol.c p9_transport.c \ p9fs_subr.c p9fs_vfsops.c p9fs_vnops.c + EXPORT_SYMS= \ + p9_debug_level \ + p9_destroy_zones \ + p9_init_zones \ + p9_register_trans + .include <bsd.kmod.mk> Test Logs: root@freebsd16:~ # cat /boot/loader.conf debug.link_elf_leak_locals=0 debug.link_elf_obj_leak_locals=0 root@freebsd16:~ # sysctl debug.link_elf_leak_locals debug.link_elf_leak_locals: 0 root@freebsd16:~ # sysctl debug.link_elf_obj_leak_locals debug.link_elf_obj_leak_locals: 0 root@freebsd16:~ # kldstat Id Refs Address Size Name 1 8 0xffffffff80200000 2126750 kernel 2 1 0xffffffff82d10000 3220 intpm.ko 3 1 0xffffffff82d14000 2178 smbus.ko 4 1 0xffffffff82d17000 2a80 mac_ntpd.ko root@freebsd16:~ # kldload /boot/kernel/virtio_p9fs.ko root@freebsd16:~ # kldstat Id Refs Address Size Name 1 14 0xffffffff80200000 2126750 kernel 2 1 0xffffffff82d10000 3220 intpm.ko 3 1 0xffffffff82d14000 2178 smbus.ko 4 1 0xffffffff82d17000 2a80 mac_ntpd.ko 5 1 0xffffffff82d1a000 24e0 virtio_p9fs.ko 6 1 0xffffffff82d1d000 a888 p9fs.ko Both virtio_p9fs.ko and p9fs.ko now load without reporting undefined symbols.
Some developers have expressed a desire to avoid using per-symbol export lists and generally use EXPORT_SYMS=YES, possibly adding missing `static` declarations where necessary. I took a look at this module with that in mind: With the four explicit EXPORT_SYMS, as expected `readelf -s | grep GLOBAL | grep -v UND` reports: 159: 0000000000000000 4 OBJECT GLOBAL DEFAULT 10 p9_debug_level 169: 00000000000008b0 132 FUNC GLOBAL DEFAULT 1 p9_init_zones 171: 0000000000000940 41 FUNC GLOBAL DEFAULT 1 p9_destroy_zones 186: 00000000000036d0 34 FUNC GLOBAL DEFAULT 1 p9_register_trans With EXPORT_SYMS=YES many symbols are GLOBAL: 98: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 p9_is_proto_dotl 99: 0000000000000010 15 FUNC GLOBAL DEFAULT 1 p9_is_proto_dotu 100: 0000000000000020 59 FUNC GLOBAL DEFAULT 1 p9_client_disconnect 101: 0000000000000000 4 OBJECT GLOBAL DEFAULT 10 p9_debug_level 103: 0000000000000060 59 FUNC GLOBAL DEFAULT 1 p9_client_begin_disconnect 104: 00000000000000a0 72 FUNC GLOBAL DEFAULT 1 p9_tag_create 106: 00000000000000f0 68 FUNC GLOBAL DEFAULT 1 p9_tag_destroy 108: 0000000000000140 130 FUNC GLOBAL DEFAULT 1 p9_fid_create 111: 00000000000001d0 80 FUNC GLOBAL DEFAULT 1 p9_fid_destroy 112: 0000000000000220 364 FUNC GLOBAL DEFAULT 1 p9_client_version 113: 0000000000002ae0 2561 FUNC GLOBAL DEFAULT 1 p9_buf_readf 116: 0000000000003560 35 FUNC GLOBAL DEFAULT 1 p9_buf_prepare 117: 00000000000020f0 2452 FUNC GLOBAL DEFAULT 1 p9_buf_vwritef 118: 0000000000003590 93 FUNC GLOBAL DEFAULT 1 p9_buf_finalize 121: 00000000000008b0 132 FUNC GLOBAL DEFAULT 1 p9_init_zones 123: 0000000000000940 41 FUNC GLOBAL DEFAULT 1 p9_destroy_zones 125: 0000000000000970 461 FUNC GLOBAL DEFAULT 1 p9_client_create 130: 0000000000003730 64 FUNC GLOBAL DEFAULT 1 p9_get_trans_by_name 132: 0000000000000b40 160 FUNC GLOBAL DEFAULT 1 p9_client_destroy 134: 0000000000000be0 498 FUNC GLOBAL DEFAULT 1 p9_client_attach 135: 0000000000000de0 141 FUNC GLOBAL DEFAULT 1 p9_client_remove 136: 0000000000000e70 124 FUNC GLOBAL DEFAULT 1 p9_client_unlink 137: 0000000000000ef0 249 FUNC GLOBAL DEFAULT 1 p9_client_clunk 138: 0000000000000ff0 766 FUNC GLOBAL DEFAULT 1 p9_client_walk 139: 00000000000012f0 316 FUNC GLOBAL DEFAULT 1 p9_client_open 140: 0000000000001430 345 FUNC GLOBAL DEFAULT 1 p9_client_readdir 142: 0000000000001590 460 FUNC GLOBAL DEFAULT 1 p9_client_read 143: 0000000000001760 454 FUNC GLOBAL DEFAULT 1 p9_client_write 144: 0000000000001930 279 FUNC GLOBAL DEFAULT 1 p9_client_file_create 145: 0000000000001a50 300 FUNC GLOBAL DEFAULT 1 p9_client_statfs 146: 0000000000001b80 155 FUNC GLOBAL DEFAULT 1 p9_client_renameat 147: 0000000000001c20 256 FUNC GLOBAL DEFAULT 1 p9_create_symlink 148: 0000000000001d20 144 FUNC GLOBAL DEFAULT 1 p9_create_hardlink 149: 0000000000001db0 213 FUNC GLOBAL DEFAULT 1 p9_readlink 150: 0000000000001e90 394 FUNC GLOBAL DEFAULT 1 p9_client_getattr 151: 0000000000002020 193 FUNC GLOBAL DEFAULT 1 p9_client_setattr 154: 0000000000000140 112 OBJECT GLOBAL DEFAULT 4 sysctl___vfs_p9fs 158: 00000000000034f0 97 FUNC GLOBAL DEFAULT 1 p9stat_read 159: 00000000000035f0 20 FUNC GLOBAL DEFAULT 1 p9_buf_reset 160: 0000000000003610 181 FUNC GLOBAL DEFAULT 1 p9_dirent_read 162: 00000000000036d0 34 FUNC GLOBAL DEFAULT 1 p9_register_trans 163: 0000000000000020 16 OBJECT GLOBAL DEFAULT 10 transports 164: 0000000000003700 37 FUNC GLOBAL DEFAULT 1 p9_unregister_trans 166: 0000000000003790 12 FUNC GLOBAL DEFAULT 1 p9fs_proto_dotl 167: 00000000000037a0 551 FUNC GLOBAL DEFAULT 1 p9fs_init_session 169: 00000000000039d0 104 FUNC GLOBAL DEFAULT 1 p9fs_prepare_to_close 171: 0000000000003a40 24 FUNC GLOBAL DEFAULT 1 p9fs_complete_close 172: 0000000000003a60 95 FUNC GLOBAL DEFAULT 1 p9fs_close_session 174: 0000000000003ac0 225 FUNC GLOBAL DEFAULT 1 p9fs_fid_remove_all 175: 0000000000003bb0 276 FUNC GLOBAL DEFAULT 1 p9fs_fid_remove 178: 0000000000003cd0 176 FUNC GLOBAL DEFAULT 1 p9fs_fid_add 179: 0000000000003d80 914 FUNC GLOBAL DEFAULT 1 p9fs_get_fid 182: 0000000000004250 153 FUNC GLOBAL DEFAULT 1 p9fs_dispose_node 183: 00000000000042f0 62 FUNC GLOBAL DEFAULT 1 p9fs_node_cmp 184: 0000000000004330 55 FUNC GLOBAL DEFAULT 1 p9fs_destroy_node 185: 0000000000004370 1172 FUNC GLOBAL DEFAULT 1 p9fs_vget_common 187: 0000000000004fd0 261 FUNC GLOBAL DEFAULT 1 p9fs_reload_stats_dotl 190: 0000000000000480 680 OBJECT GLOBAL DEFAULT 4 p9fs_vnops 204: 0000000000000040 8 OBJECT GLOBAL DEFAULT 10 p9fs_getattr_zone 205: 0000000000000048 8 OBJECT GLOBAL DEFAULT 10 p9fs_setattr_zone 208: 0000000000000050 8 OBJECT GLOBAL DEFAULT 10 p9fs_pbuf_zone 209: 0000000000000038 8 OBJECT GLOBAL DEFAULT 10 p9fs_io_buffer_zone 211: 00000000000001f0 208 OBJECT GLOBAL DEFAULT 4 p9fs_vfsops 214: 0000000000004ea0 303 FUNC GLOBAL DEFAULT 1 p9fs_cleanup 217: 00000000000050e0 263 FUNC GLOBAL DEFAULT 1 p9fs_stat_vnode_dotl Will update again here after more discussion