Re: Should /usr/include/fts.h adding fts_dirfd and __fts_reserved[3] to FTSENT (a.k.a. struct _ftsent) require a change from libc.so.7 to .8 because of some resulting binary incompatibilities?

From: Konstantin Belousov <kib_at_freebsd.org>
Date: Sun, 27 Sep 2026 20:33:37 UTC
On Sun, Sep 27, 2026 at 01:19:15PM -0700, Mark Millard wrote:
> On 9/24/26 20:06, Konstantin Belousov wrote:
> > On Thu, Sep 24, 2026 at 06:57:04PM -0700, Mark Millard wrote:
> >> On 9/24/26 17:16, Mark Millard wrote:
> >>> On 9/24/26 16:40, Warner Losh wrote:
> >>>> No. Symbol versioning.
> >>>
> >>> jevents has no symbol versioning for its own, internal implementation of
> >>> a function it decided to name fts_compare as far as I can tell. That
> >>> same fts_compare implementation is passed in everywhere that the same
> >>> libc.so.7 naming is in use and then is called no matter what,
> >>> independent of which fts.h vintage it was built with.
> >>>
> >>> If symbol versioning applies here, how does it work?
> >>>
> >>> (Why I ended up with the mix of an older jevents with a post-change
> >>> environment is a separate issue and is my fault. But nothing rejected or
> >>> fixed the combination. libc.so.8 vs. libc.so.7 would have rejected.)
> >>
> >> Your note caused me to somewhat better isolate the structure of the mix:
> >>
> >> The kernel is updated  to  : 1600026
> >> The world  is updating from: 1600019 (so: old fts.h)
> >> The world  is updating to  : 1600026 (or trying to)
> >>
> >> # readelf -a
> >> /usr/obj/BUILDs/main-CA76-dbg-clang/usr/main-src/arm64.aarch64/lib/libc/libc.so.7
> >> | grep fts_open@
> >> 0000000000243d10  00000c7a00000402 R_AARCH64_JUMP_SLOT
> >> 00000000000c0c40 fts_open@@FBSD_1.9 + 0
> >>   3194: 00000000000c0c40   128 FUNC    GLOBAL DEFAULT    14
> >> fts_open@@FBSD_1.9
> >>   3195: 00000000000c29c0  1016 FUNC    GLOBAL DEFAULT    14
> >> fts_open@FBSD_1.0
> >>   3196: 00000000000c4260   924 FUNC    GLOBAL DEFAULT    14
> >> fts_open@FBSD_1.1
> >>   3197: 00000000000c5b60   124 FUNC    GLOBAL DEFAULT    14
> >> fts_open@FBSD_1.5
> >>
> >> (Associated with building from modern 1600026 source at the time.)
> >>
> >> jevents got:
> >>
> >> # readelf -a
> >> /usr/obj/BUILDs/main-CA76-dbg-clang/usr/main-src/arm64.aarch64/lib/libpmc/pmu-events/jevents
> >> | grep fts_open@
> >> 0000000000037680  0000001300000402 R_AARCH64_JUMP_SLOT
> >> 0000000000000000 fts_open@FBSD_1.9 + 0
> >>     19: 0000000000000000     0 FUNC    GLOBAL DEFAULT   UND
> >> fts_open@FBSD_1.9
> >>
> >>
> >> But, as a build-tool, jevents was built with /usr/include/fts.h instead
> >> of with the /usr/main-src/ tree that I use for the build if the update.
> >> (/usr/src/ is from pkgbase.):
> >>
> >> # diff -u /usr/include/fts.h /usr/main-src/include/fts.h
> >> --- /usr/include/fts.h	2025-05-11 07:29:42.791259000 -0700
> >> +++ /usr/main-src/include/fts.h	2026-09-18 16:13:35.766402000 -0700
> >> @@ -1,4 +1,4 @@
> >> -/*-
> >> +/*
> >>   * SPDX-License-Identifier: BSD-3-Clause
> >>   *
> >>   * Copyright (c) 1989, 1993
> >> @@ -92,6 +92,8 @@
> >>  	char *fts_path;			/* root path */
> >>  	int fts_errno;			/* errno for this node */
> >>  	int fts_symfd;			/* fd for symlink */
> >> +	int fts_dirfd;                  /* fd for this directory, if a
> >> directory */
> >> +	int __fts_reserved[3];          /* reserved for future use */
> >>  	__size_t fts_pathlen;		/* strlen(fts_path) */
> >>  	__size_t fts_namelen;		/* strlen(fts_name) */
> >>
> >> @@ -146,6 +148,8 @@
> >>  #define	 fts_get_stream(ftsent)	((ftsent)->fts_fts)
> >>  FTS	*fts_open(char * const *, int,
> >>  	    int (*)(const FTSENT * const *, const FTSENT * const *));
> >> +FTS	*fts_openat(int, char * const *, int,
> >> +	    int (*)(const FTSENT * const *, const FTSENT * const *));
> >>  #ifdef __BLOCKS__
> >>  FTS	*fts_open_b(char * const *, int,
> >>  	    int (^)(const FTSENT * const *, const FTSENT * const *));
> >>
> >> Thus the result was a jevents using:
> >>
> >> ) the old /usr/include/fts.h header of its "host" environment
> >> but also:
> >> ) the new symbol version fts_open@@FBSD_1.9 was used.
> >>
> >> Nothing rejected the bad combination (other than a later SIGSEGV).
> > 
> > Right, if it is the case, then the ABI mismatch is unavoidable.
> > By itself, this sounds like a serious bug in our build system.
> > 
> >>
> >> I do not see how /usr/include/fts.h file content would end up somehow in
> >> control over fts_open@@FBSD_1.5 vs. fts_open@@FBSD_1.9 being used for
> >> the likes of the jevents build.
> > 
> > Include files provide a part of the ABI, which is used by compiler.
> > In particular, include files define the layout of C structures.
> > 
> > Shared libraries provide another part, giving the (default) versions to the
> > symbols referenced by the final object.  Layout of the parameters to
> > the specific version of the function must match to what headers used
> > at compile time defined.
> > 
> > Mixing these parts across ABI changes cannot work.
> > 
> > 
> 
> I think I've identified what went wrong in the history of trying this
> update in my context: a separate installworld race issue (-j used) lead
> to an initial partial update of the chroot/jail tree, where:
> 
> ) libc.so.7 updated before that installworld failed.
> 
> ) /usr/include/fts.h did not.
>   (Would have installed after the installworld failure point.)
> 
> So later activity then had the incoherent mix without me identifying it
> at the time. For this sequencing, libc.so.8 use would not have helped.

Just for the posteriority:
libc.so.8 will never happen.  At least now we do not have any reason
to believe otherwise.

> 
> (Via avoiding -j use for installworld I've gotten all my chroot/jail
> tree personal builds to avoid the race.)
> 
> -- 
> ===
> Mark Millard
> marklmi at yahoo.com