Mach-O header analysis and editing
macholib is a Python library used for analyzing and editing Mach-O headers, the executable format employed by macOS. It functions primarily as a dependency analysis tool, capable of rewriting dylib references within Mach-O headers to be @executable_path relative. The library is pure Python, platform, and endian-independent, currently at version 1.16.4, with active development and regular releases.
Warnings
- breaking The standalone command-line tools `macho_find`, `macho_dump`, and `macho_standalone` were deprecated in version 1.4. They have been replaced by the module-execution interface: `python -m macholib find`, `python -m macholib dump`, and `python -m macholib standalone` respectively.
- deprecated In version 1.4, private functionality (identified by names starting with an underscore) within modules was renamed, and basic packable types in `macholib.ptypes` were renamed to better represent corresponding C types. Old `ptypes` names are deprecated and will be removed in future releases.
- gotcha Version 1.16 introduced the `allow_unknown_load_commands` option to `MachO` and `macholib.MachOHeader`. If set to `False` (which might be the default in some contexts or previous versions' implicit behavior), parsing a file with unknown load commands will raise an error. If `True`, unknown commands are ignored. This can change error-handling behavior for malformed or newer Mach-O files.
- deprecated The mapping `macholib.macho_dump.ARCH_MAP` was undocumented and no longer used internally by `macholib` as of version 1.4.2, and was scheduled for removal in a subsequent release.
Install
-
pip install macholib
Imports
- MachO
from macholib.MachO import MachO
- dyld
from macholib import dyld
- framework
from macholib import framework
- util
from macholib import util
Quickstart
import os
from macholib.MachO import MachO
from macholib import dyld
def get_macho_dependencies(filepath):
try:
macho = MachO(filepath)
print(f"Analyzing: {filepath}")
for header in macho.headers:
print(f" Architecture: {header.architecture()}")
for idx, name, other in header.walk_libraries():
resolved_name = dyld.dyld_find(name, executable_path=filepath)
print(f" Depends on: {name} (Resolved: {resolved_name})")
except Exception as e:
print(f"Error processing {filepath}: {e}")
# Example usage with a common macOS binary
# Ensure the file exists, e.g., '/usr/bin/ls'
system_binary = os.environ.get('MACHO_BINARY_PATH', '/usr/bin/ls')
if os.path.exists(system_binary):
get_macho_dependencies(system_binary)
else:
print(f"Skipping quickstart: {system_binary} not found. Set MACHO_BINARY_PATH to a valid Mach-O file.")