MetalLibraryArchive ist ein Produkt des metallib -Dateiformats von Reverse-Engineering Apple.
Sie können MetalLibraryArchive verwenden, um den Bibliothekstyp, die Zielplattform, die Metallfunktionen usw. aus einer metallib -Datei abzurufen.
Die extrahierten Informationen einer Metallfunktion umfassen:
metallib so konfiguriert ist, dass Quellcode einbezieht. Verfügbar unter: https://yuao.github.io/metallibraryexplorer
Erfahren Sie mehr
Ein ausführbares Ziel namens "Explorer" ist im Paket enthalten. "Explorer" ist eine GUI-App, die metallib Dateien (mit Hilfe von llvm-dis ).
HINWEIS llvm-dis ist nicht enthalten. Sie können eine Kopie des Binary unter https://github.com/llvm/llvm-project/releases erhalten
Verwenden Sie das Menü "Disassembler" in der App, um die ausführbare llvm-dis Datei zu finden.

Sie können auch MetalLibraryArchive als Bibliothek verwenden:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
| 0 ... 3 | Viercharcode | MTLB |
| 4 ... 5 | Uint16 | Zielplattform |
| 6 ... 9 | (Uint16, uint16) | Version der Metallib -Datei (Major, Minor) |
| 10 | Uint8 | Typ der Metallib -Datei |
| 11 | Uint8 | Zielbetrieb |
| 12 ... 15 | (Uint16, uint16) | Version des Zielbetriebs (Major, Minor) |
| 16 ... 23 | Uint64 | Größe der Metallib -Datei |
| 24 ... 39 | (Uint64, uint64) | Offset und Größe der Funktionsliste |
| 40 ... 55 | (Uint64, uint64) | Offset und Größe der öffentlichen Metadatenabteilung |
| 56 ... 71 | (Uint64, uint64) | Offset und Größe des privaten Metadatenabschnitts |
| 72 ... 87 | (Uint64, uint64) | Offset und Größe des Bitcode -Abschnitts |
| Zielplattform | Wert |
|---|---|
| macos | 0x8001 (0x01,0x80) |
| iOS | 0x0001 (0x01,0x00) |
| Metallib -Typ | Wert |
|---|---|
| Ausführbar | 0x00 |
| Kernbild | 0x01 |
| Dynamisch | 0x02 |
| Symbol Begleiter | 0x03 |
| Zielbetrieb | Wert |
|---|---|
| Unbekannt | 0x00 |
| macos | 0x81 |
| iOS | 0x82 |
| tvos | 0x83 |
| Watchos | 0x84 |
| Bridgeos (wahrscheinlich) | 0x85 |
| Makcatalysator | 0x86 |
| iOS Simulator | 0x87 |
| TVOS -Simulator | 0x88 |
| Watchos -Simulator | 0x89 |
| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
| 0 ... 3 | Uint32 | Eintragszahl (Anzahl der Funktionen) |
| 4 ... | Tag -Gruppen | Jede Tag -Gruppe enthält einige Informationen zu einer Metallfunktion |
Die Anzahl der Tag -Gruppen entspricht der Anzahl der Funktionen.
| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
| 0 ... 3 | Uint32 | Größe der Tag -Gruppe |
| 4 ... | Tags |
| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
| 0 ... 3 | Viercharcode | Name des Tags |
| 4 ... 5 | Uint16 | Größe des Tags |
| 6 ... | Bytes | Inhalt des Tags |
| Name | Inhaltsdatentyp | Inhalt |
|---|---|---|
| NAME | NULL-terminierte String im C-Stil | Name der Funktion |
| MDSZ | Uint64 | Größe des Bitcode |
| TYP | Uint8 | Art der Funktion |
| Hash | SHA256 Digest | Hash der Bitcode -Daten (SHA256) |
| Ausfall | (Uint64, uint64, uint64) | Offsets der Informationen zu dieser Funktion im Abschnitt mit öffentlichen Metadaten, Abschnitt "Private Metadaten" und Bitcode -Abschnitt |
| Laib | Uint64 | Versatz des Quellcodearchivs der Funktion im Abschnitt Embedded Source Code |
| Ver | (Uint16, uint16, uint16, uint16) | Bitcode- und Sprachversionen (Air.major, Air.minor, Language.major, Sprache.minor) |
| Laie | Uint8 | Metalltyp des render_target_array_index (für das Schichtwesen) |
| Tess | Uint8 | Patch-Typ und Anzahl der Steuerpunkte pro Patch (für die Nachverscheizungsfunktion) |
| Endt | Ende der Tag -Gruppe |
| Funktionstyp | Wert | Notiz |
|---|---|---|
| Scheitel | 0x00 | |
| Fragment | 0x01 | |
| Kernel | 0x02 | |
| Unqualifiziert | 0x03 | Funktionen in der metalldynamischen Bibliothek |
| Sichtbar | 0x04 | Funktionen mit [[visible]] oder [[stitchable]] Attributen |
| Extern | 0x05 | Externe Funktionen entsprachen der Option -fcikernel |
| Überschneidung | 0x06 |
Inhalt des TESS -Tags:
// Patch types:
// - triangle: 1
// - quad: 2
let content : UInt8 = controlPointCount << 2 | patchTypeEnthält Informationen zu Funktionskonstanten, Tessellation -Patches, Rückgabetypen usw.
Tags: CNST , VATT , VATY , RETR , ARGR usw.
Enthält Pfade zum Shader Source ( DEBI -Tag) und .air ( DEPF -Tag) -Dateien.
Existiert nur, wenn FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | Tags | Header -Erweiterungs -Tags |
| Name | Typ | Inhalt |
|---|---|---|
| HDYN | (Uint64, uint64) | Versatz und Größe des dynamischen Headerabschnitts |
| Vlst | (Uint64, uint64) | Offset und Größe der exportierten Variablenliste |
| Ilst | (Uint64, uint64) | Offset und Größe der importierten Symbolliste |
| HSRD/HSRC | (Uint64, uint64) | Offset und Größe des Abschnitts Embedded Source Code |
| Uuid | Uuid | Uuid der Metallbibliothek. |
| Endt | Ende der Header -Erweiterung |
| Name | Inhaltsdatentyp | Inhalt |
|---|---|---|
| NAME | NULL-terminierte String im C-Stil | Installieren Sie den Namen der Bibliothek |
| Dynl | NULL-terminierte String im C-Stil | Verknüpfte dynamische Bibliothek |
Variablenliste und importierte Symbolliste haben Strukturen, die der der Funktionsliste ähneln.
Es gibt nur dann, wenn der metallib -Build -Prozess so konfiguriert ist, dass Quellcode einbezieht.
| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
| 0 ... 1 | Uint16 | Anzahl der Elemente in diesem Abschnitt |
| 2 ... n | NULL-terminierte String im C-Stil | Verknüpfungsoptionen der metallib -Datei |
| n ... m | NULL-terminierte String im C-Stil | Arbeitsverzeichnis |
| M... | Tag -Gruppe | SARC -Tag |
Beachten Sie "Arbeitsverzeichnis" existiert nur in HSRD .
HINWEIS SARC -Tag verwendet 4 Bytes ( UInt32 ) Inhaltsgröße.
Inhalt des SARC -Tags:
| Byte -Reichweite | Typ | Inhalt |
|---|---|---|
| 0 ... n | NULL-terminierte String im C-Stil | ID des Quellcodearchivs |
| N... | Bzh | BZIP2 Compressed Quellcodearchiv |
| Wert | Typ | Wert | Typ |
|---|---|---|---|
| 0x00 | Keiner | 0x01 | Struktur |
| 0x02 | Array | 0x03 | Schweben |
| 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 | Hälfte | 0x11 | Half2 |
| 0x12 | Half3 | 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 | Kurz |
| 0x26 | Short2 | 0x27 | Short3 |
| 0x28 | Short4 | 0x29 | Ushort |
| 0x2a | UHort2 | 0x2b | UHort3 |
| 0x2c | UHort4 | 0x2d | Verkohlen |
| 0x2e | Char2 | 0x2f | Char3 |
| 0x30 | Char4 | 0x31 | Uchar |
| 0x32 | Uchar2 | 0x33 | Uchar3 |
| 0x34 | Uchar4 | 0x35 | Bool |
| 0x36 | Bool2 | 0x37 | Bool3 |
| 0x38 | Bool4 | 0x3a | Textur |
| 0x3b | Sampler | 0x3c | Zeiger |
| 0x3e | R8unorm | 0x3f | R8Snorm |
| 0x40 | R16UMOMM | 0x41 | R16Snorm |
| 0x42 | Rg8unorm | 0x43 | RG8Snorm |
| 0x44 | RG16UMORM | 0x45 | RG16Snorm |
| 0x46 | Rgba8unorm | 0x47 | Rgba8unorm_srgb |
| 0x48 | RGBA8Snorm | 0x49 | RGBA16UMORM |
| 0x4a | RGBA16Snorm | 0x4b | RGB10A2UMORM |
| 0x4c | Rg11b10float | 0x4d | Rgb9e5float |
| 0x4e | Renderpipeline | 0x4f | Computpipeline |
| 0x50 | IndirekteKommandbuffer | 0x51 | Lang |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Ulong |
| 0x56 | Ulong2 | 0x57 | Ulong3 |
| 0x58 | Ulong4 | 0x59 | Doppelt |
| 0x5a | Double2 | 0x5b | Double3 |
| 0x5c | Double4 | 0x5d | Float8 |
| 0x5e | Float16 | 0x5f | Halb8 |
| 0x60 | Halb16 | 0x61 | Int8 |
| 0x62 | Int16 | 0x63 | Uint8 |
| 0x64 | Uint16 | 0x65 | Short8 |
| 0x66 | Short16 | 0x67 | UHort8 |
| 0x68 | UHort16 | 0x69 | Char8 |
| 0x6a | Char16 | 0x6b | Uchar8 |
| 0x6c | Uchar16 | 0x6d | Long8 |
| 0x6e | Long16 | 0x6f | Ulong8 |
| 0x70 | Ulong16 | 0x71 | Double8 |
| 0x72 | Double16 | 0x73 | Sichtbarfunktiontable |
| 0x74 | IntersectionFunctionTable | 0x75 | PrimitiveAccelerationsstruktur |
| 0x76 | InstanceAccelerationsstruktur | 0x77 | Bool8 |
| 0x78 | Bool16 |
Wenn Sie der Meinung sind, dass es einen Fehler gibt, öffnen Sie bitte ein Problem. Sie können auch eine Pull -Anfrage mit dem fehlgeschlagenen Fehlertest öffnen.
Dieses Projekt hätte ohne die Forschung von Zhuowei nicht begonnen, die das grundlegende binäre Layout einer metallib -Datei, die Funktionsliste sowie den Bitcode -Abschnitt enthüllten. Danke, @zhuowei!
Ich habe versucht, die Forschung fortzusetzen, um eine vollständige Struktur der metallib -Datei zu erhalten, fand es jedoch zu schwer, auf der Grundlage von Vermutungen allein voranzukommen. Also wandte ich meine Aufmerksamkeit auf das Metal.framework , in der Hoffnung, herauszufinden, wie das Framework eine metallib -Datei lädt. Glücklicherweise ist es nicht zu schwer, nach dem Ziehen von Metal.framework/Metal zum Hopper Disassembler.
Metal.framework verwendet MTLLibraryDataWithArchive::parseArchiveSync(...) um metallib -Dateien zu laden. Es gibt viele Informationen, die in der Zusammenstellung von MTLLibraryDataWithArchive versteckt sind. Zum Beispiel:
Die Datei beginnt mit 0x424c544d (MTLB); Die Größe der Datei wird bei Offset 0x10 aufgezeichnet.
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 " ;
...
} Ein Int16 -Wert bei Offset 0x4 bezieht sich auf die Zielplattform.
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;Es gibt einen Abschnitt "Header -Erweiterung", der Informationen über den Abschnitt "Dynamic Header", "Imported Symbol List" und "Variable List" enthält:
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 ;
}Der Bitcode wird mit SHA256 validiert.
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);
...
}Viele viercc -Codes:
// 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; Nachdem ich mich herumgegraben hatte, konnte ich einen Überblick über die Struktur der metallib -Datei erhalten:
Die Datei verfügt über einen 88 Bytes -Header, der die Dateiversion, die Zielplattform, den Bibliothekstyp, die Abschnittsindizes usw. enthält.
Im Dateikopf sind 4 Abschnitte aufgezeichnet:
Funktionsliste
Öffentliche Metadaten
Private Metadaten
Bitcode -Module
Jeder Abschnitt wird mit Offset und einer Größe aufgezeichnet. Dies bedeutet, dass Abschnitte nicht übereinstimmend sein können, mit denen Apple neue Abschnitte dazwischen einführen kann, ohne die Kompatibilität zu brechen. Und Apple hat das genau für den Abschnitt "Header -Erweiterung" getan - es liegt zwischen der Funktionsliste und dem Abschnitt mit öffentlichen Metadaten.
Die meisten Abschnitte (mit Ausnahme des Bitcode -Abschnitts) ähneln einer "Tag" -Basierten Struktur:
FourCharCode wird als Name/Typ des Tags verwendet.
Ein UInt16 -Wert (in den meisten Fällen) der Größe folgt dem Namen des Tags.
Das Quellarchivdaten -Tag -Tag SARC verwendet nicht überraschend einen UInt32 -Wert für seine Größe - ein Quellarchiv kann 65 KB leicht überschreiten.
Tags werden gruppiert:
Jede Gruppe repräsentiert eine Reihe von Eigenschaften eines Elements.
Die Tag -Gruppe endet mit einem ENDT -Tag.
Als nächstes muss ich herausfinden, welche Informationen jedes Tag/Feld enthalten. Dies kann schwierig sein, von der Montage des Metal.framework zu bekommen. Framework, weil:
Einige Felder können nur für Werkzeuge oder Debuggen konzipiert werden, sodass MTLLibraryDataWithArchive sie möglicherweise nur ignoriert.
Die Montage ist plattformabhängig. Beispielsweise kann die iOS -Version von MTLLibraryDataWithArchive nur prüfen, ob die metallib für iOS erstellt wurde, und kann nicht erkennen, ob die Bibliothek für macOS erstellt wurde.
Einige Felder sind nur schwer zu analysieren und zu folgen. Beispiele:
Es gibt 3 Offsets im OFFT -Tag der Funktion. Wohin zeigen sie? Und wie werden sie endlich benutzt?
Was sind die möglichen Werte des Funktionstyps? Was bedeutet jeder Wert?
Es scheint, dass der schnellste Weg, diese Informationen zu erhalten, durch Experimente besteht.
Ich begann damit, metal manuell mit verschiedenen Shadern, Optionen und SDKs zu kompilieren und dann jedes Feld zu inspizieren, an dem ich interessiert war. Mein Desktop wurde schnell mit metallib -Dateien und Hexfiend -Fenstern überflutet, aber ich fand nicht viele nützliche Informationen. Ich brauche etwas, das metallib automatisch erstellen kann und mir nur das Feld darstellt, an dem ich interessiert bin.
Ich habe das "testgetriebene Erraten" entwickelt:
Schreiben Sie einen metallib -Parser basierend auf dem vorliegenden Binärstrukturüberblick.
Legen Sie im Parser den Wert eines Feldes/Tags (oder einiger zugehöriger Felder) an, das derzeit unbekannt ist.
Erstellen Sie Tests, die metallib -Dateien mit verschiedenen Arten von Shadern erstellen und Optionen kompilieren, die den Wert des Feldes beeinflussen können, und verwenden Sie den Parser, um die Dateidaten zu analysieren.
Führen Sie Tests aus und analysieren Sie das Protokoll, um Hypothesen zu erstellen.
Aktualisieren Sie den Parser basierend auf Hypothesen.
Führen Sie erneut Tests aus, um dies zu überprüfen.
Nach ein paar Runden konnte ich die Funktionstabelle, die Target OS -Tabelle und die Bedeutung von 3 Offsets im OFFT -Tag erhalten.
Ich fand auch ein paar Dinge interessant in diesem Prozess:
Metal unterstützt keine WatchOS. Es ist jedoch möglich, ein metallib -Targeting -WatchOS zu bauen. Und Apple enthält einige metallib in der 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 )
Leere metallib Ziele alte Versionen von iOS werden fälschlicherweise als Targeting macOS markiert.
Ich kann keine metallib mit dem Ziel -Betriebssystemwert 0x85 erstellen. Zuerst dachte ich, es könnte für die verdeckten Realityos reserviert sein, fand aber später heraus, dass es für die Bridgeos wahrscheinlicher ist.
10. April 2022
Tags wie LAYR , VATY , CNST usw. enthalten UInt8 -Werte von Metalldatentypen. Die entsprechende Beschreibung für jeden Datentypwert kann mit einer privaten Klasse in Metal abgerufen werden.
id value = [[ NSClassFromString ( @" MTLTypeInternal " ) alloc ] initWithDataType: 0x06 ];
NSLog ( @" %@ " , value.description); // MTLDataTypeFloat4Ich habe ein Befehlszeilenwerkzeug erstellt, um die Tabelle Metalldatentyp zu generieren.
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. März 2022
Die air-lld ( Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/ios/bin/air-lld ) enthält auch viele Informationen darüber, wie die metallib Datei erstellt wird. Einige Abschnittsnamen und Beschreibungen werden aktualisiert.
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;
}