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 <[email protected]>    2026-06-12 17:07:55
+0000
committer       Alan Somers <[email protected]>       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
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


Reply via email to