Что такое Mach-O: формат исполняемых файлов в macOS простыми словами

Mach-O (Mach Object) — это формат исполняемых файлов, библиотек и объектного кода, который используется в macOS, iOS и других операционных системах Apple. Любое приложение, которое вы запускаете на Mac, любая системная библиотека и даже ядро системы существуют именно в этом формате. Если сравнивать с миром Windows и Linux, то Mach-O занимает то же место, что PE (EXE-файлы) у Microsoft и ELF у Linux: это «контейнер», внутри которого лежит скомпилированный машинный код и всё необходимое для его загрузки в память.

Понимание устройства Mach-O нужно не только разработчикам компиляторов. Оно помогает разбираться, почему приложение не запускается с сообщением о повреждённом файле, что такое «универсальный бинарник», зачем нужна подпись кода, как проверить, под какую архитектуру собрана программа, и что происходит между двойным щелчком по иконке и работающим процессом.

Зачем вообще нужен отдельный формат файлов

Когда компилятор превращает исходный код в машинные инструкции, результат — это просто последовательность байтов, понятных процессору. Но чтобы операционная система могла этот код запустить, ей нужно знать гораздо больше:

  • какие части файла являются кодом, а какие — данными;
  • по какому адресу в памяти загружать каждую часть;
  • какие внешние функции и библиотеки требуются программе;
  • где находится точка входа, с которой начинается выполнение;
  • какие разрешения нужны этому коду при работе.

Всю эту метаинформацию и описывает формат исполняемого файла. Mach-O решает те же задачи, что ELF или PE, но исторически вырос из ядра Mach, разработанного в Университете Карнеги — Меллона в 1980-х годах. Это ядро легло в основу NeXTSTEP, а затем macOS, поэтому формат «перекочевал» вместе с системой и используется в ней до сих пор.

Структура файла Mach-O

Каждый файл Mach-O состоит из трёх логических частей: заголовка, команд загрузки и самих данных.

Заголовок (header)

Первые байты файла — это заголовок, в котором система узнаёт базовые сведения о бинарнике:

  • магическое число — специальная сигнатура, по которой система распознаёт формат; разные значения соответствуют 32-битной, 64-битной версии и порядку байтов;
  • тип файла — исполняемый файл, динамическая библиотека, объектный модуль после компиляции, набор статических библиотек или файл дампа памяти;
  • архитектура — например, ARM64 для процессоров Apple Silicon или x86_64 для Intel;
  • количество команд загрузки, которые следуют далее.

Именно по магическому числу macOS мгновенно понимает, что перед ней Mach-O, ещё до какого-либо анализа содержимого.

Команды загрузки (load commands)

Сразу после заголовка идёт таблица команд загрузки — своего рода «инструкция по сборке» процесса. Каждая команда говорит загрузчику, что сделать с той или иной частью файла. Наиболее важные типы команд:

  • сегменты — указывают, какие области файла и в какие адреса виртуальной памяти отображать;
  • подключение динамических библиотек — список зависимостей вида «загрузить такую-то библиотеку»;
  • точка входа — адрес, с которого начинается выполнение программы;
  • подпись кода — ссылка на блок криптографической подписи в конце файла;
  • минимальная версия ОС — ограничение снизу на версию системы;
  • символы и таблицы экспорта — информация о функциях, которые библиотека предоставляет наружу.

Если упростить: заголовок отвечает на вопрос «что это», а команды загрузки — «что с этим делать при запуске».

Сегменты и секции

Основное содержимое файла организовано в сегменты, а внутри сегментов — в секции. Сегмент — это область, которая целиком отображается в память с определёнными правами доступа; секция — более мелкая единица внутри него. Типичное устройство выглядит так:

Сегмент Что содержит Права доступа
__TEXT Машинный код, константы, заголовочные структуры Чтение и исполнение
__DATA / __DATA_CONST Глобальные переменные, таблицы указателей Чтение и запись (или только чтение)
__LINKEDIT Метаданные: символы, relocation-записи, подпись Чтение

