Recently I ran into an unexpected issue when attempting to rebuild and reinstall world and kernel. I was doing this to try to figure out why another install of xlibre was dropping me into VESA. I'll spoil that cliffhanger to say that for whatever reason graphics/drm-kmod is not installed in that process and so my AMDGPU driver doesn't get used. It is for the user to add graphics/drm-kmod even with xorg, not sure how this couldn't be automated in any way. I cannot say why missing drm-kmod wasn't an issue before except if there was an update at xlibre-server upstream which is entirely possible, or very mysteriously it remained installed. The output of the error-aborted buildworld is below.
root@ichigo:/usr/src # make -j `sysctl -n hw.ncpu` buildworld && make -j `sysctl -n hw.ncpu` kernel KERNCONF=ICHIGO KERNCONFDIR /root/kernconf/ && echo "go to singleuser" --- buildworld --- make[1]: /usr/src/Makefile.inc1:362: SYSTEM_COMPILER: Determined that CC=/usr/local/bin/ccache cc matches the source tree. Not bootstrapping a cross-compiler. make[1]: /usr/src/Makefile.inc1:367: SYSTEM_LINKER: Determined that LD=ld matches the source tree. Not bootstrapping a cross-linker. --- buildworld_prologue --- -------------------------------------------------------------- >>> World build started on Sat Oct 4 21:19:21 CDT 2025 -------------------------------------------------------------- --- _cleanworldtmp --- >>> Deleting stale files in build tree... ld-elf.so.1: Shared object "libutil.so.9" not found, required by "env" 0.00 real 0.00 user 0.00 sys *** [_cleanworldtmp] Error code 1 make[1]: stopped making "buildworld" in /usr/src make[1]: 1 error make[1]: stopped making "buildworld" in /usr/src make: stopped making "buildworld" in /usr/src
I had searched online for the issue but once more nothing pertinent appeared, or things that may have been vaguely pertinent were horribly outdated. When I returned once more, after a few further updates via git to /usr/src, just to be sure that wasn't somehow the issue. On a whim I checked to see if Grok might point me in any direction that could help me solve it. I gave it a query: "How can I update world and kernel on FreeBSD 15-stable if it fails because libutil is missing?" and part of the answer is below, it seems to have searched 40 urls, 10 of them from reddit, part from freebsd forums and handbooks.
Step-by-Step Fix Follow these steps to clean up and rebuild. This resolves most libutil-related failures by ensuring a consistent build tree. 1. Update Source Tree Fetch the latest 15-STABLE sources to ensure you're building against current code: cd /usr/src git pull # If using Git (recommended; init with `git clone https://git.FreeBSD.org/src.git` if needed) # Or, if using SVN (legacy): svn update 2. Clean Build Artifacts Remove stale object directories and temporary files that could reference old libutil versions: rm -rf /usr/obj/* cd /usr/src make clean # Cleans world build remnants 3. Check and Clean Installed Headers/Libraries (If Mismatched) If /usr/include has mixed dates (e.g., old headers conflicting with new kernel), back it up and reinstall base headers: mv /usr/include /usr/include.old # Backup make -C /usr/src/etc/ maketmpenv # Recreates /usr/include from source 4. Verify libutil header: ls /usr/include/libutil.h (should exist post-this step). If /lib/libutil.so.9 is missing or broken: ls -l /lib/libutil.so* # Should show libutil.so.9 -> libutil.so.9.x
The first step I had already done. The second step I immediately followed with the next ones. I was able to rename the /usr/include directory but maketmpenv doesn't seem to exist any longer which points to how AI (like Grok) is not always 100% accurate and its results should always be checked or verified. After I tried having it build within /usr/src/etc and it failed after a little built, I tried buildworld again which also didn't work yet, and so did a cleanworld. Of course I had to put things back as they were originally, so renamed /usr/include.old since the step after that couldn't create the fresh /usr/include. With it all back as it should be, I tried buildworld again and it iterated through all the needed things to build, building as it went.
rm -rf /usr/obj/* root@ichigo:/usr/src # make clean mv /usr/include /usr/include.old make -C /usr/src/etc/ maketmpenv cd /usr/src/etc make cd /usr/src make -j `sysctl -n hw.ncpu` buildworld && make -j `sysctl -n hw.ncpu` kernel KERNCONF=ICHIGO KERNCONFDIR /root/kernconf/ && echo "go to singleuser" mv /usr/include.old /usr/include make cleanworld make -j `sysctl -n hw.ncpu` buildworld && make -j `sysctl -n hw.ncpu` kernel KERNCONF=ICHIGO KERNCONFDIR /root/kernconf/ && echo "go to singleuser"
Now that it seems everything is working, I should have tried buildworld after the rm -rf /usr/obj/* because that seems to have been likely what cured the weird state my build environment had. Perhaps something in the newer build routine for 15-stable allowed things to remain in a condition that would not allow me to immediately buildworld as I had been doing so many times in the past. I recall some old instructions, I believe from back when I was using or playing with PC-BSD, that included cleaning up /usr/obj/ before a fresh buildworld and buildkernel. I always end with delete-old and delete-old-libs but do I then also remove /usr/obj? I suspect that this is a step directly before building so that is where it will go. It looks like I will have to add that step into my normal routine just to be sure, so that future attempts will not leave me with an error like Shared object "libutil.so.9" not found again in the future.
Had I not known, recalled that cleaning of /usr/obj/ as a step of the build process, I would not have recognized it was the cure. Something like Grok could help spur an idea for a solution, it may give avenues to investigate and research, but be wary that it is pattern matching software and truly doesn't know like a human might. An AI may very well give old advice, as we note it mentioned the legacy SVN. I'll be adding more things to distilled and adjusting the buildworld + kernel updating info and probably blog posts as well.
There was also a situation where etcupdate needed to have an mtree change reconciled, but since I do not modify that myself and I am not a dev with any reason for it to be anything but vanilla, I just told it to accept 'their version' of the file. The odd part was that etcupdate first gave me options that were not helpful because I did not understand what I was seeing, nor how that mtree file should look, so my choice of diff had to be cancelled. Right after, I noticed there were other commands not immediately listed, one of them was reconcile and gave its options. When I tried to resolve instead, it wouldn't take the command with 'theirs' as the option, so I had to exit etcupdate entirely, and when I retried etcupdate resolve with tf it worked properly. I am not sure what this affected, of course it cured the conflict, aside from any future etcupdate attempt functioning properly and completely, not prevented by a needed though not initiated resolve. Somehow I thought it said 'reconcile' somewhere in that tool's interaction but I cannot find proof of that now. According to the man 8 etcupdate
Resolve Mode The resolve mode is used to resolve any conflicts encountered during a merge. In this mode, etcupdate iterates over any existing conflicts prompting the user for actions to take on each conflicted file. For each file, the following actions are available: (p) postpone Ignore this conflict for now. (df) diff-full Show all changes made to the merged file as a unified diff. (e) edit Change the merged file in an editor. (r) resolved Install the merged version of the file into the destination directory. (mf) mine-full Use the version of the file in the destination directory and ignore any changes made to the file in the “current” tree. (tf) theirs-full Use the version of the file from the “current” tree and discard any local changes made to the file. (h) help Display the list of commands.
This is not the first time that etcupdate had to correct something before other steps would succeed. I am not sure how I could have missed that that previous installworld had a conflict and I passed that step seeming to succeed with a whole install. I also have no idea what the change to that mtree file was or how it could affect anything itself. How do I make certain that such a step is not missed? Maybe I was tired or distracted and that caused me not to see it, so I need to do a buildworld and new kernel install when I am not sleepy and when I can focus. The /usr/obj/ clean issue is an easy fix, just add it to steps taken when preparing to rebuild world.
I wish I could say that each of these missteps makes me less likely to repeat them, that I will learn and remember what I learned and correct things for all successive efforts. I do my best to keep track of what I do and since writing this blog I have some notes in that form I can reference. Surely even if I repeat a mistake, those who read of my trials will have the smallest chance to make my mistakes and will instead avoid them.