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?
Date: Fri, 25 Sep 2026 01:20:06 UTC
On Thu, Sep 24, 2026 at 05:16:10PM -0700, 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.
jevents is the consumer of the libc which exports compat symbols.
If old jevents does not work with the current libc there is a compat
bug, which needs to be identified and fixed.
>
> If symbol versioning applies here, how does it work?
Same as in all other cases. old jevents should record references to
symbols with version FBSD_1.5, and runtime linker resolves names to
corresponding compat implementation.
I do not know, but suspect that jevents, which is the build tool,
gets somehow compiled against the new headers but linked with old
libc.so,7.
>
> (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.)
>
> >
> > Warner
> >
> > On Thu, Sep 24, 2026, 5:37 PM Mark Millard <marklmi@yahoo.com
> > <mailto:marklmi@yahoo.com>> wrote:
> >
> > The SHLIB_MAJOR last changed on 2006-05-21 and is:
> >
> > LIB=c
> > SHLIB_MAJOR= 7
> >
> > Changes to the binary interface for function (pointers) passed into any
> > FreeBSD libc inteferface functions lead to incompatibilities. In this
> > case FTSENT (a.k.a. struct _ftsent) is used via function arguments in:
> >
> > 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 *));
> >
> > but, as of 2026-08-03, the content of the *FTSENT (i.e.,
> > struct _ftsent) was updated in a non-binary compatible way:
> >
> > author Jitendra Bhati <bhatijitendra2022@gmail.com
> > <mailto:bhatijitendra2022@gmail.com>> 2026-06-12 17:07:55
> > +0000
> > committer Alan Somers <asomers@FreeBSD.org> 2026-08-03
> > 19:12:28 +0000
> > commit 4bd01d6ae01632501b63438b8d9a401db9744a78 (patch)
> > tree e896c1844fa0fa5897efc4e591ebe02f59a64e61 /include/fts.h
> > parent 9590878fca68e62c63d607da73139698e204d0f0 (diff)
> >
> > . . .
> >
> > Sponsored by: Google LLC (GSoC 2026)
> > Reviewed by: asomers
> > Pull Request: https://github.com/freebsd/freebsd-src/pull/2303
> > <https://github.com/freebsd/freebsd-src/pull/2303>
> > Diffstat (limited to 'include/fts.h')
> > -rw-r--r-- include/fts.h 2
> > 1 files changed, 2 insertions, 0 deletions
> > diff --git a/include/fts.h b/include/fts.h
> > index 479905bda463..0308b8ff880b 100644
> > --- a/include/fts.h
> > +++ b/include/fts.h
> > @@ -92,6 +92,8 @@ struct _ftsent {
> > char *fts_path; /* root path */
> > int fts_errno; /* errno for this node */
> > int fts_symfd; /* fd for symlink */
> > + int fts_dirfd; /* fd for parent directory */
> > + int __fts_reserved[3]; /* reserved for future use */
> > __size_t fts_pathlen; /* strlen(fts_path) */
> > __size_t fts_namelen; /* strlen(fts_name) */
> >
> > There is a lot more after what is visible in the diff (starting with the
> > inserted fields to have it all together):
> >
> > 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) */
> >
> > __ino_t fts_ino; /* inode */
> > __dev_t fts_dev; /* device */
> > __nlink_t fts_nlink; /* link count */
> >
> > #define FTS_ROOTPARENTLEVEL -1
> > #define FTS_ROOTLEVEL 0
> > long fts_level; /* depth (-1 to N) */
> >
> > #define FTS_D 1 /* preorder directory */
> > . . .
> > #define FTS_W 14 /* whiteout object */
> > int fts_info; /* user status for FTSENT
> > structure */
> >
> > #define FTS_DONTCHDIR 0x01 /* don't chdir .. to the
> > parent */
> > #define FTS_SYMFOLLOW 0x02 /* followed a symlink to get
> > here */
> > #define FTS_ISW 0x04 /* this is a whiteout object */
> > unsigned fts_flags; /* private flags for FTSENT
> > structure */
> >
> > #define FTS_AGAIN 1 /* read node again */
> > ...
> > #define FTS_SKIP 4 /* discard node */
> > int fts_instr; /* fts_set() instructions */
> >
> > struct stat *fts_statp; /* stat(2) information */
> > char *fts_name; /* file name */
> > FTS *fts_fts; /* back pointer to main FTS */
> > };
> >
> >
> > An example is in src/tree/lib/libpmc/pmu-events/jevents.c that has:
> >
> > #include <fts.h>
> >
> > static int
> > #if defined(__linux__) || defined(__APPLE__)
> > fts_compare(const FTSENT **a, const FTSENT **b)
> > #else
> > fts_compare(const FTSENT * const *a, const FTSENT * const *b)
> > #endif
> > {
> > return (strcmp((*a)->fts_name, (*b)->fts_name));
> > }
> >
> > Note that the compile time offset for ->fts_name change changes
> > depending on which header version was used and that it will not track
> > the environment that jevents is run in.
> >
> > The same jevent executable file's fts_compare code (non-linux/non-apple)
> > is compatible with only one of:
> >
> > ) a libc.so.7 *FTSENT from before the 2026-08-03 change
> > vs.
> > ) a libc.so.7 *FTSENT from after the 2026-08-03 change
> >
> >
> > I have observed SIGSEGV with backtraces that show the failure at such a
> > fts_compare (*?)->fts_name based deference. That is what eventually
> > resulted in my noticing the above in this unfamiliar code.
> >
> >
> > --
> > ===
> > Mark Millard
> > marklmi at yahoo.com <http://yahoo.com>
> >
> >
>
>
> --
> ===
> Mark Millard
> marklmi at yahoo.com