MetalLibraryArchive est un produit du format de fichier metallib d'Apple à ingénierie rétro-inverse.
Vous pouvez utiliser MetalLibraryArchive pour obtenir le type de bibliothèque, la plate-forme cible, les fonctions métalliques, etc., à partir d'un fichier metallib .
Les informations extraites d'une fonction métallique comprennent:
metallib est configuré pour inclure le code source. Disponible sur: https://yuao.github.io/metallibraryExplorer
Apprendre encore plus
Une cible exécutable appelée "Explorer" est incluse dans le package. "Explorer" est une application GUI qui peut ouvrir, déballer et désassembler (à l'aide de fichiers metallib llvm-dis ).
Remarque llvm-dis n'est pas inclus, vous pouvez obtenir une copie du binaire à https://github.com/llvm/llvm-project/releases
Utilisez le menu "Disassebler" dans l'application pour localiser le fichier exécutable llvm-dis .

Vous pouvez également utiliser MetalLibraryArchive comme bibliothèque:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| Gamme d'octets | Taper | Contenu |
|---|---|---|
| 0 ... 3 | Quadrilatère | MTLB |
| 4 ... 5 | Uint16 | Plate-forme cible |
| 6 ... 9 | (Uint16, uint16) | Version du fichier métallib (major, mineur) |
| 10 | Uint8 | Type du fichier métallib |
| 11 | Uint8 | Target OS |
| 12 ... 15 | (Uint16, uint16) | Version du système d'exploitation cible (major, mineur) |
| 16 ... 23 | Uint64 | Taille du fichier métallib |
| 24 ... 39 | (Uint64, uint64) | Décalage et taille de la liste des fonctions |
| 40 ... 55 | (Uint64, uint64) | Décalage et taille de la section des métadonnées publiques |
| 56 ... 71 | (Uint64, uint64) | Décalage et taille de la section des métadonnées privées |
| 72 ... 87 | (Uint64, uint64) | Décalage et taille de la section Bitcode |
| Plate-forme cible | Valeur |
|---|---|
| macos | 0x8001 (0x01,0x80) |
| ios | 0x0001 (0x01,0x00) |
| type métallib | Valeur |
|---|---|
| Exécutable | 0x00 |
| Image de base | 0x01 |
| Dynamique | 0x02 |
| Compagnon de symbole | 0x03 |
| Target OS | Valeur |
|---|---|
| Inconnu | 0x00 |
| macos | 0x81 |
| ios | 0x82 |
| tvos | 0x83 |
| watchos | 0x84 |
| Bridgeos (probablement) | 0x85 |
| maccatalyste | 0x86 |
| simulateur iOS | 0x87 |
| simulateur TVOS | 0x88 |
| simulateur Watchos | 0x89 |
| Gamme d'octets | Taper | Contenu |
|---|---|---|
| 0 ... 3 | Uint32 | Nombre d'entrée (le nombre de fonctions) |
| 4 ... | Groupes de balises | Chaque groupe de balises contient des informations sur une fonction métallique |
Le nombre de groupes de balises est égal au nombre de fonctions.
| Gamme d'octets | Taper | Contenu |
|---|---|---|
| 0 ... 3 | Uint32 | Taille du groupe de balises |
| 4 ... | Balises |
| Gamme d'octets | Taper | Contenu |
|---|---|---|
| 0 ... 3 | Quadrilatère | Nom de la balise |
| 4 ... 5 | Uint16 | Taille de la balise |
| 6 ... | Octets | Contenu de la balise |
| Nom | Type de données de contenu | Contenu |
|---|---|---|
| NOM | Chaîne de style C à terminaison nulle | Nom de la fonction |
| MDSZ | Uint64 | Taille du code binaire |
| TAPER | Uint8 | Type de fonction |
| HACHER | Sha256 digest | Hachage des données de code binde (SHA256) |
| Offs | (Uint64, uint64, uint64) | Comptabilisation des informations sur cette fonction dans la section des métadonnées publiques, la section des métadonnées privées et la section Bitcode |
| Soff | Uint64 | Décalage de l'archive du code source de la fonction dans la section de code source intégré |
| Contre | (Uint16, uint16, uint16, uint16) | Bitcode and Language Versions (Air.Major, Air.Minor, Language.Major, Language.Minor) |
| Pale | Uint8 | Type de métal du render_target_array_index (pour le rendu en couches) |
| Tess | Uint8 | Type de correctif et nombre de points de contrôle par patch (pour la fonction de sommet post-tassellation) |
| Fin | Fin du groupe de balises |
| Type de fonction | Valeur | Note |
|---|---|---|
| Sommet | 0x00 | |
| Fragment | 0x01 | |
| Noyau | 0x02 | |
| Sans réserve | 0x03 | Fonctions dans la bibliothèque dynamique métallique |
| Visible | 0x04 | Fonctions avec [[visible]] ou [[stitchable]] Attributs |
| Externe | 0x05 | Les fonctions externes sont conformes à l'option -fcikernel |
| Intersection | 0x06 |
Contenu de la balise TESS :
// Patch types:
// - triangle: 1
// - quad: 2
let content : UInt8 = controlPointCount << 2 | patchTypeContient des informations sur les constantes de fonction, les correctifs de télésilation, les types de retour, etc.
Tags: CNST , VATT , VATY , RETR , ARGR , etc.
Contient des chemins vers les fichiers Source Shader ( DEBI TAG) et .air (balise DEPF ).
N'existe que si FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| Gamme d'octets | Taper | Contenu |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | Balises | Étiquettes d'extension d'en-tête |
| Nom | Taper | Contenu |
|---|---|---|
| Hdyn | (Uint64, uint64) | Décalage et taille de la section d'en-tête dynamique |
| Vlst | (Uint64, uint64) | Décalage et taille de la liste des variables exportées |
| Envers | (Uint64, uint64) | Décalage et taille de la liste des symboles importés |
| HSRD / HSRC | (Uint64, uint64) | Décalage et taille de la section de code source intégré |
| Uuid | Uuid | Uuid de la bibliothèque métallique. |
| Fin | Fin de l'extension de l'en-tête |
| Nom | Type de données de contenu | Contenu |
|---|---|---|
| NOM | Chaîne de style C à terminaison nulle | Installez le nom de la bibliothèque |
| Dynl | Chaîne de style C à terminaison nulle | Bibliothèque dynamique liée |
La liste des variables et la liste des symboles importés ont des structures similaires à celles de la liste des fonctions.
N'existe que si le processus de construction metallib est configuré pour inclure le code source.
| Gamme d'octets | Taper | Contenu |
|---|---|---|
| 0 ... 1 | Uint16 | Nombre d'articles dans cette section |
| 2 ... n | Chaîne de style C à terminaison nulle | Options de liaison du fichier metallib |
| n ... m | Chaîne de style C à terminaison nulle | Répertoire de travail |
| m ... | Groupe de balises | SARC sarc |
Remarque "Répertoire de travail" n'existe que dans HSRD .
Remarque La balise SARC utilise la taille du contenu de 4 octets ( UInt32 ).
Contenu de la balise SARC :
| Gamme d'octets | Taper | Contenu |
|---|---|---|
| 0 ... n | Chaîne de style C à terminaison nulle | ID de l'archive du code source |
| n ... | Bzh | Archive de code source compressé BZIP2 |
| Valeur | Taper | Valeur | Taper |
|---|---|---|---|
| 0x00 | Aucun | 0x01 | Structure |
| 0x02 | Tableau | 0x03 | Flotter |
| 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 | Moitié | 0x11 | Demi-2 |
| 0x12 | Demi-3 | 0x13 | Demi-4 |
| 0x14 | Demi-2x2 | 0x15 | Demi-2x3 |
| 0x16 | Demi-2x4 | 0x17 | Half3x2 |
| 0x18 | Half3x3 | 0x19 | Half3x4 |
| 0x1a | Demi-4x2 | 0x1b | Half4X3 |
| 0x1c | Demi-4x4 | 0x1d | Int |
| 0x1e | Int2 | 0x1f | Int3 |
| 0x20 | Int4 | 0x21 | Uint |
| 0x22 | Uint2 | 0x23 | Uint3 |
| 0x24 | Uint4 | 0x25 | Court |
| 0x26 | Short2 | 0x27 | Short3 |
| 0x28 | Short4 | 0x29 | Faire une œuvre |
| 0x2a | Ushort2 | 0x2b | Ushort3 |
| 0x2c | Ushort4 | 0x2d | Carboniser |
| 0x2e | Char2 | 0x2f | Char3 |
| 0x30 | Char4 | 0x31 | Uchar |
| 0x32 | Uchar2 | 0x33 | Uchar3 |
| 0x34 | Uchar4 | 0x35 | Bool |
| 0x36 | Bool2 | 0x37 | Bool3 |
| 0x38 | Bool4 | 0x3a | Texture |
| 0x3b | Échantillonneur | 0x3c | Aiguille |
| 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 | ComposePipeline |
| 0x50 | COMMANDBUFFER INDIRECT | 0x51 | Long |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Ulong |
| 0x56 | Ulong2 | 0x57 | Ulong3 |
| 0x58 | Ulong4 | 0x59 | Double |
| 0x5a | Double2 | 0x5b | Double3 |
| 0x5c | Double4 | 0x5d | Float8 |
| 0x5e | Float16 | 0x5f | Demi-8 |
| 0x60 | Demi-16 | 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 | Intersectionfunctiontable | 0x75 | PrimitiveAcelerationsTructure |
| 0x76 | Instructure instructive | 0x77 | Bool8 |
| 0x78 | Bool16 |
Si vous pensez qu'il y a une erreur, veuillez ouvrir un problème. Vous pouvez également choisir d'ouvrir une demande de traction avec le test de défaillance inclus.
Ce projet n'aurait pas commencé sans les recherches de Zhuowei qui ont révélé la disposition binaire de base d'un fichier metallib , la liste des fonctions ainsi que la section Bitcode. Merci, @zhuowei!
J'ai essayé de poursuivre les recherches pour obtenir une structure complète du fichier metallib , mais j'ai trouvé trop difficile d'aller de l'avant en fonction de la conjecture seule. J'ai donc tourné mon attention vers le Metal.framework metallib Heureusement, ce n'est pas trop difficile après avoir traîné Metal.framework/Metal .
Metal.framework utilise MTLLibraryDataWithArchive::parseArchiveSync(...) pour charger des fichiers metallib . Il y a beaucoup d'informations cachées dans l'assemblage de MTLLibraryDataWithArchive . Par exemple:
Le fichier commence par 0x424c544d (MTLB); La taille du fichier est enregistrée au décalage 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 " ;
...
} Une valeur Int16 à Offset 0x4 est liée à la plate-forme cible.
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;Il existe une "section d'extension d'en-tête" qui contient des informations sur la "section d'en-tête dynamique", "Liste de symboles importée" et "Liste des variables":
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 ;
}Le bitcode est validé à l'aide de 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);
...
}Beaucoup de codes quatrecc:
// 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; Après avoir creusé, j'ai pu obtenir un aperçu de la structure du fichier metallib :
Le fichier a un en-tête de 88 octets qui contient la version du fichier, la plate-forme cible, le type de bibliothèque, les indices de section, etc.
Il y a 4 sections enregistrées dans l'en-tête du fichier:
Liste de fonctions
Métadonnées publiques
Métadonnées privées
Modules de code binaire
Chaque section est enregistrée avec un décalage et une taille. Cela signifie que les sections peuvent être non contiguës, ce qui permet à Apple d'introduire de nouvelles sections entre les deux sans casser la compatibilité. Et Apple l'a fait exactement pour la section "Extension d'en-tête" - il se situe entre la liste des fonctions et la section des métadonnées publiques.
La plupart des sections (à l'exception de la section Bitcode) ressemblent à une structure basée sur "tag":
FourCharcode est utilisé comme nom / type de la balise.
Une valeur UInt16 (dans la plupart des cas) de taille suit le nom de la balise.
La balise de données d'archives source SARC utilise sans surprise une valeur UInt32 pour sa taille - une archive source peut facilement dépasser 65 Ko.
Les balises sont regroupées:
Chaque groupe représente un ensemble de propriétés d'un élément.
Le groupe de balises se termine par une balise ENDT .
Ensuite, je dois comprendre les informations que chaque balise / champ détient. Cela peut être difficile à obtenir de l'assemblage du Metal.framework .
Certains champs peuvent être conçus uniquement pour l'outillage ou le débogage, de sorte que MTLLibraryDataWithArchive peut simplement les ignorer.
L'assemblage dépend de la plate-forme. Par exemple, la version iOS de MTLLibraryDataWithArchive ne peut vérifier que si le metallib est conçu pour iOS et ne peut pas dire si la bibliothèque est conçue pour MacOS.
Certains champs sont difficiles à analyser et à suivre. Exemples:
Il y a 3 décalages dans la balise OFFT de la fonction, où pointent-ils? Et comment sont-ils finalement utilisés?
Quelles sont les valeurs possibles du type de fonction? Que signifie chaque valeur?
Il semble que le moyen le plus rapide d'obtenir ces informations soit par le biais d'expériences.
J'ai commencé par compilation manuellement des fichiers metal avec différents shaders, options et SDK, puis inspectant chaque champ qui m'intéressait. Mon bureau a été rapidement inondé de fichiers metallib et de fenêtres HexFiend, mais je n'ai pas trouvé beaucoup d'informations utiles. J'ai besoin de quelque chose qui peut construir automatiquement metallib et ne me présente que le domaine qui m'intéresse.
J'ai proposé la "devinettes conduites au test":
Écrivez un analyseur metallib en fonction de la vue d'ensemble de la structure binaire à portée de main.
Dans l'analyseur, enregistrez la valeur d'un champ / balise (ou de certains champs connexes) qui est actuellement inconnue.
Créez des tests qui produisent des fichiers metallib à l'aide de différents types de shaders et des options de compilation qui peuvent affecter la valeur du champ, et utiliser l'analyseur pour analyser les données du fichier.
Exécutez des tests et analysez le journal pour faire des hypothèses.
Mettez à jour l'analyseur en fonction des hypothèses.
Exécutez à nouveau des tests pour vérifier.
Après quelques tours, j'ai pu obtenir la table de type de fonction, la table du système d'exploitation cible et la signification de 3 décalages dans la balise OFFT .
J'ai également trouvé quelques choses intéressantes dans ce processus:
Le métal ne prend pas en charge WatchOS, cependant, il est possible de construire un metallib ciblant WatchOS. Et Apple inclut certains metallib dans le SDK Watchos. (par exemple 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
Les metallib vides ciblant les anciennes versions d'iOS sont marqués par erreur comme ciblant les macOS.
Je ne peux pas construire un metallib qui a la valeur du système d'exploitation cible 0x85 . Au début, je pensais que cela pourrait être réservé aux réalités dissimulés, mais j'ai découvert plus tard qu'il était plus probable pour les Bridgeos.
10 avril 2022
Les balises comme LAYR , VATY , CNST , etc., contiennent des valeurs UInt8 des types de données métalliques. La description correspondante de chaque valeur de type de données peut être récupérée à l'aide d'une classe privée dans Metal.Framework - MtlTypeinternal
id value = [[ NSClassFromString ( @" MTLTypeInternal " ) alloc ] initWithDataType: 0x06 ];
NSLog ( @" %@ " , value.description); // MTLDataTypeFloat4J'ai créé un outil de ligne de commande pour générer la table de type de données métalliques.
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 mars 2022
L' air-lld ( Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/ios/bin/air-lld ) fournit également de nombreuses informations sur la façon dont le fichier metallib est construit. Certains noms et descriptions de section sont mis à jour.
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;
}