MetalLibraryArchive -это продукт формата файла metallib с обратной инженерией.
Вы можете использовать MetalLibraryArchive для получения типа библиотеки, целевой платформы, металлических функций и т. Д. из файла metallib .
Извлеченная информация о металлической функции включает в себя:
metallib настроен на включение исходного кода. Доступно по адресу: https://yuao.github.io/metallibraryexplorer
Узнать больше
Исполняемая цель, называемая «Explorer», включена в пакет. «Explorer»-это приложение GUI, которое может открывать, распаковать и разбирать (с помощью файлов metallib llvm-dis ).
Примечание llvm-dis не включена, вы можете получить копию бинарника по адресу https://github.com/llvm/llvm-project/releases
Используйте меню «Дизассемблер» в приложении, чтобы найти исполняемый файл llvm-dis .

Вы также можете использовать MetalLibraryArchive в качестве библиотеки:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| Байт диапазон | Тип | Содержание |
|---|---|---|
| 0 ... 3 | Четырехчаркод | MTLB |
| 4 ... 5 | Uint16 | Целевая платформа |
| 6 ... 9 | (Uint16, uint16) | Версия файла Metallib (майор, несовершеннолетний) |
| 10 | Uint8 | Тип файла металлиба |
| 11 | Uint8 | Целевая ОС |
| 12 ... 15 | (Uint16, uint16) | Версия Target OS (Major, Minor) |
| 16 ... 23 | Uint64 | Размер файла металлиба |
| 24 ... 39 | (Uint64, uint64) | Смещение и размер списка функций |
| 40 ... 55 | (Uint64, uint64) | Смещение и размер общественного раздела метаданных |
| 56 ... 71 | (Uint64, uint64) | Смещение и размер секции частных метаданных |
| 72 ... 87 | (Uint64, uint64) | Смещение и размер секции биткода |
| Целевая платформа | Ценить |
|---|---|
| macOS | 0x8001 (0x01,0x80) |
| ios | 0x0001 (0x01,0x00) |
| тип металлиба | Ценить |
|---|---|
| Исполняемый файл | 0x00 |
| Основное изображение | 0x01 |
| Динамика | 0x02 |
| Символ -компаньон | 0x03 |
| Целевая ОС | Ценить |
|---|---|
| Неизвестный | 0x00 |
| macOS | 0x81 |
| ios | 0x82 |
| TVOS | 0x83 |
| ВОЗДА | 0x84 |
| Bridgeos (вероятно) | 0x85 |
| Маккатализатор | 0x86 |
| симулятор iOS | 0x87 |
| TVOS Simulator | 0x88 |
| Смотритель WatchOS | 0x89 |
| Байт диапазон | Тип | Содержание |
|---|---|---|
| 0 ... 3 | Uint32 | Количество входа (количество функций) |
| 4 ... | Теги группы | Каждая группа тегов содержит некоторую информацию о металлической функции |
Количество групп тегов равно количеству функций.
| Байт диапазон | Тип | Содержание |
|---|---|---|
| 0 ... 3 | Uint32 | Размер группы тегов |
| 4 ... | Теги |
| Байт диапазон | Тип | Содержание |
|---|---|---|
| 0 ... 3 | Четырехчаркод | Имя тега |
| 4 ... 5 | Uint16 | Размер тега |
| 6 ... | Байты | Содержание тега |
| Имя | Тип данных контента | Содержание |
|---|---|---|
| ИМЯ | NULL-конечная строка C-стиля | Имя функции |
| MDSZ | Uint64 | Размер биткода |
| ТИП | Uint8 | Тип функции |
| Хэш | SHA256 DIGEST | Хэш данных биткода (SHA256) |
| Выйти | (Uint64, uint64, uint64) | Заседания информации об этой функции в разделе общественных метаданных, разделе «Частные метаданные» и раздел биткода |
| Соус | Uint64 | Смещение архива исходного кода функции в разделе встроенного исходного кода |
| Версии | (Uint16, uint16, uint16, uint16) | Биткод и языковые версии (Air.major, Air.minor, Language.major, Language.minor) |
| Милый | Uint8 | Металлический тип render_target_array_index (для многоуровневого рендеринга) |
| Тесс | Uint8 | Тип патча и количество контрольных точек на патч (для функции вершины пост-тсекселя) |
| Конечный | Конец группы тегов |
| Тип функции | Ценить | Примечание |
|---|---|---|
| Вершина | 0x00 | |
| Фрагмент | 0x01 | |
| Ядро | 0x02 | |
| Неквалифицированный | 0x03 | Функции в металлической динамической библиотеке |
| Видимый | 0x04 | Функции с [[visible]] или [[stitchable]] атрибутов |
| Внешний | 0x05 | Внешние функции выполнены с опцией -fcikernel |
| Пересечение | 0x06 |
Содержание тега TESS :
// Patch types:
// - triangle: 1
// - quad: 2
let content : UInt8 = controlPointCount << 2 | patchTypeСодержит информацию о константах функции, патчах с тесселяциями, типами возврата и т. Д.
Теги: CNST , VATT , VATY , RETR , ARGR и т. Д.
Содержит пути к файлам Shayer Source (TAG DEBI ) и .air (тег DEPF ).
Существует только если FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| Байт диапазон | Тип | Содержание |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | Теги | Теги расширения заголовка |
| Имя | Тип | Содержание |
|---|---|---|
| Hdyn | (Uint64, uint64) | Смещение и размер секции динамического заголовка |
| Vlst | (Uint64, uint64) | Смещение и размер экспортированного списка переменных |
| ILST | (Uint64, uint64) | Смещение и размер импортированного списка символов |
| HSRD/HSRC | (Uint64, uint64) | Смещение и размер встроенного раздела исходного кода |
| Uuid | Uuid | Uuid из металлической библиотеки. |
| Конечный | Конец расширения заголовка |
| Имя | Тип данных контента | Содержание |
|---|---|---|
| ИМЯ | NULL-конечная строка C-стиля | Установить имя библиотеки |
| Dynl | NULL-конечная строка C-стиля | Связанная динамическая библиотека |
Список переменных и импортированный список символов имеют структуры, похожие на структуру списка функций.
Существует только в том случае, если процесс сборки metallib настроен на включение исходного кода.
| Байт диапазон | Тип | Содержание |
|---|---|---|
| 0 ... 1 | Uint16 | Количество предметов в этом разделе |
| 2 ... n | NULL-конечная строка C-стиля | Параметры ссылки файла metallib |
| n ... m | NULL-конечная строка C-стиля | Рабочий каталог |
| м ... | TAG GROUP | SARC TAG |
Примечание «Рабочий каталог» существует только в HSRD .
ПРИМЕЧАНИЕ SARC TAG использует размер контента 4-битов ( UInt32 ).
Содержание тега SARC :
| Байт диапазон | Тип | Содержание |
|---|---|---|
| 0 ... n | NULL-конечная строка C-стиля | Идентификатор архива исходного кода |
| n ... | Bzh | BZIP2 сжатый архив исходного кода |
| Ценить | Тип | Ценить | Тип |
|---|---|---|---|
| 0x00 | Никто | 0x01 | Структура |
| 0x02 | Множество | 0x03 | Плавать |
| 0x04 | Float2 | 0x05 | Float3 |
| 0x06 | Float4 | 0x07 | Float2x2 |
| 0x08 | Float2x3 | 0x09 | Float2x4 |
| 0x0a | Float3x2 | 0x0b | Float3x3 |
| 0x0c | Float3x4 | 0x0d | Float4x2 |
| 0x0e | Float4x3 | 0x0f | Float4x4 |
| 0x10 | Половина | 0x11 | Половина2 |
| 0x12 | Половина3 | 0x13 | Половина 4 |
| 0x14 | Половина2x2 | 0x15 | Половина2x3 |
| 0x16 | Пол2x4 | 0x17 | Половина3x2 |
| 0x18 | Половина3x3 | 0x19 | Половина3x4 |
| 0x1a | Половина 4x2 | 0x1b | Половина 4x3 |
| 0x1c | Половина 4x4 | 0x1d | Инт |
| 0x1e | Int2 | 0x1f | Int3 |
| 0x20 | Int4 | 0x21 | Uint |
| 0x22 | Uint2 | 0x23 | Uint3 |
| 0x24 | Uint4 | 0x25 | Короткий |
| 0x26 | Short2 | 0x27 | Short3 |
| 0x28 | Short4 | 0x29 | Ushort |
| 0x2a | USHORT2 | 0x2b | USHORT3 |
| 0x2c | USHORT4 | 0x2d | Девчонка |
| 0x2e | Чар2 | 0x2f | Чар3 |
| 0x30 | Чар4 | 0x31 | UCHAR |
| 0x32 | UCHAR2 | 0x33 | UCHAR3 |
| 0x34 | UCHAR4 | 0x35 | Буль |
| 0x36 | Bool2 | 0x37 | Bool3 |
| 0x38 | Bool4 | 0x3a | Текстура |
| 0x3b | Пробоотборник | 0x3c | Указатель |
| 0x3e | R8Unorm | 0x3f | R8snorm |
| 0x40 | R16Unorm | 0x41 | R16Snorm |
| 0x42 | RG8Unorm | 0x43 | Rg8snorm |
| 0x44 | RG16Unorm | 0x45 | RG16Snorm |
| 0x46 | RGBA8Unorm | 0x47 | Rgba8unorm_srgb |
| 0x48 | Rgba8snorm | 0x49 | RGBA16Unorm |
| 0x4a | RGBA16SNORM | 0x4b | RGB10A2Unorm |
| 0x4c | RG11b10float | 0x4d | Rgb9e5float |
| 0x4e | Renderpipeline | 0x4f | ComputePipeline |
| 0x50 | Компания EdiRectCommandbuffer | 0x51 | Длинный |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Улонг |
| 0x56 | Ulong2 | 0x57 | Ulong3 |
| 0x58 | Ulong4 | 0x59 | Двойной |
| 0x5a | Двойной 2 | 0x5b | Двойной 3 |
| 0x5c | Двойной 4 | 0x5d | Float8 |
| 0x5e | Float16 | 0x5f | Половина 8 |
| 0x60 | Половина16 | 0x61 | Int8 |
| 0x62 | Int16 | 0x63 | Uint8 |
| 0x64 | Uint16 | 0x65 | Short8 |
| 0x66 | Short16 | 0x67 | USHORT8 |
| 0x68 | USHORT16 | 0x69 | Чар8 |
| 0x6a | Char16 | 0x6b | UCHAR8 |
| 0x6c | UCHAR16 | 0x6d | Long8 |
| 0x6e | Long16 | 0x6f | Ulong8 |
| 0x70 | Ulong16 | 0x71 | Double8 |
| 0x72 | Double16 | 0x73 | VisibleFunctionTable |
| 0x74 | Recsectionfunctiontable | 0x75 | PrimitiveAccelerationStructure |
| 0x76 | Инстакселяция | 0x77 | Bool8 |
| 0x78 | Bool16 |
Если вы думаете, что есть ошибка, пожалуйста, откройте проблему. Вы также можете открыть запрос на вытяжку с включенным тестом на сбой.
Этот проект не начался бы без исследований Zhuowei, который выявил основной двоичный макет файла metallib , списка функций, а также раздел биткодов. Спасибо, @zhuowei!
Я попытался продолжить исследование, чтобы получить полную структуру файла metallib , но было слишком трудно двигаться вперед, основываясь только на догадках. Поэтому я обратил свое внимание на Metal.framework metallib К счастью, это не слишком сложно после того, как перетаскивать Metal.framework/Metal в Hopper Disassassembler.
Metal.framework использует MTLLibraryDataWithArchive::parseArchiveSync(...) для загрузки файлов metallib . В сборке MTLLibraryDataWithArchive есть много информации. Например:
Файл начинается с 0x424c544d (MTLB); Размер файла записывается при смещении 0x10 .
int __ZN25MTLLibraryDataWithArchive16parseArchiveSyncEPP7NSErrorb ( void * * arg0, bool arg1) {
r12 = rdx;
r14 = arg1;
r13 = arg0;
(*(*arg0 + 0xb8 ))(arg0, 0x0 ); // LibraryWithFile::setPosition(...)
r15 = r13 + 0x78 ;
rbx = (*(*r13 + 0xc0 ))(r13, r15, 0x58 ); // LibraryWithFile::readBytes(...)
rax = *r13;
rax = (*(rax + 0xc8 ))(r13); // LibraryWithFile::getFileSize(...)
// 0x424c544d - MTLB
// File size field offset: 0x88 - 0x78 = 0x10
if (((rbx != 0x58 ) || (*( int32_t *)(r13 + 0x78 ) != 0x424c544d )) || (*(r13 + 0x88 ) != rax)) goto loc_6a65b;
...
loc_6a65b:
if (r14 == 0x0 ) goto loc_6a6c5;
loc_6a660:
rdx = @ " Invalid library file " ;
...
} Значение Int16 при смещении 0x4 связано с целевой платформой.
loc_6a610:
// 0x7c - 0x78 = 0x4
rax = *( int16_t *)(r13 + 0x7c ) & 0xffff ;
if ((rax >= 0x0 ) || (r12 == 0x0 )) goto loc_6a6ea;
loc_6a627:
if (r14 == 0x0 ) goto loc_6a6c5;
loc_6a630:
rdx = @ " This library format is not supported on this platform (or was built with an old version of the tools) " ;
goto loc_6a689;Существует «раздел расширения заголовка», который содержит информацию о «разделе динамического заголовка», «Список импортных символов» и «Список переменных»:
if (MTLLibraryDataWithArchive::parseHeaderExtension(r13, r13 + 0x100 , r14) != 0x0 ) {
if ( MTLLibraryDataWithArchive::parseDynamicHeaderSection (r13) != 0x0 ) {
if ( MTLLibraryDataWithArchive::parseImportedSymbolListSection (r13) != 0x0 ) {
rax = MTLLibraryDataWithArchive::parseVariableListSection (r13);
} else {
rax = 0x0 ;
}
} else {
rax = 0x0 ;
}
} else {
rax = 0x0 ;
}Биткод проверяется с использованием SHA256.
int ____ZN25MTLLibraryDataWithArchive15validateBitCodeEmmPK6NSDataRK12MTLUINT256_t_block_invoke ( int arg0) {
...
CC_SHA256_Init (&var_B0);
CC_SHA256_Update (&var_B0, r14, *( int32_t *)(r15 + 0x38 ));
CC_SHA256_Final (&var_48, &var_B0);
...
}Много кодов Fourcc:
// 0x454e4454 - ENDT
loc_6a8bc:
if (rax == 0x454e4454 ) goto loc_6a871;
...
// 0x54595045 - TYPE
loc_6a984:
if (rax == 0x54595045 ) goto loc_6a9dc;
...
// 0x44594e4c - DYNL
loc_6ae5b:
if (rax != 0x44594e4c ) goto loc_6b002;
...
// 0x56455253 - VERS
loc_6b731:
if (rax == 0x56455253 ) goto loc_6b81c; После некоторого копания я смог получить обзор структуры файла metallib :
Файл имеет заголовок 88 байтов, который содержит версию файла, целевую платформу, тип библиотеки, индексы раздела и т. Д.
В заголовке файла записано 4 секции:
Список функций
Общественные метаданные
Частные метаданные
Модули биткода
Каждый раздел записывается с смещением и размером. Это означает, что разделы могут быть несвязанными, что позволяет Apple вводить новые разделы между ними, не нарушая совместимости. И Apple сделала это именно для раздела «Расширение заголовка» - она находится между списком функций и разделом общественных метаданных.
Большинство разделов (кроме секции биткода) напоминают структуру, основанную на «теге»:
FourCharCode используется в качестве имени/типа тега.
Значение UInt16 (в большинстве случаев) размера следует за именем тега.
Источник -тег данных архива SARC Неудивительно, что значение UInt32 для своего размера - архив источника может легко превышать 65 КБ.
Теги сгруппированы:
Каждая группа представляет набор свойств элемента.
Группа тегов заканчивается ENDT тегом.
Далее мне нужно выяснить, какую информацию содержит каждый тег/поле. Это может быть трудно получить от сборки Metal.framework потому что:
Некоторые поля могут быть разработаны исключительно для инструментов или отладки, поэтому MTLLibraryDataWithArchive может просто игнорировать их.
Сборка зависит от платформы. Например, версия iOS MTLLibraryDataWithArchive может только проверить, создан ли metallib для iOS, и не может сказать, построена ли библиотека для macOS.
Некоторые поля просто сложно проанализировать и следить. Примеры:
В OFFT функции есть 3 смещения, куда они указывают? И как они наконец используются?
Каковы возможные значения типа функции? Что означает каждое значение?
Кажется, что самый быстрый способ получить эту информацию - это эксперименты.
Я начал с ручной компиляции metal файлов с различными шейдерами, параметрами и SDK, а затем осматривал каждое поле, которое меня интересовало. Мой рабочий стол был быстро затоплен файлами metallib и шестигранными окнами, но я не нашел много полезной информации. Мне нужно что -то, что может автоматически построить metallib , и представляет мне только поле, которое меня интересует.
Я придумал «Угадание с тестированием»:
Напишите анализатор metallib на основе обзора бинарной структуры под рукой.
В анализаторе зарегистрируйте значение поля/тега (или некоторых связанных полей), которое в настоящее время неизвестно.
Создайте тесты, которые производят файлы metallib с использованием различных видов шейдеров и параметров компиляции, которые могут повлиять на значение поля, и используйте анализатор для анализа данных файлов.
Запустите тесты и проанализируйте журнал, чтобы выдвинуть гипотезы.
Обновите анализатор на основе гипотез.
Запустите тесты еще раз, чтобы проверить.
После нескольких раундов я смог получить таблицу типа функции, таблицу целевой ОС и значение 3 смещений в OFFT .
Я также нашел несколько интересных вещей в этом процессе:
Metal не поддерживает WatchOS, однако можно построить metallib -таргетинг WatchOS. И Apple включает в себя некоторые metallib в SDK WatchOS. (eg Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/watchOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreImage.framework/ci_filters.metallib .)
Пустые metallib , нацеленные на старые версии iOS, ошибочно отмечены как нацеливание на macOS.
Я не могу построить metallib с целевым значением ОС 0x85 . Сначала я подумал, что это может быть зарезервировано для скрытой реальности, но позже узнал, что это более вероятно для Бриджса.
10 апреля 2022 года
Теги, такие как LAYR , VATY , CNST и т. Д., Содержит значения UInt8 типов металлических данных. Соответствующее описание для каждого значения типа данных может быть получено с помощью частного класса в Metal.FrameWork - mtltypeinternal
id value = [[ NSClassFromString ( @" MTLTypeInternal " ) alloc ] initWithDataType: 0x06 ];
NSLog ( @" %@ " , value.description); // MTLDataTypeFloat4Я создал инструмент командной строки для создания таблицы типа данных металла.
cd Utilities
swift run metal-data-type-tools gen-markdown --columns 2 # generate a markdown table
swift run metal-data-type-tools gen-swift # generate a Swift enum for Metal data types.31 марта 2022 года
air-lld ( Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/ios/bin/air-lld ) также предоставляет много информации о том, как строится файл metallib . Некоторые имена разделов и описания обновлены.
int __ZN4llvm3air20MetalLibObjectWriter5writeEv () {
r14 = rdi;
rax = llvm::air::MetalLibObjectWriter::writeHeader ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035135:
rax = llvm::air::MetalLibObjectWriter::writeFunctionList ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035141:
rax = llvm::air::MetalLibObjectWriter::writeHeaderExtension ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_10003514d:
rax = llvm::air::MetalLibObjectWriter::writePublicMetadata ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035159:
rax = llvm::air::MetalLibObjectWriter::writePrivateMetadata ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035165:
rax = llvm::air::MetalLibObjectWriter::writeModuleList ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035171:
rax = llvm::air::MetalLibObjectWriter::writeSources ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_10003517d:
rax = llvm::air::MetalLibObjectWriter::writeDynamicHeader ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035189:
rax = llvm::air::MetalLibObjectWriter::writeVariableList ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_100035195:
rax = llvm::air::MetalLibObjectWriter::writeImportedSymbolList ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_1000351a1:
rax = llvm::air::MetalLibObjectWriter::computeUUID ();
if (rax != 0x0 ) goto loc_1000351b9;
loc_1000351ad:
rax = llvm::air::MetalLibObjectWriter::backpatchAllLocations ();
if (rax == 0x0 ) goto loc_1000351c2;
loc_1000351b9:
rbx = rax;
goto loc_1000351bb;
loc_1000351bb:
rax = rbx;
return rax;
loc_1000351c2:
rbx = 0x0 ;
std::__1::system_category ();
goto loc_1000351bb;
}