Что такое IL2CPP и как работает эта технология в Unity

Если вы распаковали APK-файл игры на Unity и вместо привычных DLL-сборок в папке Managed обнаружили файл libil2cpp.so и каталог il2cpp_data — значит, проект собран через IL2CPP, и привычные инструменты анализа .NET-кода здесь напрямую не сработают. Именно с этой ситуацией чаще всего сталкиваются те, кто впервые встречает термин IL2CPP: при попытке открыть игру в dnSpy или при чтении требований к сборке в документации Unity.

Эта статья объясняет, что представляет собой технология IL2CPP, зачем Unity перевела на неё большинство платформ, чем она отличается от классического рантайма Mono и что меняется для разработчиков, мододелов и исследователей кода. Материал будет полезен как начинающим разработчикам игр, так и тем, кто анализирует готовые приложения.

Что такое IL2CPP простыми словами

IL2CPP (расшифровывается как Intermediate Language to C++) — это технология компиляции, разработанная Unity, которая преобразует промежуточный код .NET (IL-код) в нативный C++-код. Полученный C++ затем компилируется стандартными платформенными инструментами в машинный код конкретного устройства: процессора смартфона, консоли или компьютера.

Проще говоря, цепочка выглядит так: вы пишете игру на C#, компилятор превращает код в IL-сборки, а IL2CPP переводит эти сборки в C++ и собирает из них нативный исполняемый файл. В результате на устройстве выполняется не интерпретируемый байт-код, а скомпилированный машинный код.

Важно понимать, что IL2CPP — это не виртуальная машина и не отдельный язык. Это компиляторная технология AOT (Ahead-of-Time), при которой вся компиляция происходит заранее, до запуска приложения, а не во время его работы.

Как работает конвейер компиляции IL2CPP

Процесс сборки проекта Unity с IL2CPP состоит из нескольких последовательных этапов. Сначала стандартный компилятор C# превращает исходники в управляемые сборки — файлы с IL-кодом, такие же, как в обычном .NET-приложении. На этом этапе ничего необычного не происходит.

Затем в работу вступает сам IL2CPP. Он анализирует IL-сборки и генерирует эквивалентный C++-код, который помещается в директорию Il2CppOutputProject внутри папки проекта. Параллельно выполняется удаление неиспользуемого кода (managed code stripping): классы и методы, до которых нельзя добраться из точек входа, вырезаются для уменьшения размера сборки.

Финальный шаг — компиляция сгенерированного C++ нативным компилятором целевой платформы. Для Android это NDK с Clang, для Windows — MSVC, для iOS — Xcode. На выходе получается нативная библиотека, например libil2cpp.so для Android.

  • 📝 Этап 1: компиляция C# в IL-сборки стандартными средствами .NET
  • 🔄 Этап 2: трансляция IL в C++ инструментом IL2CPP
  • ✂️ Этап 3: удаление неиспользуемого кода (stripping)
  • ⚙️ Этап 4: нативная компиляция C++ под целевую платформу
⚠️ Внимание: из-за агрессивного удаления «неиспользуемого» кода код, вызываемый только через рефлексию, может исчезнуть из сборки. Чтобы этого избежать, используйте файл link.xml с явным перечислением сохраняемых типов — это частая причина ошибок, которые не воспроизводятся в редакторе, но появляются в собранном приложении.

Чем IL2CPP отличается от Mono

Mono — это классический рантайм .NET с JIT-компиляцией: IL-код превращается в машинный прямо во время выполнения программы. IL2CPP работает иначе — вся компиляция завершается до запуска. Это фундаментальное различие определяет все остальные отличия.

КритерийMono (JIT)IL2CPP (AOT)
Момент компиляцииВо время выполненияДо запуска приложения
Скорость сборки проектаБыстрееЗаметно медленнее
Производительность кодаБазоваяОбычно выше за счёт оптимизаций C++
Поддержка консолей и iOSОграниченаПолная
Сложность анализа кодаЛегко декомпилируетсяТребует специальных инструментов

Практический вывод для разработчика прост: Mono удобен на этапе разработки из-за быстрой сборки и возможности отладки управляемого кода, а IL2CPP предпочтителен для релиза — из-за производительности, совместимости с платформами и лучшей защиты кода от беглого анализа.

📊 Для каких задач вы интересуетесь IL2CPP?
Разработка игр на Unity
Анализ или моддинг игр
Оптимизация производительности
Просто расширяю кругозор

Почему Unity перешла на IL2CPP

Изначально Unity использовала Mono как единственный бэкенд выполнения скриптов. Однако со временем накопилось несколько проблем, которые старый рантайм решить не мог. Главной стала поддержка платформ: многие системы, включая iOS и игровые консоли, запрещают или ограничивают JIT-компиляцию — генерировать исполняемый код в рантайме там нельзя по соображениям безопасности.

