MetalLibraryArchive adalah produk dari format file metallib Apple yang menyinari balik.
Anda dapat menggunakan MetalLibraryArchive untuk mendapatkan jenis perpustakaan, platform target, fungsi logam, dll., Dari file metallib .
Informasi yang diekstraksi dari fungsi logam meliputi:
metallib dikonfigurasi untuk memasukkan kode sumber. Tersedia di: https://yuao.github.io/metallibraryexplorer
Pelajari lebih lanjut
Target yang dapat dieksekusi yang disebut "Explorer" termasuk dalam paket. "Explorer" adalah aplikasi GUI yang dapat membuka, membongkar dan membongkar (dengan bantuan file metallib llvm-dis ).
Catatan llvm-dis tidak termasuk, Anda bisa mendapatkan salinan biner di https://github.com/llvm/llvm-project/releases
Gunakan menu "Disassembler" di aplikasi untuk menemukan file yang dapat dieksekusi llvm-dis .

Anda juga dapat menggunakan MetalLibraryArchive sebagai perpustakaan:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| Rentang byte | Jenis | Isi |
|---|---|---|
| 0 ... 3 | Fourcharcode | Mtlb |
| 4 ... 5 | Uint16 | Platform target |
| 6 ... 9 | (Uint16, uint16) | Versi file Metallib (Mayor, Minor) |
| 10 | Uint8 | Jenis file metallib |
| 11 | Uint8 | OS target |
| 12 ... 15 | (Uint16, uint16) | Versi OS target (Mayor, Minor) |
| 16 ... 23 | Uint64 | Ukuran file metallib |
| 24 ... 39 | (Uint64, uint64) | Offset dan ukuran daftar fungsi |
| 40 ... 55 | (Uint64, uint64) | Offset dan ukuran bagian metadata publik |
| 56 ... 71 | (Uint64, uint64) | Offset dan ukuran bagian metadata pribadi |
| 72 ... 87 | (Uint64, uint64) | Offset dan ukuran bagian kode bitco |
| Platform target | Nilai |
|---|---|
| MacOS | 0x8001 (0x01.0x80) |
| iOS | 0x0001 (0x01.0x00) |
| Jenis Metallib | Nilai |
|---|---|
| Dapat dieksekusi | 0x00 |
| Gambar inti | 0x01 |
| Dinamis | 0x02 |
| Simbol Teman | 0x03 |
| OS target | Nilai |
|---|---|
| Tidak dikenal | 0x00 |
| MacOS | 0x81 |
| iOS | 0x82 |
| TVOS | 0x83 |
| Watchos | 0x84 |
| Bridgeos (mungkin) | 0x85 |
| Maccatalyst | 0x86 |
| simulator iOS | 0x87 |
| Simulator TVOS | 0x88 |
| simulator watchos | 0x89 |
| Rentang byte | Jenis | Isi |
|---|---|---|
| 0 ... 3 | Uint32 | Hitungan masuk (jumlah fungsi) |
| 4 ... | Grup tag | Setiap grup tag menyimpan beberapa informasi tentang fungsi logam |
Jumlah grup tag sama dengan jumlah fungsi.
| Rentang byte | Jenis | Isi |
|---|---|---|
| 0 ... 3 | Uint32 | Ukuran grup tag |
| 4 ... | Tag |
| Rentang byte | Jenis | Isi |
|---|---|---|
| 0 ... 3 | Fourcharcode | Nama tag |
| 4 ... 5 | Uint16 | Ukuran tag |
| 6 ... | Byte | Konten tag |
| Nama | Tipe data konten | Isi |
|---|---|---|
| NAMA | String gaya-C yang diakhiri nol | Nama fungsinya |
| MDSZ | Uint64 | Ukuran bitcode |
| JENIS | Uint8 | Jenis fungsi |
| Hash | SHA256 Digest | Hash dari data bitcode (SHA256) |
| Offt | (Uint64, uint64, uint64) | Offset informasi tentang fungsi ini di bagian metadata publik, bagian metadata pribadi, dan bagian kode bitcode |
| Soff | Uint64 | Offset arsip kode sumber fungsi di bagian kode sumber tertanam |
| Vers | (UInt16, UInt16, UInt16, UInt16) | Versi Bitcode dan Bahasa (Air.Major, Air.Minor, Language.Major, Language.Minor) |
| Layr | Uint8 | Jenis logam dari render_target_array_index (untuk rendering berlapis) |
| Tess | Uint8 | Jenis tambalan dan jumlah titik kontrol per-patch (untuk fungsi verteks pasca-tessellation) |
| Endt | Akhir grup tag |
| Tipe fungsi | Nilai | Catatan |
|---|---|---|
| Puncak | 0x00 | |
| Fragmen | 0x01 | |
| Inti | 0x02 | |
| Tidak memenuhi syarat | 0x03 | Fungsi di Perpustakaan Dinamis Logam |
| Bisa dilihat | 0x04 | Fungsi dengan Atribut [[visible]] atau [[stitchable]] |
| Extern | 0x05 | Fungsi luar memenuhi opsi -fcikernel |
| Persimpangan | 0x06 |
Konten tag TESS :
// Patch types:
// - triangle: 1
// - quad: 2
let content : UInt8 = controlPointCount << 2 | patchTypeBerisi informasi tentang konstanta fungsi, tambalan tessellation, jenis pengembalian, dll.
Tag: CNST , VATT , VATY , RETR , ARGR , dll.
Berisi jalur ke file sumber shader (tag DEBI ) dan .air (tag DEPF ).
Hanya ada jika FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| Rentang byte | Jenis | Isi |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | Tag | Tag ekstensi header |
| Nama | Jenis | Isi |
|---|---|---|
| Hdyn | (Uint64, uint64) | Offset dan ukuran bagian header dinamis |
| Vlst | (Uint64, uint64) | Offset dan ukuran daftar variabel yang diekspor |
| Ilst | (Uint64, uint64) | Offset dan ukuran daftar simbol yang diimpor |
| HSRD/HSRC | (Uint64, uint64) | Offset dan ukuran bagian kode sumber tertanam |
| UUID | UUID | UUID dari Perpustakaan Logam. |
| Endt | Akhir ekstensi header |
| Nama | Tipe data konten | Isi |
|---|---|---|
| NAMA | String gaya-C yang diakhiri nol | Instal Nama Perpustakaan |
| Dynl | String gaya-C yang diakhiri nol | Perpustakaan Dinamis Tertaut |
Daftar variabel dan daftar simbol yang diimpor memiliki struktur yang mirip dengan daftar fungsi.
Hanya ada jika proses pembuatan metallib dikonfigurasi untuk memasukkan kode sumber.
| Rentang byte | Jenis | Isi |
|---|---|---|
| 0 ... 1 | Uint16 | Jumlah item di bagian ini |
| 2 ... n | String gaya-C yang diakhiri nol | Opsi tautan file metallib |
| n ... m | String gaya-C yang diakhiri nol | Direktori Bekerja |
| M... | Grup tag | Tag SARC |
Catatan "Direktori Kerja" hanya ada di HSRD .
Catatan Tag SARC menggunakan ukuran konten 4-byte ( UInt32 ).
Konten tag SARC :
| Rentang byte | Jenis | Isi |
|---|---|---|
| 0 ... n | String gaya-C yang diakhiri nol | ID dari arsip kode sumber |
| N... | BZH | Arsip kode sumber terkompresi bzip2 |
| Nilai | Jenis | Nilai | Jenis |
|---|---|---|---|
| 0x00 | Tidak ada | 0x01 | Struct |
| 0x02 | Array | 0x03 | Mengambang |
| 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 | Setengah | 0x11 | Setengah2 |
| 0x12 | Setengah3 | 0x13 | Half4 |
| 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 | Pendek |
| 0x26 | Short2 | 0x27 | Short3 |
| 0x28 | Short4 | 0x29 | Ushort |
| 0x2a | Ushort2 | 0x2b | Ushort3 |
| 0x2c | Ushort4 | 0x2d | Arang |
| 0x2e | Char2 | 0x2f | Char3 |
| 0x30 | Char4 | 0x31 | Uchar |
| 0x32 | Uchar2 | 0x33 | Uchar3 |
| 0x34 | Uchar4 | 0x35 | Bool |
| 0x36 | Bool2 | 0x37 | Bool3 |
| 0x38 | Bool4 | 0x3a | Tekstur |
| 0x3b | Sampler | 0x3c | Penunjuk |
| 0x3e | R8unorm | 0x3f | R8Snorm |
| 0x40 | R16Unorm | 0x41 | R16Snorm |
| 0x42 | RG8Unorm | 0x43 | RG8Snorm |
| 0x44 | RG16Unorm | 0x45 | RG16Snorm |
| 0x46 | Rgba8unor | 0x47 | Rgba8unorm_srgb |
| 0x48 | RGBA8Snorm | 0x49 | RGBA16Unorm |
| 0x4a | RGBA16Snorm | 0x4b | RGB10A2Unorm |
| 0x4c | RG11b10Float | 0x4d | RGB9E5Float |
| 0x4e | Renderpipeline | 0x4f | ComputePipeline |
| 0x50 | IndirectCommandBuffer | 0x51 | Panjang |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Ulong |
| 0x56 | Ulong2 | 0x57 | Ulong3 |
| 0x58 | Ulong4 | 0x59 | Dobel |
| 0x5a | Double2 | 0x5b | Double3 |
| 0x5c | Double4 | 0x5d | Float8 |
| 0x5e | Float16 | 0x5f | Setengah8 |
| 0x60 | Setengah16 | 0x61 | Int8 |
| 0x62 | Int16 | 0x63 | Uint8 |
| 0x64 | Uint16 | 0x65 | Short8 |
| 0x66 | Short16 | 0x67 | Ushort8 |
| 0x68 | Ushort16 | 0x69 | Char8 |
| 0x6a | Char16 | 0x6b | Uchar8 |
| 0x6c | Uchar16 | 0x6d | Long8 |
| 0x6e | Long16 | 0x6f | Ulong8 |
| 0x70 | Ulong16 | 0x71 | Double8 |
| 0x72 | Double16 | 0x73 | VisibleFunctiontable |
| 0x74 | Persimpangan yang dapat digunakan | 0x75 | PrimitiveAccelationstructure |
| 0x76 | Instanceaccelerationstructure | 0x77 | Bool8 |
| 0x78 | Bool16 |
Jika Anda pikir ada kesalahan, buka masalah. Anda juga dapat memilih untuk membuka permintaan tarik dengan tes kegagalan yang disertakan.
Proyek ini tidak akan dimulai tanpa penelitian Zhuowei yang mengungkapkan tata letak biner dasar dari file metallib , daftar fungsi serta bagian bitcode. Terima kasih, @zhuowei!
Saya mencoba melanjutkan penelitian untuk mendapatkan struktur lengkap file metallib , tetapi merasa terlalu sulit untuk bergerak maju berdasarkan tebakan saja. Jadi saya mengalihkan perhatian saya ke Metal.framework berharap untuk mengetahui bagaimana kerangka kerja memuat file metallib . Untungnya, tidak terlalu sulit setelah menyeret Metal.framework/Metal ke Hopper Disassembler.
Metal.framework menggunakan MTLLibraryDataWithArchive::parseArchiveSync(...) untuk memuat file metallib . Ada banyak informasi yang tersembunyi di perakitan MTLLibraryDataWithArchive . Misalnya:
File dimulai dengan 0x424c544d (MTLB); Ukuran file direkam di Offset 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 " ;
...
} Nilai Int16 di Offset 0x4 terkait dengan platform target.
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;Ada "bagian ekstensi header" yang berisi informasi tentang "bagian header dinamis", "daftar simbol yang diimpor" dan "daftar variabel"::
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 divalidasi menggunakan 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);
...
}Banyak kode 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; Setelah beberapa penggalian di sekitar saya bisa mendapatkan gambaran tentang struktur file metallib :
File ini memiliki header 88 byte yang berisi versi file, platform target, jenis perpustakaan, indeks bagian, dll.
Ada 4 bagian yang direkam di header file:
Daftar Fungsi
Metadata publik
Metadata pribadi
Modul bitcode
Setiap bagian dicatat dengan offset dan ukuran. Ini berarti bagian bisa tidak bersebelahan, yang memungkinkan Apple untuk memperkenalkan bagian baru di antaranya tanpa melanggar kompatibilitas. Dan Apple melakukan itu dengan tepat untuk bagian "ekstensi header" - itu terletak di antara daftar fungsi dan bagian metadata publik.
Sebagian besar bagian (kecuali bagian bitcode) menyerupai struktur berbasis "tag":
FourCharcode digunakan sebagai nama/tipe tag.
Nilai ukuran UInt16 (dalam kebanyakan kasus) mengikuti nama tag.
Tag data arsip sumber SARC tidak mengejutkan menggunakan nilai UInt32 untuk ukurannya - arsip sumber dapat dengan mudah melebihi 65kb.
Tag dikelompokkan:
Setiap grup mewakili satu set properti suatu item.
Grup tag diakhiri dengan tag ENDT .
Selanjutnya, saya perlu mencari tahu informasi apa yang dimiliki setiap tag/bidang. Ini bisa sulit untuk mendapatkan dari perakitan Metal.framework karena:
Beberapa bidang mungkin dirancang murni untuk alat atau debugging, jadi MTLLibraryDataWithArchive dapat mengabaikannya.
Majelis tergantung pada platform. Misalnya, versi iOS dari MTLLibraryDataWithArchive hanya dapat memeriksa apakah metallib dibangun untuk iOS dan tidak dapat mengetahui apakah perpustakaan dibangun untuk macOS.
Beberapa bidang hanya sulit dianalisis dan diikuti. Contoh:
Ada 3 offset di tag OFFT dari fungsi, di mana mereka menunjuk? Dan bagaimana mereka akhirnya digunakan?
Apa nilai yang mungkin dari tipe fungsi? Apa arti masing -masing nilai?
Tampaknya cara tercepat untuk mendapatkan informasi ini adalah melalui eksperimen.
Saya mulai dengan secara manual menyusun file metal dengan shader, opsi, dan SDK yang berbeda, kemudian memeriksa setiap bidang yang saya minati. Desktop saya dengan cepat dibanjiri dengan file metallib dan jendela hexfiend, tetapi saya tidak menemukan banyak informasi yang berguna. Saya membutuhkan sesuatu yang secara otomatis dapat membangun metallib dan hanya memberi saya bidang yang saya minati.
Saya datang dengan "Test Driven Dugaan":
Tulis parser metallib berdasarkan ikhtisar struktur biner yang ada.
Di parser, log nilai bidang/tag (atau beberapa bidang terkait) yang saat ini tidak diketahui.
Buat tes yang menghasilkan file metallib menggunakan berbagai jenis shader dan kompilasi opsi yang dapat mempengaruhi nilai bidang, dan menggunakan parser untuk mengurai data file.
Jalankan tes dan analisis log untuk membuat hipotesis.
Perbarui parser berdasarkan hipotesis.
Jalankan tes lagi untuk memverifikasi.
Setelah beberapa putaran, saya bisa mendapatkan tabel tipe fungsi, tabel OS target, dan arti dari 3 offset dalam tag OFFT .
Saya juga menemukan beberapa hal yang menarik dalam proses ini:
Logam tidak mendukung Watchos, bagaimanapun, dimungkinkan untuk membangun Watchos yang menargetkan metallib . Dan Apple memang menyertakan beberapa metallib di 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 yang kosong menargetkan versi lama iOS secara keliru ditandai sebagai penargetan macOS.
Saya tidak dapat membangun metallib yang memiliki nilai OS target 0x85 . Pada awalnya saya pikir itu mungkin dicadangkan untuk realityos yang disembunyikan, tetapi kemudian menemukan itu lebih mungkin untuk Bridgeos.
10 Apr 2022
Tag seperti LAYR , VATY , CNST , dll., Berisi nilai UInt8 dari tipe data logam. Deskripsi yang sesuai untuk setiap nilai tipe data dapat diambil menggunakan kelas pribadi di Metal.Framework - MtlTypeInternal
id value = [[ NSClassFromString ( @" MTLTypeInternal " ) alloc ] initWithDataType: 0x06 ];
NSLog ( @" %@ " , value.description); // MTLDataTypeFloat4Saya membuat alat baris perintah untuk menghasilkan tabel tipe data logam.
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 Mar 2022
air-lld ( Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/ios/bin/air-lld ) juga menyediakan banyak informasi tentang bagaimana file metallib dibangun. Beberapa nama dan deskripsi bagian diperbarui.
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;
}