Такое разделение не случайно. Код помещается в память с запретом записи — это усложняет эксплуатацию многих уязвимостей: злоумышленник не может одновременно записать вредоносные инструкции и заставить процессор их выполнить. Данные, наоборот, доступны для изменения во время работы, но не исполняются как код. Современные версии macOS дополнительно защищают неизменяемые части __DATA_CONST, делая их доступными только для чтения после начальной настройки.

Универсальные бинарники: несколько программ в одном файле

Особенность экосистемы Apple — fat binary (универсальный бинарник). Это файл, который содержит несколько независимых образов Mach-O подряд: например, один для Intel x86_64 и один для ARM64. В начале такого файла стоит отдельный «внешний» заголовок со списком вложенных образов и смещениями, где каждый из них находится.

При запуске система читает список, выбирает образ, подходящий под текущий процессор, и работает уже с ним. Благодаря этому одно приложение можно распространять для Mac на Intel и на Apple Silicon без двух отдельных дистрибутивов. Когда переход на собственные процессоры только начинался, тот же механизм обеспечивал совместимость через трансляцию Rosetta: она получала x86_64-образ из универсального файла и выполняла его на ARM-процессоре.

Проверить состав любого бинарника на Mac можно прямо в терминале командой file (например, file /bin/ls) — она покажет перечень архитектур внутри. Команда lipo -info даёт ту же информацию, а lipo также умеет извлекать или удалять отдельные архитектуры из файла.

Как проходит запуск Mach-O приложения

Последовательность от запуска до работающего процесса примерно такая:

  1. Система читает заголовок и убеждается, что формат распознан, а архитектура подходит текущему процессору.
  2. Проверяется подпись кода: целостность файла и его происхождение. Нарушенная подпись — частая причина отказа в запуске.
  3. Загрузчик (динамический линковщик) читает команды загрузки и отображает сегменты файла в виртуальную память процесса.
  4. По списку зависимостей подгружаются динамические библиотеки; если какой-то библиотеки нет или её версия несовместима, запуск прерывается с ошибкой.
  5. Выполняется привязка символов: вызовы функций из библиотек связываются с реальными адресами.
  6. Управление передаётся точке входа, и программа начинает работу.

Отсюда становятся понятны многие практические ошибки. Сообщение о том, что файл «повреждён и не может быть открыт», часто означает не физическую порчу, а проблему с подписью или карантинным атрибутом. Ошибка вида «библиотека не найдена» — это сбой на четвёртом шаге. А «Bad CPU type in executable» означает, что подходящей архитектуры в файле нет.

Подпись кода и защита целостности

В macOS подпись — не необязательное украшение, а часть формата. Блок подписи хранится в сегменте __LINKEDIT и охватывает все страницы файла: каждая страница кода получает свой хеш, а хеши защищены общей подписью разработчика. При загрузке система может проверять страницы по мере обращения к ним.

Это даёт несколько эффектов. Во-первых, система знает, кто выпустил программу и не менялся ли файл после подписания. Во-вторых, становится возможным жёсткое разграничение прав доступа: приложение получает доступ к камере, микрофону, ключам и другим защищённым ресурсам только если подпись совпадает с той, под которой эти права были выданы. Изменённый файл теряет права и, как правило, возможность запуска.

Для проверки подписи из терминала используются команды codesign -dv (сведения о подписи) и codesign —verify —deep (проверка целостности). Для просмотра внутренних структур удобна утилита otool: например, otool -L показывает список подключённых библиотек, а otool -l — все команды загрузки. Более детальный разбор дают otool -ov и сторонние графические анализаторы.

Mach-O рядом с приложением .app

Приложение macOS визуально выглядит как единый файл, но на самом деле это папка-пакет (bundle). Исполняемый Mach-O лежит внутри, обычно в подпапке Contents/MacOS, рядом с ресурсами, настройками и метаданными. Посмотреть содержимое пакета можно через контекстное меню «Показать содержимое пакета».

