File (cmd tool) shows /bin/ls is a fat binary on macOS (Sonoma 14.2.1 on Apple M1) and otool -f confirms that:
% file /bin/ls
/bin/ls: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/ls (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/ls (for architecture arm64e): Mach-O 64-bit executable arm64e
% otool -f /bin/ls
Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0
cputype 16777223
cpusubtype 3
capabilities 0x0
offset 16384
size 72896
align 2^14 (16384)
architecture 1
cputype 16777228
cpusubtype 2
capabilities 0x80
offset 98304
size 88816
align 2^14 (16384)
However, otool -h shows only one Mach-O header:
% otool -h /bin/ls
/bin/ls:
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777228 2 0x80 2 19 1728 0x00200085
The same behavior can be observed for other binaries in /bin folder.
Interestingly, otool -h does show all architectures for fat binaries being part of 3rd party applications (being arm64, while system binaries are arm64e). Example:
% otool -h /Applications/Firefox.app/Contents/MacOS/firefox
/Applications/Firefox.app/Contents/MacOS/firefox (architecture x86_64):
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777223 3 0x80 2 21 1856 0x00210085
/Applications/Firefox.app/Contents/MacOS/firefox (architecture arm64):
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777228 0 0x00 2 22 1880 0x00210085
Why does otool -h not show x86_64 Mach-O header for system binaries (arm64e) while it does show it for 3rd party binaries (arm64)?