MetalLibraryArchive es un producto del formato de archivo metallib de Apple de ingeniería inversa.
Puede usar MetalLibraryArchive para obtener el tipo de biblioteca, la plataforma de destino, las funciones de metal, etc., desde un archivo metallib .
La información extraída de una función de metal incluye:
metallib está configurado para incluir el código fuente. Disponible en: https://yuao.github.io/metallibraryExplorer
Obtenga más información
Un objetivo ejecutable llamado "Explorer" se incluye en el paquete. "Explorer" es una aplicación GUI que puede abrir, desempacar y desmontar (con la ayuda de llvm-dis ) archivos metallib .
Nota llvm-dis no está incluida, puede obtener una copia del binario en https://github.com/llvm/llvm-project/releases
Use el menú "Desensamblador" en la aplicación para ubicar el archivo ejecutable llvm-dis .

También puede usar MetalLibraryArchive como biblioteca:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| Rango de bytes | Tipo | Contenido |
|---|---|---|
| 0 ... 3 | Four Charcode | Mtlb |
| 4 ... 5 | Uint16 | Plataforma objetivo |
| 6 ... 9 | (Uint16, uint16) | Versión del archivo Metallib (mayor, menor) |
| 10 | Uint8 | Tipo de archivo metallib |
| 11 | Uint8 | OS de destino |
| 12 ... 15 | (Uint16, uint16) | Versión del sistema operativo objetivo (mayor, menor) |
| 16 ... 23 | Uint64 | Tamaño del archivo metallib |
| 24 ... 39 | (Uint64, uint64) | Compensación y tamaño de la lista de funciones |
| 40 ... 55 | (Uint64, uint64) | Compensación y tamaño de la sección de metadatos públicos |
| 56 ... 71 | (Uint64, uint64) | Compensación y tamaño de la sección de metadatos privados |
| 72 ... 87 | (Uint64, uint64) | Compensación y tamaño de la sección de código de bits |
| Plataforma objetivo | Valor |
|---|---|
| macosa | 0x8001 (0x01,0x80) |
| iOS | 0x0001 (0x01,0x00) |
| Tipo de metalib | Valor |
|---|---|
| Ejecutable | 0x00 |
| Imagen central | 0x01 |
| Dinámica | 0x02 |
| Símbolo compañero | 0x03 |
| OS de destino | Valor |
|---|---|
| Desconocido | 0x00 |
| macosa | 0x81 |
| iOS | 0x82 |
| tvos | 0x83 |
| vigilancia | 0x84 |
| Bridgeos (probablemente) | 0x85 |
| maccatalst | 0x86 |
| simulador de iOS | 0x87 |
| simulador de tvos | 0x88 |
| Simulador de WatchOS | 0x89 |
| Rango de bytes | Tipo | Contenido |
|---|---|---|
| 0 ... 3 | Uint32 | Recuento de entrada (el número de funciones) |
| 4 ... | Grupos de etiquetas | Cada grupo de etiquetas contiene información sobre una función de metal |
El número de grupos de etiquetas es igual al número de funciones.
| Rango de bytes | Tipo | Contenido |
|---|---|---|
| 0 ... 3 | Uint32 | Tamaño del grupo de etiquetas |
| 4 ... | Etiquetas |
| Rango de bytes | Tipo | Contenido |
|---|---|---|
| 0 ... 3 | Four Charcode | Nombre de la etiqueta |
| 4 ... 5 | Uint16 | Tamaño de la etiqueta |
| 6 ... | Bytes | Contenido de la etiqueta |
| Nombre | Tipo de datos de contenido | Contenido |
|---|---|---|
| NOMBRE | Cadena de estilo C nulo | Nombre de la función |
| Mdsz | Uint64 | Tamaño del código de bits |
| TIPO | Uint8 | Tipo de función |
| PICADILLO | SHA256 DIGEST | Hash de los datos del código de bits (SHA256) |
| Despreocupado | (Uint64, uint64, uint64) | Compensaciones de la información sobre esta función en la sección de metadatos públicos, la sección de metadatos privados y la sección de código de bits |
| Salpicar | Uint64 | Compensación del archivo del código fuente de la función en la sección del código fuente integrado |
| En comparación | (Uint16, uint16, uint16, uint16) | BitCode and Language Versions (Air.major, Air.Minor, Language.Major, Language.Minor) |
| Leña | Uint8 | Tipo de metal del render_target_array_index (para la representación en capas) |
| Tess | Uint8 | Tipo de parche y número de puntos de control por parche (para la función de vértice posterior a la teselación) |
| Endt | Fin del grupo de etiquetas |
| Tipo de función | Valor | Nota |
|---|---|---|
| Vértice | 0x00 | |
| Fragmento | 0x01 | |
| Núcleo | 0x02 | |
| No cualificado | 0x03 | Funciones en la biblioteca dinámica de metal |
| Visible | 0x04 | Funciones con [[visible]] o [[stitchable]] atributos |
| Externo | 0x05 | Funciones externas cumplidas con la opción -fcikernel |
| Intersección | 0x06 |
Contenido de la etiqueta TESS :
// Patch types:
// - triangle: 1
// - quad: 2
let content : UInt8 = controlPointCount << 2 | patchTypeContiene información sobre constantes de función, parches de teselación, tipos de devolución, etc.
Etiquetas: CNST , VATT , VATY , RETR , ARGR , etc.
Contiene rutas a los archivos de la fuente del sombreador (etiqueta DEBI ) y .air (etiqueta DEPF ).
Solo existe si FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| Rango de bytes | Tipo | Contenido |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | Etiquetas | Etiquetas de extensión del encabezado |
| Nombre | Tipo | Contenido |
|---|---|---|
| Hdyn | (Uint64, uint64) | Compensación y tamaño de la sección de encabezado dinámico |
| VLST | (Uint64, uint64) | Compensación y tamaño de la lista de variables exportadas |
| Párroco | (Uint64, uint64) | Compensación y tamaño de la lista de símbolos importados |
| HSRD/HSRC | (Uint64, uint64) | Compensación y tamaño de la sección del código fuente incrustado |
| Uuid | Uuid | Uuid de la biblioteca de metal. |
| Endt | Fin de la extensión del encabezado |
| Nombre | Tipo de datos de contenido | Contenido |
|---|---|---|
| NOMBRE | Cadena de estilo C nulo | Instalar el nombre de la biblioteca |
| Dynl | Cadena de estilo C nulo | Biblioteca dinámica vinculada |
La lista de variables y la lista de símbolos importados tienen estructuras que son similares a las de la lista de funciones.
Solo existe si el proceso de compilación metallib está configurado para incluir el código fuente.
| Rango de bytes | Tipo | Contenido |
|---|---|---|
| 0 ... 1 | Uint16 | Número de elementos en esta sección |
| 2 ... N | Cadena de estilo C nulo | Opciones de enlace del archivo metallib |
| Nuevo Méjico | Cadena de estilo C nulo | Directorio de trabajo |
| metro... | Grupo de etiquetas | Etiqueta SARC |
Nota "Directorio de trabajo" solo existe en HSRD .
Nota La etiqueta SARC usa el tamaño de contenido 4-bytes ( UInt32 ).
Contenido de la etiqueta SARC :
| Rango de bytes | Tipo | Contenido |
|---|---|---|
| 0 ... N | Cadena de estilo C nulo | ID del archivo del código fuente |
| norte... | Bzh | Archivo del código fuente comprimido BZIP2 |
| Valor | Tipo | Valor | Tipo |
|---|---|---|---|
| 0x00 | Ninguno | 0x01 | Estructura |
| 0x02 | Formación | 0x03 | Flotar |
| 0x04 | Flotante2 | 0x05 | Flotante3 |
| 0x06 | Float4 | 0x07 | Float2x2 |
| 0x08 | Float2x3 | 0x09 | Float2x4 |
| 0x0a | FLOAT3X2 | 0x0b | FLOAT3X3 |
| 0x0c | FLOAT3X4 | 0x0d | Float4x2 |
| 0x0e | Float4x3 | 0x0f | Float4x4 |
| 0x10 | Medio | 0x11 | Media2 |
| 0x12 | Medio3 | 0x13 | Medio 4 |
| 0x14 | Half2x2 | 0x15 | Medio2x3 |
| 0x16 | Half2x4 | 0x17 | Half3x2 |
| 0x18 | Medio3x3 | 0x19 | Half3x4 |
| 0x1a | Half4x2 | 0x1b | Medio 4x3 |
| 0x1c | Half4x4 | 0x1d | Intencionalmente |
| 0x1e | Int2 | 0x1f | Int3 |
| 0x20 | Int4 | 0x21 | Uint |
| 0x22 | Uint2 | 0x23 | Uint3 |
| 0x24 | Uint4 | 0x25 | Corto |
| 0x26 | Short2 | 0x27 | Corto 3 |
| 0x28 | Corto 4 | 0x29 | Mina |
| 0x2a | USHORT2 | 0x2b | Ushort3 |
| 0x2c | USHORT4 | 0x2d | Carbonizarse |
| 0x2e | Char2 | 0x2f | Char3 |
| 0x30 | Char4 | 0x31 | Álamos |
| 0x32 | UCHAR2 | 0x33 | UCHAR3 |
| 0x34 | UCHAR4 | 0x35 | Bool |
| 0x36 | Bool2 | 0x37 | Bool3 |
| 0x38 | Bool4 | 0x3a | Textura |
| 0x3b | Dechado | 0x3c | Puntero |
| 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 | Computación |
| 0x50 | Indirecto | 0x51 | Largo |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Ulong |
| 0x56 | Ulong2 | 0x57 | Ulong3 |
| 0x58 | Ulong4 | 0x59 | Doble |
| 0x5a | Doble2 | 0x5b | Doble3 |
| 0x5c | Doble4 | 0x5d | Flotante |
| 0x5e | Flotante16 | 0x5f | Medio 8 |
| 0x60 | Media16 | 0x61 | Int8 |
| 0x62 | Int16 | 0x63 | Uint8 |
| 0x64 | Uint16 | 0x65 | Corta 8 |
| 0x66 | Corto16 | 0x67 | USHORT8 |
| 0x68 | USHORT16 | 0x69 | Char8 |
| 0x6a | Char16 | 0x6b | UCHAR8 |
| 0x6c | UCHAR16 | 0x6d | Long8 |
| 0x6e | Long16 | 0x6f | Ulong8 |
| 0x70 | Ulong16 | 0x71 | Doble8 |
| 0x72 | Doble16 | 0x73 | Manta visible |
| 0x74 | Intersección | 0x75 | PrimitiveAccelerationStructure |
| 0x76 | InstanceAccelerationStructure | 0x77 | Bool8 |
| 0x78 | Bool16 |
Si cree que hay un error, abra un problema. También puede optar por abrir una solicitud de extracción con la prueba de falla incluida.
Este proyecto no habría comenzado sin la investigación de Zhuowei que reveló el diseño binario básico de un archivo metallib , la lista de funciones y la sección del código de bits. ¡Gracias, @zhuowei!
Traté de continuar la investigación para obtener una estructura completa del archivo metallib , pero me resultó demasiado difícil avanzar en base a conjeturas solas. Así que dirigí mi atención al mar metallib Metal.framework . Afortunadamente, no es demasiado difícil después de arrastrar Metal.framework/Metal a tolva desarmador.
Metal.framework utiliza MTLLibraryDataWithArchive::parseArchiveSync(...) para cargar archivos metallib . Hay mucha información oculta en el ensamblaje de MTLLibraryDataWithArchive . Por ejemplo:
El archivo comienza con 0x424c544d (mtlb); El tamaño del archivo se registra en el desplazamiento 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 " ;
...
} Un valor Int16 al desplazamiento 0x4 está relacionado con la plataforma de destino.
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;Hay una "sección de extensión de encabezado" que contiene información sobre la "sección de encabezado dinámico", "lista de símbolos importados" y "lista de 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 ;
}El código de bits se valida usando 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);
...
}Muchos códigos de cuatrocc:
// 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; Después de cavar un poco, pude obtener una descripción general de la estructura del archivo metallib :
El archivo tiene un encabezado de 88 bytes que contiene la versión del archivo, la plataforma de destino, el tipo de biblioteca, los índices de sección, etc.
Hay 4 secciones registradas en el encabezado del archivo:
Lista de funciones
Metadatos públicos
Metadatos privados
Módulos de código de bits
Cada sección se registra con un desplazamiento y un tamaño. Esto significa que las secciones pueden ser no contiguas, lo que permite a Apple introducir nuevas secciones intermedias sin romper la compatibilidad. Y Apple hizo eso exactamente para la sección "Extensión del encabezado": se encuentra entre la lista de funciones y la sección de metadatos públicos.
La mayoría de las secciones (excepto la sección de código de bits) se parecen a una estructura basada en "etiqueta":
FourCharcode se usa como nombre/tipo de etiqueta.
Un valor de tamaño UInt16 (en la mayoría de los casos) sigue el nombre de la etiqueta.
La etiqueta de datos del archivo de origen SARC como era de esperar, utiliza un valor UInt32 para su tamaño: un archivo de origen puede exceder fácilmente los 65 kb.
Las etiquetas están agrupadas:
Cada grupo representa un conjunto de propiedades de un elemento.
El grupo de etiquetas termina con una etiqueta ENDT .
A continuación, necesito descubrir qué información tiene cada etiqueta/campo. Esto puede ser difícil de obtener del ensamblaje del Metal.framework .
Algunos campos pueden diseñarse exclusivamente para herramientas o depuración, por lo que MTLLibraryDataWithArchive puede ignorarlos.
El ensamblaje depende de la plataforma. Por ejemplo, la versión iOS de MTLLibraryDataWithArchive solo puede verificar si el metallib está construido para iOS y no puede saber si la biblioteca está construida para macOS.
Algunos campos son difíciles de analizar y seguir. Ejemplos:
Hay 3 compensaciones en la etiqueta OFFT de la función, ¿dónde señalan? ¿Y cómo se usan finalmente?
¿Cuáles son los valores posibles del tipo de función? ¿Qué significa cada valor?
Parece que la forma más rápida de obtener esta información es a través de experimentos.
Comencé compilando manualmente archivos metal con diferentes sombreadores, opciones y SDK, luego inspeccionando cada campo que estaba interesado. Mi escritorio se inundó rápidamente con archivos metallib y Windows Hexfiend, pero no encontré mucha información útil. Necesito algo que pueda construir automáticamente metallib y me presente solo el campo que me interesa.
Se me ocurrió la "adivinación impulsada por las pruebas":
Escriba un analizador metallib basado en la descripción general de la estructura binaria en cuestión.
En el analizador, registre el valor de un campo/etiqueta (o algunos campos relacionados) que actualmente se desconoce.
Cree pruebas que produzcan archivos metallib utilizando diferentes tipos de sombreadores y compilar opciones que pueden afectar el valor del campo, y use el analizador para analizar los datos del archivo.
Ejecute pruebas y analice el registro para hacer hipótesis.
Actualice el analizador basado en hipótesis.
Ejecute las pruebas nuevamente para verificar.
Después de algunas rondas, pude obtener la tabla de tipo de función, la tabla de OS de destino y el significado de 3 compensaciones en la etiqueta OFFT .
También encontré algunas cosas interesantes en este proceso:
El metal no es compatible con WatchOS, sin embargo, es posible construir un WatchOS de metallib dirigido. Y Apple incluye algunos metallib s en el SDK Watchos. (por ejemplo, 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 vacío que se dirige a versiones antiguas de iOS se marcan erróneamente como macOS de orientación.
No puedo construir un metallib que tenga el valor del sistema operativo objetivo 0x85 . Al principio pensé que podría estar reservado para los reality de los realidad ocultos, pero luego descubrí que es más probable para los Bridgeos.
10 de abril de 2022
Etiquetas como LAYR , VATY , CNST , etc., contienen valores de UInt8 de tipos de datos de metal. La descripción correspondiente para cada valor de tipo de datos se puede recuperar utilizando una clase privada en metal.framework - mtltypeinternal
id value = [[ NSClassFromString ( @" MTLTypeInternal " ) alloc ] initWithDataType: 0x06 ];
NSLog ( @" %@ " , value.description); // MTLDataTypeFloat4Creé una herramienta de línea de comandos para generar la tabla de tipo de datos de metal.
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 de marzo de 2022
El air-lld ( Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/ios/bin/air-lld ) también proporciona mucha información sobre cómo se construye el archivo metallib . Se actualizan algunos nombres y descripciones de la sección.
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;
}