Дополнительные причины перехода:

  • 🚀 Производительность: нативный C++-код оптимизируется зрелыми компиляторами платформ
  • 🎮 Консоли: PlayStation, Xbox и Nintendo Switch требуют AOT-компиляции
  • 🔒 Защита кода: нативный бинарник сложнее декомпилировать, чем IL-сборку
  • 📱 Кроссплатформенность: одна кодовая база C++ портируется на любую платформу с C++-компилятором

Как IL2CPP влияет на анализ и моддинг игр

Здесь кроется причина, по которой запрос «что такое il2cpp» чаще всего ищут не разработчики, а мододелы. В играх, собранных на Mono, весь управляемый код хранится в DLL-файлах, которые легко открываются в dnSpy или dotPeek — можно читать и править логику почти как исходники. С IL2CPP этот путь закрыт: управляемых сборок в готовом виде нет, есть только нативная библиотека.

Тем не менее анализ возможен. Вместе с libil2cpp.so (или GameAssembly.dll на Windows) в сборке присутствует файл метаданных global-metadata.dat, содержащий имена классов, методов и полей. Инструменты вроде Il2CppDumper используют эти метаданные, чтобы восстановить структуру классов и сопоставить её с нативным кодом.

Il2CppDumper.exe libil2cpp.so global-metadata.dat output_dir

Результат работы таких утилит — не читаемый C#-код, а карта структур и адресов функций, которую дальше изучают в дизассемблерах вроде IDA Pro или Ghidra. Это существенно сложнее, чем правка IL-кода, и требует навыков реверс-инжиниринга.

⚠️ Внимание: модификация игровых файлов может нарушать лицензионное соглашение и правила конкретной игры, а в онлайн-играх — привести к блокировке аккаунта. Перед любыми экспериментами изучите условия использования продукта и применяйте полученные знания только в легальных целях, например для анализа собственных проектов.
Почему в IL2CPP-сборке нет папки Managed

При AOT-компиляции IL-сборки нужны только на этапе сборки проекта. После трансляции в C++ они не упаковываются в итоговое приложение — вместо них поставляется нативная библиотека и файл метаданных, поэтому привычная папка Managed в релизной сборке отсутствует.

Особенности разработки под IL2CPP

Переключение проекта на IL2CPP меняет рабочий процесс. Самое заметное — время сборки: генерация и компиляция C++ занимает значительно дольше, чем обычная сборка на Mono. Поэтому распространена практика использовать Mono для ежедневной разработки и тестирования, а IL2CPP — для релизных и предрелизных сборок.

Есть и технические ограничения. AOT-компиляция не поддерживает динамическую генерацию кода в рантайме: например, System.Reflection.Emit работать не будет. Обобщённые типы (generics) должны быть полностью определены на этапе компиляции, что иногда вызывает специфические ошибки при использовании сложных generic-конструкций.

☑️ Проверка проекта перед сборкой на IL2CPP

Выполнено: 0 / 5

Отладка тоже отличается: управляемый стектрейс в IL2CPP-сборке доступен, но построчная отладка C# на устройстве требует дополнительной настройки. Для диагностики крашей нативной части применяются символы, которые Unity генерирует при сборке, — их стоит сохранять для каждой релизной версии.

Частые вопросы об IL2CPP

Можно ли полностью защитить игру с помощью IL2CPP?

IL2CPP заметно усложняет анализ кода по сравнению с Mono, но не делает его невозможным. Файл метаданных global-metadata.dat содержит имена классов и методов, а нативный код поддаётся дизассемблированию. Это повышение порога входа, а не абсолютная защита.

Почему сборка с IL2CPP занимает так много времени?

К стандартной компиляции C# добавляются два тяжёлых этапа: трансляция IL в C++ и последующая нативная компиляция всех сгенерированных файлов. Объём C++-кода велик, и его оптимизация компилятором занимает существенное время, особенно в релизной конфигурации.

Работает ли IL2CPP на всех платформах Unity?

IL2CPP поддерживается на подавляющем большинстве платформ, включая мобильные ОС, консоли и десктоп. Для некоторых платформ, например iOS, это фактически единственный доступный вариант из-за запрета JIT-компиляции. Актуальный список поддерживаемых платформ стоит сверять с документацией вашей версии Unity.

Что делать, если игра на IL2CPP падает, а на Mono работает?

Наиболее вероятная причина — удаление кода при stripping: тип или метод, вызываемый через рефлексию, был вырезан из сборки. Проверьте логи на наличие MissingMethodException или похожих ошибок и добавьте нужные типы в link.xml. Также убедитесь, что проблема воспроизводится именно в релизной конфигурации, а не связана с конкретным устройством.

Нужно ли знать C++, чтобы работать с IL2CPP?

Для обычной разработки игр — нет: вы по-прежнему пишете на C#, а трансляция происходит автоматически. Знание C++ пригодится только для нативных плагинов, глубокой отладки крашей или анализа скомпилированного кода.