Динамические библиотеки, которые поставляются вместе с приложением, тоже представляют собой Mach-O, но другого типа — они помечены как dylib, а не как исполняемый файл. Фреймворки — это, по сути, пакеты, внутри которых такая библиотека и сопутствующие ресурсы объединены вместе.

Чем Mach-O отличается от ELF и PE

Все три формата решают одну задачу, но различаются деталями, которые заметны на практике:

  • Организация. В ELF и PE разделы (sections) первичны, а сегменты собираются из них при загрузке. В Mach-O наоборот: сегмент — основная единица, а секции живут внутри него.
  • Подпись кода. В Mach-O она встроена в сам формат и активно используется системой для безопасности и управления правами. В Windows и Linux механизмы проверки целостности реализованы иначе и менее тесно связаны с форматом файла.
  • Универсальные бинарники. Fat-обёртка с несколькими архитектурами в одном файле — характерная черта платформы Apple; в других экосистемах мультиархитектурность обычно решается раздельной поставкой пакетов.
  • Связь с системой. Mach-O рассчитан на конкретную модель загрузки и защиты памяти macOS/iOS, поэтому некоторые оптимизации (например, работа с разделяемым кэшем системных библиотек) встроены глубже, чем в более универсальные форматы.

Для обычного пользователя эти различия малозаметны, но они объясняют, почему нельзя просто «переупаковать» Linux-программу в Mach-O: требуется перекомпиляция под другой формат, набор системных библиотек и ABI.

Практические сценарии: когда знание формата пригождается

Проверка, под какую архитектуру собрана программа

Перед покупкой или установкой инструмента, распространяемого вручную, полезно убедиться, что в бинарнике есть поддержка вашего процессора. Команда file путь/к/файлу покажет это за секунду. Отсутствие нужной архитектуры означает либо ошибку «Bad CPU type», либо необходимость Rosetta (если речь о запуске x86_64-кода на Apple Silicon).

Диагностика проблем с зависимостями

Если программа падает при старте с жалобой на отсутствующую библиотеку, otool -L покажет полный список ожидаемых зависимостей и пути, по которым они ищутся. Часто проблема в том, что библиотека установлена в нестандартное место, либо её версия несовместима с тем, чего требует бинарник.

Проверка подозрительных файлов

Файл, который «должен быть приложением», но не открывается, легко проверить: file покажет его настоящий тип, а codesign — есть ли подпись и кем она выдана. Отсутствие подписи у программы из сомнительного источника — веский повод насторожиться.

Разработка и сборка

Разработчикам формат важен при кросс-компиляции, сборке универсальных бинарников (компилятор Apple умеет создавать fat-файлы сразу) и при работе с инструментами вроде статических анализаторов, которым нужно понимать структуру объекта.

Типичные заблуждения

  • «Mach-O — это только EXE-аналог». Нет: в этом формате существуют и библиотеки, и объектные файлы, и даже дампы ядра.
  • «Переименование файла изменит его тип». Тип определяется содержимым, прежде всего магическим числом, а не расширением.
  • «Универсальный бинарник работает быстрее». Он лишь содержит несколько вариантов; скорость определяет тот образ, который выбран под ваш процессор.
  • «Подпись нужна только для App Store». Подпись влияет на запуск, права доступа и доверие системы независимо от канала распространения.

Что делать дальше

Если вы хотите разобраться с Mach-O на практике, начните с безопасных наблюдений на собственной системе: выполните file для нескольких системных утилит, посмотрите otool -L у любимого приложения и codesign -dv для любого бинарника. Эти три команды дают наглядное представление об архитектурах, зависимостях и подписи без каких-либо изменений в системе.

Главный принцип, который стоит удержать в голове: Mach-O — это не просто «код в файле», а структурированный договор между программой и операционной системой. Заголовок говорит, что перед нами, команды загрузки — как это выполнить, сегменты — где код, где данные и какие у них права, а подпись подтверждает, что содержимое не менялось с момента выпуска. Понимание этой логики позволяет самостоятельно диагностировать большинство проблем запуска и осознанно оценивать любые исполняемые файлы на Mac.

PEFile.ru