How to inspect Mach-O files
clang main.c produces an
which on macOS is a binary in the Mach-O (“Mach object”) format:
$ clang main.c $ file a.out a.out: Mach-O 64-bit executable x86_64
clang produces Mach-O files when run on macOS
because the executable format in macOS is Mach-O.
By contrast, on Linux,
clang produces ELF files (“Executable and Linkable Format”),
because Linux’s executable format is ELF.
This is documented in man pages.
On macOS, the page for the
execve system call says:
execve()transforms the calling process into a new process. The new process is constructed from an ordinary file … This file is either an executable object file, or a file of data for an interpreter. An executable object file consists of … see a.out(5).
The page for
The object files produced by the assembler and link editor are in Mach-O (Mach object) file format.
Since Mach-O files are just ordinary files, we can dig into the bits-and-bytes.
But we can also inspect Mach-O files with a tool called
otool (“object tool”).
For example, we can see what dynamic libraries our
$ otool -L a.out a.out: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
.dylib is a Mach-O dynamic module/library.
clang decided that our program should depend on a dynamic library at
This provides the implementations of many things used by C programs, such as stdio functions.
Dynamic libraries can themselves require dynamic libraries.
The big dylib at
/usr/lib/libSystem.B.dylib requires a bunch more dylibs:
$ otool -L /usr/lib/libSystem.B.dylib /usr/lib/libSystem.B.dylib: ... /usr/lib/system/libsystem_asl.dylib (compatibility version 1.0.0, current version 349.50.5) /usr/lib/system/libsystem_blocks.dylib (compatibility version 1.0.0, current version 67.0.0) /usr/lib/system/libsystem_c.dylib (compatibility version 1.0.0, current version 1158.50.2) /usr/lib/system/libsystem_configuration.dylib (compatibility version 1.0.0, current version 888.60.2) /usr/lib/system/libsystem_coreservices.dylib (compatibility version 1.0.0, current version 41.4.0) ...
An important dylib in here is
It defines a bunch of functions used by C programs.
For example, this dylib defines the function
We can see this using a tool
nm (“name”), which shows the name/symbol table of a Mach-O file.
$ nm -g /usr/lib/system/libsystem_c.dylib | grep fprintf 000000000003ed45 T _fprintf 000000000003ee18 T _fprintf_l 0000000000046355 T _vfprintf 0000000000046308 T _vfprintf_l
Notice that the symbol is not
_fprintf. This is because “The name of a symbol representing a function that conforms to standard C calling conventions is the name of the function with an underscore prefix”, according to Apple.
I wrote this because I felt like it. This post is not associated with my employer. This site is hosted by Netlify (who are great, but I'm not associated with them either).