MetalLibraryArchive هو نتاج تنسيق ملف metallib من الهندسة العكسية.
يمكنك استخدام MetalLibraryArchive للحصول على نوع المكتبة ، والمنصة المستهدفة ، والوظائف المعدنية ، وما إلى ذلك ، من ملف metallib .
تتضمن المعلومات المستخرجة من وظيفة المعادن:
metallib لتضمين رمز المصدر. متاح على: https://yuao.github.io/metallibraryexplorer
يتعلم أكثر
يتم تضمين هدف قابل للتنفيذ يسمى "Explorer" في الحزمة. "Explorer" هو تطبيق واجهة المستخدم الرسومية يمكنه فتح ملفات metallib (بمساعدة llvm-dis ).
ملاحظة لم يتم تضمين llvm-dis ، يمكنك الحصول على نسخة من الثنائي على https://github.com/llvm/llvm-project
استخدم قائمة "disassembler" في التطبيق لتحديد موقع الملف القابل للتنفيذ llvm-dis .

يمكنك أيضًا استخدام MetalLibraryArchive كمكتبة:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| نطاق البايت | يكتب | محتوى |
|---|---|---|
| 0 ... 3 | Fourcharcode | MTLB |
| 4 ... 5 | uint16 | منصة الهدف |
| 6 ... 9 | (UINT16 ، UINT16) | نسخة من ملف metallib (التخصص ، قاصر) |
| 10 | uint8 | نوع ملف metallib |
| 11 | uint8 | OS الهدف |
| 12 ... 15 | (UINT16 ، UINT16) | نسخة من نظام التشغيل المستهدف (Major ، Minor) |
| 16 ... 23 | uint64 | حجم ملف metallib |
| 24 ... 39 | (UINT64 ، UINT64) | إزاحة وحجم قائمة الوظائف |
| 40 ... 55 | (UINT64 ، UINT64) | إزاحة وحجم قسم البيانات الوصفية العامة |
| 56 ... 71 | (UINT64 ، UINT64) | إزاحة وحجم قسم البيانات الوصفية الخاصة |
| 72 ... 87 | (UINT64 ، UINT64) | إزاحة وحجم قسم الرمز البري |
| منصة الهدف | قيمة |
|---|---|
| ماكوس | 0x8001 (0x01،0x80) |
| iOS | 0x0001 (0x01،0x00) |
| نوع ميتالب | قيمة |
|---|---|
| قابلة للتنفيذ | 0x00 |
| الصورة الأساسية | 0x01 |
| متحرك | 0x02 |
| رفيق الرمز | 0x03 |
| OS الهدف | قيمة |
|---|---|
| مجهول | 0x00 |
| ماكوس | 0x81 |
| iOS | 0x82 |
| tvos | 0x83 |
| Watchos | 0x84 |
| بريدجوس (ربما) | 0x85 |
| MacCatalyst | 0x86 |
| محاكي iOS | 0x87 |
| TVOS Simulator | 0x88 |
| Watchos Simulator | 0x89 |
| نطاق البايت | يكتب | محتوى |
|---|---|---|
| 0 ... 3 | uint32 | عدد الدخول (عدد الوظائف) |
| 4 ... | مجموعات العلامات | تحتوي كل مجموعة على بعض المعلومات حول وظيفة المعادن |
عدد مجموعات العلامات يساوي عدد الوظائف.
| نطاق البايت | يكتب | محتوى |
|---|---|---|
| 0 ... 3 | uint32 | حجم مجموعة العلامات |
| 4 ... | العلامات |
| نطاق البايت | يكتب | محتوى |
|---|---|---|
| 0 ... 3 | Fourcharcode | اسم العلامة |
| 4 ... 5 | uint16 | حجم العلامة |
| 6 ... | بايت | محتوى العلامة |
| اسم | نوع بيانات المحتوى | محتوى |
|---|---|---|
| اسم | سلسلة على طراز C المنتهية | اسم الوظيفة |
| MDSZ | uint64 | حجم الرمز البريطاني |
| يكتب | uint8 | نوع الوظيفة |
| التجزئة | SHA256 Digest | تجزئة بيانات Bitcode (SHA256) |
| Offt | (UINT64 ، UINT64 ، UINT64) | إزاحة المعلومات حول هذه الوظيفة في قسم البيانات الوصفية العامة ، قسم البيانات الوصفية الخاصة ، وقسم Bitcode |
| سوف | uint64 | إزاحة أرشيف رمز المصدر للوظيفة في قسم رمز المصدر المضمن |
| مقابل | (UINT16 ، UINT16 ، UINT16 ، UINT16) | إصدارات Bitcode and Language (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 ، إلخ.
يحتوي على مسارات إلى ملفات مصدر التظليل (علامة DEBI ) و .air (علامة DEPF ).
موجود فقط إذا كانت FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| نطاق البايت | يكتب | محتوى |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | العلامات | علامات تمديد الرأس |
| اسم | يكتب | محتوى |
|---|---|---|
| هدين | (UINT64 ، UINT64) | إزاحة وحجم قسم الرأس الديناميكي |
| vlst | (UINT64 ، UINT64) | إزاحة وحجم القائمة المتغيرة المصدرة |
| ilst | (UINT64 ، UINT64) | إزاحة وحجم قائمة الرموز المستوردة |
| HSRD/HSRC | (UINT64 ، UINT64) | إزاحة وحجم قسم رمز المصدر المدمج |
| uuid | uuid | uuid من مكتبة المعادن. |
| نهاية | نهاية تمديد الرأس |
| اسم | نوع بيانات المحتوى | محتوى |
|---|---|---|
| اسم | سلسلة على طراز C المنتهية | تثبيت اسم المكتبة |
| Dynl | سلسلة على طراز C المنتهية | مكتبة ديناميكية مرتبطة |
القائمة المتغيرة وقائمة الرموز المستوردة لها هياكل مماثلة لتلك الموجودة في قائمة الوظائف.
موجود فقط إذا تم تكوين عملية بناء metallib لتضمين رمز المصدر.
| نطاق البايت | يكتب | محتوى |
|---|---|---|
| 0 ... 1 | uint16 | عدد العناصر في هذا القسم |
| 2 ... ن | سلسلة على طراز C المنتهية | خيارات ربط ملف metallib |
| ن ... م | سلسلة على طراز C المنتهية | دليل العمل |
| م ... | مجموعة العلامات | علامة SARC |
ملاحظة "دليل العمل" موجود فقط في HSRD .
ملاحظة تستخدم علامة SARC حجم المحتوى 4-BYTES ( UInt32 ).
محتوى علامة SARC :
| نطاق البايت | يكتب | محتوى |
|---|---|---|
| 0 ... ن | سلسلة على طراز C المنتهية | معرف أرشيف رمز المصدر |
| ن ... | BZH | BZIP2 أرشيف رمز المصدر المضغوط |
| قيمة | يكتب | قيمة | يكتب |
|---|---|---|---|
| 0x00 | لا أحد | 0x01 | بنية |
| 0x02 | صفيف | 0x03 | يطفو |
| 0x04 | تعويم 2 | 0x05 | Float3 |
| 0x06 | تعويم 4 | 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 | Half2x2 | 0x15 | Half2x3 |
| 0x16 | Half2x4 | 0x17 | Half3x2 |
| 0x18 | Half3x3 | 0x19 | Half3x4 |
| 0x1a | Half4x2 | 0x1B | Half4x3 |
| 0x1c | Half4x4 | 0x1d | int |
| 0x1e | int2 | 0x1f | int3 |
| 0x20 | int4 | 0x21 | uint |
| 0x22 | uint2 | 0x23 | uint3 |
| 0x24 | uint4 | 0x25 | قصير |
| 0x26 | قصيرة 2 | 0x27 | قصيرة 3 |
| 0x28 | قصيرة 4 | 0x29 | Ushort |
| 0x2a | USHORT2 | 0x2b | Ushort3 |
| 0x2c | Ushort4 | 0x2d | شار |
| 0x2e | char2 | 0x2f | char3 |
| 0x30 | char4 | 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 | IndirectCommandbuffer | 0x51 | طويل |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Ulong |
| 0x56 | Ulong2 | 0x57 | Ulong3 |
| 0x58 | Ulong4 | 0x59 | مزدوج |
| 0x5A | Double2 | 0x5b | Double3 |
| 0x5C | Double4 | 0x5D | تعويم 8 |
| 0x5e | Float16 | 0x5F | نصف 8 |
| 0x60 | نصف 16 | 0x61 | int8 |
| 0x62 | int16 | 0x63 | uint8 |
| 0x64 | uint16 | 0x65 | قصيرة 8 |
| 0x66 | قصيرة 16 | 0x67 | Ushort8 |
| 0x68 | USHORT16 | 0x69 | char8 |
| 0x6a | char16 | 0x6b | uchar8 |
| 0x6c | uchar16 | 0x6d | Long8 |
| 0x6e | Long16 | 0x6f | Ulong8 |
| 0x70 | Ulong16 | 0x71 | مزدوج 8 |
| 0x72 | Double16 | 0x73 | VisibleFunctionTable |
| 0x74 | InterSectionFunctionTable | 0x75 | primitiveaccelerationsstructure |
| 0x76 | instanceaccelerationsstructure | 0x77 | Bool8 |
| 0x78 | Bool16 |
إذا كنت تعتقد أن هناك خطأ ، يرجى فتح مشكلة. يمكنك أيضًا اختيار فتح طلب سحب مع اختبارات الفشل المدرجة.
لن يبدأ هذا المشروع بدون بحث Zhuowei الذي كشف عن التصميم الثنائي الأساسي لملف metallib ، وقائمة الوظائف وكذلك قسم Bitcode. شكرا ، Zhuowei!
حاولت مواصلة البحث للحصول على بنية كاملة لملف metallib ، لكنني وجدت أنه من الصعب للغاية المضي قدمًا بناءً على التخمين وحده. لذا حولت انتباهي إلى Metal.framework metallib لحسن الحظ ، ليس من الصعب جدًا سحب Metal.framework/Metal .
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 ;
}يتم التحقق من صحة Bitcode باستخدام 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 أقسام مسجلة في رأس الملف:
قائمة الوظائف
البيانات الوصفية العامة
البيانات الوصفية الخاصة
وحدات Bitcode
يتم تسجيل كل قسم مع إزاحة وحجم. هذا يعني أن الأقسام يمكن أن تكون غير متجانسة ، مما يسمح لـ Apple بإدخال أقسام جديدة بينهما دون كسر التوافق. وفعلت Apple ذلك تمامًا لقسم "تمديد الرأس" - فهو يقع بين قائمة الوظائف وقسم البيانات الوصفية العامة.
تشبه معظم الأقسام (باستثناء قسم Bitcode) الهيكل القائم على "علامة":
يتم استخدام Fourcharcode كاسم/نوع العلامة.
تتبع قيمة UInt16 (في معظم الحالات) اسم العلامة.
تستخدم علامة بيانات أرشيف المصدر SARC بشكل غير مفاجئ قيمة UInt32 لحجمها - يمكن أن يتجاوز أرشيف المصدر بسهولة 65 كيلو بايت.
تم تجميع العلامات:
تمثل كل مجموعة مجموعة من خصائص عنصر ما.
تنتهي مجموعة العلامات بعلامة ENDT .
بعد ذلك ، أحتاج إلى معرفة المعلومات التي يحتفظ بها كل علامة/حقل. قد يكون من الصعب الحصول على هذا من مجموعة Metal.framework .
قد يتم تصميم بعض الحقول بحتة للأدوات أو تصحيح الأخطاء ، لذلك قد يتجاهلها MTLLibraryDataWithArchive .
التجميع يعتمد على المنصة. على سبيل المثال ، يجوز لإصدار iOS من MTLLibraryDataWithArchive التحقق من ما إذا كان metallib قد تم تصميمه لنظام التشغيل iOS ولا يمكنه معرفة ما إذا كانت المكتبة مصممة لـ MacOS.
بعض الحقول يصعب تحليلها ومتابعتها. أمثلة:
هناك 3 تعويضات في علامة OFFT للوظيفة ، إلى أين يشيرون إلى؟ وكيف يتم استخدامها أخيرًا؟
ما هي القيم المحتملة لنوع الوظيفة؟ ماذا تعني كل قيمة؟
يبدو أن أسرع طريقة للحصول على هذه المعلومات هي من خلال التجارب.
لقد بدأت بتجميع الملفات metal يدويًا بتظليلات وخيارات و SDKs مختلفة ، ثم فحص كل حقل كنت مهتمًا به. تم إغراق سطح المكتب بسرعة بملفات metallib ونوافذ Hexfiend ، لكنني لم أجد الكثير من المعلومات المفيدة. أحتاج إلى شيء يمكن أن يبني metallib تلقائيًا ويعرضني فقط الحقل الذي أهتم به.
توصلت إلى "التخمين الذي يقوده الاختبار":
اكتب محلل metallib بناءً على نظرة عامة على الهيكل الثنائي في متناول اليد.
في المحلل ، قم بتسجيل قيمة الحقل/العلامة (أو بعض الحقول ذات الصلة) غير معروفة حاليًا.
قم بإنشاء اختبارات تنتج ملفات metallib باستخدام أنواع مختلفة من التظليلات وخيارات التجميع التي قد تؤثر على قيمة الحقل ، واستخدام المحلل لتحليل بيانات الملف.
قم بتشغيل الاختبارات وتحليل السجل لجعل الفرضيات.
قم بتحديث المحلل المستند إلى الفرضيات.
إجراء الاختبارات مرة أخرى للتحقق.
بعد بضع جولات ، تمكنت من الحصول على جدول نوع الوظيفة ، وجدول OS المستهدف ، ومعنى 3 تعويضات في علامة OFFT .
لقد وجدت أيضًا بعض الأشياء المثيرة للاهتمام في هذه العملية:
لا يدعم Metal WatchOS ، ومع ذلك ، من الممكن بناء WatchOS metallib . وتشمل Apple بعض metallib s في Watchos SDK. (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 الذي يحتوي على قيمة OS المستهدفة 0x85 . في البداية اعتقدت أنه قد يكون مخصصًا للواقع المخفي ، ولكن اكتشفت لاحقًا أنه من المرجح أن يكون للجسر.
10 أبريل 2022
تحتوي العلامات مثل LAYR ، VATY ، CNST ، إلخ ، على قيم UInt8 لأنواع البيانات المعدنية. يمكن استرداد الوصف المقابل لكل قيمة نوع البيانات باستخدام فئة خاصة في المعادن.
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;
}