Bind fstat$INODE64 on macOS x64 - #855
Open
krejko wants to merge 1 commit into
Open
Conversation
macOS exposes two incompatible fstat ABIs. On x86_64 the bare "fstat" symbol is
the pre-10.5 variant whose struct predates the 64-bit inode layout, so reading
the result as MacStatInformation takes st_mode from the wrong offset. C code
never hits this because <sys/stat.h> redirects fstat to fstat$INODE64, but a raw
P/Invoke binds the legacy symbol.
The practical effect is that HandleIsRegularFile misreports every regular file,
so ScanFileDiscovery discards all of them:
LinkSkipped msg=[Non-regular files are not scanned.] path=/.../book.m4b
An unmatched scan over a populated root then completes successfully with zero
candidates, because directory identity only compares captured-vs-current values
(consistent garbage still matches) while files fail an absolute check.
Verified against the same file on both ABIs, parsing the modern struct:
arm64 fstat mode=0x81c0 type=0x8000 regular=True size=375267
arm64 fstat$INODE64 symbol not present
x86_64 fstat mode=0x25c3 type=0x2000 regular=False size=0
x86_64 fstat$INODE64 mode=0x81c0 type=0x8000 regular=True size=375267
Dispatch on process architecture: x64 needs the $INODE64 suffix, arm64 must keep
the bare symbol because the suffixed one does not exist there and binding it
throws EntryPointNotFoundException.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On macOS x64 the library scan finds zero files in a populated root and reports success. Every file is discarded as non-regular:
The cause is the raw
fstatP/Invoke inPinnedDirectoryCreation.NativeInterop:macOS ships two incompatible
fstatABIs. On x86_64 the bare symbol is the pre-10.5 variant whose struct predates the 64-bit inode layout, so parsing the result asMacStatInformationreadsst_modefrom the wrong offset. C never hits this because<sys/stat.h>redirectsfstat→fstat$INODE64; a P/Invoke binds the legacy symbol directly.HandleIsRegularFilethen evaluates garbage and every regular file is rejected.Evidence
Same file, same modern struct layout, both ABIs:
fstat0x81c00x8000fstat$INODE64fstat0x25c30x2000fstat$INODE640x81c00x80000x2000isS_IFCHR— a character device. The legacy struct puts other fields wherest_modeis expected.Why directories still enumerate (22 of them above) while files vanish: directory identity compares captured-vs-current values, and consistently wrong values still compare equal. Files fail an absolute
== 0x8000check, so they all drop out — which is why the scan looks healthy and simply returns nothing.Changes
Fixed
fstatbinding on process architecture:fstat$INODE64on x64, the barefstaton arm64.arm64 must keep the unsuffixed symbol —
fstat$INODE64does not exist there and binding it throwsEntryPointNotFoundException(verified above).Testing
The ABI comparison above was produced by calling both symbols through
ctypeswith the modern struct, underarch -arm64andarch -x86_64on the same machine and file.dotnet buildclean. The infrastructure project compiles with no new warnings.Not runtime-verified on x64. I develop on Apple Silicon, and exercising this path requires running the whole host under Rosetta. The symbol-level evidence is direct, but I would value a check from anyone on an Intel Mac before merge.
Notes
Scoped strictly to the symbol binding — the struct layout, call sites and every other native path are untouched. arm64 behaviour is unchanged (the bare symbol was already correct there), so this only affects the x64 build, which is the one CI publishes for macOS.
🤖 Generated with Claude Code