MetalLibraryArchive é um produto do formato de arquivo metallib da Apple reverso.
Você pode usar MetalLibraryArchive para obter o tipo de biblioteca, plataforma de destino, funções de metal etc., a partir de um arquivo metallib .
As informações extraídas de uma função de metal incluem:
metallib estiver configurado para incluir o código -fonte. Disponível em: https://yuao.github.io/metallibraryexplorer
Saber mais
Um alvo executável chamado "Explorer" está incluído no pacote. "Explorer" é um aplicativo GUI que pode abrir, desembalar e desmontar os arquivos metallib (com a ajuda do llvm-dis ).
NOTA llvm-dis não está incluído, você pode obter uma cópia do binário em https://github.com/llvm/llvm-project/releases
Use o menu "Desmontingbler" no aplicativo para localizar o arquivo executável llvm-dis .

Você também pode usar MetalLibraryArchive como uma biblioteca:
import MetalLibraryArchive
let archive = try Archive ( data : Data ( contentsOf : metallibURL ) )
let libraryType = archive . libraryType
let functions = archive . functions| Intervalo de bytes | Tipo | Contente |
|---|---|---|
| 0 ... 3 | FourcharCode | Mtlb |
| 4 ... 5 | Uint16 | Plataforma de destino |
| 6 ... 9 | (UINT16, UINT16) | Versão do arquivo Metallib (maior, menor) |
| 10 | Uint8 | Tipo do arquivo Metallib |
| 11 | Uint8 | Target OS |
| 12 ... 15 | (UINT16, UINT16) | Versão do sistema operacional alvo (maior, menor) |
| 16 ... 23 | Uint64 | Tamanho do arquivo Metallib |
| 24 ... 39 | (Uint64, uint64) | Deslocamento e tamanho da lista de funções |
| 40 ... 55 | (Uint64, uint64) | Deslocamento e tamanho da seção de metadados públicos |
| 56 ... 71 | (Uint64, uint64) | Deslocamento e tamanho da seção de metadados particulares |
| 72 ... 87 | (Uint64, uint64) | Deslocamento e tamanho da seção Bitcode |
| Plataforma de destino | Valor |
|---|---|
| macos | 0x8001 (0x01,0x80) |
| iOS | 0x0001 (0x01,0x00) |
| Tipo de Metallib | Valor |
|---|---|
| Executável | 0x00 |
| Imagem central | 0x01 |
| Dinâmico | 0x02 |
| Companheiro de símbolo | 0x03 |
| Target OS | Valor |
|---|---|
| Desconhecido | 0x00 |
| macos | 0x81 |
| iOS | 0x82 |
| TvOS | 0x83 |
| vigilância | 0x84 |
| Bridgeos (provavelmente) | 0x85 |
| MacCatalyst | 0x86 |
| simulador iOS | 0x87 |
| Simulador de tvOS | 0x88 |
| Simulador de WatchOS | 0x89 |
| Intervalo de bytes | Tipo | Contente |
|---|---|---|
| 0 ... 3 | Uint32 | Contagem de entrada (o número de funções) |
| 4 ... | Grupos de tags | Cada grupo de tags contém algumas informações sobre uma função de metal |
O número de grupos de tags é igual ao número de funções.
| Intervalo de bytes | Tipo | Contente |
|---|---|---|
| 0 ... 3 | Uint32 | Tamanho do grupo de tags |
| 4 ... | Tags |
| Intervalo de bytes | Tipo | Contente |
|---|---|---|
| 0 ... 3 | FourcharCode | Nome da tag |
| 4 ... 5 | Uint16 | Tamanho da tag |
| 6 ... | Bytes | Conteúdo da tag |
| Nome | Tipo de dados de conteúdo | Contente |
|---|---|---|
| NOME | Corda de estilo C terminada em nulo | Nome da função |
| MDSZ | Uint64 | Tamanho do código de bits |
| TIPO | Uint8 | Tipo da função |
| Hash | SHA256 Digest | Hash dos dados do Bitcode (SHA256) |
| Offt | (Uint64, uint64, uint64) | Compensações das informações sobre esta função na seção de metadados públicos, seção de metadados privados e seção de bitcode |
| Intradorso | Uint64 | Offset do arquivo de código -fonte da função na seção de código -fonte incorporada |
| Vers | (Uint16, uint16, uint16, uint16) | Bitcode e versões de idioma (Air.major, Air.Minor, Language.Major, Language.Minor) |
| Layr | Uint8 | Tipo de metal do render_target_array_index (para renderização em camadas) |
| Tess | Uint8 | Tipo de patch e número de pontos de controle por patch (para função de vértice pós-teselação) |
| Endt | Fim do grupo de tags |
| Tipo de função | Valor | Observação |
|---|---|---|
| Vértice | 0x00 | |
| Fragmento | 0x01 | |
| Kernel | 0x02 | |
| Não qualificado | 0x03 | Funções na Biblioteca Dinâmica de Metal |
| Visível | 0x04 | Funções com atributos [[visible]] ou [[stitchable]] |
| Extern | 0x05 | As funções externas cumpriam -se com a opção -fcikernel |
| Interseção | 0x06 |
Conteúdo da tag TESS :
// Patch types:
// - triangle: 1
// - quad: 2
let content : UInt8 = controlPointCount << 2 | patchTypeContém informações sobre constantes de função, patches de tesellation, tipos de retorno, etc.
Tags: CNST , VATT , VATY , RETR , ARGR , etc.
Contém caminhos para os arquivos do shader Source (Tag DEBI ) e .air (TAG DEPF ).
Existe apenas se FunctionListOffset + FunctionListSize + 4 != PublicMetadataOffset
| Intervalo de bytes | Tipo | Contente |
|---|---|---|
FunctionListOffset + FunctionListSize + 4 ... | Tags | Tags de extensão do cabeçalho |
| Nome | Tipo | Contente |
|---|---|---|
| Hdyn | (Uint64, uint64) | Deslocamento e tamanho da seção de cabeçalho dinâmico |
| Vlst | (Uint64, uint64) | Deslocamento e tamanho da lista de variáveis exportadas |
| Ilst | (Uint64, uint64) | Deslocamento e tamanho da lista de símbolos importados |
| HSRD/HSRC | (Uint64, uint64) | Deslocamento e tamanho da seção de código -fonte incorporada |
| Uuid | Uuid | Uuid da biblioteca de metal. |
| Endt | Fim da extensão do cabeçalho |
| Nome | Tipo de dados de conteúdo | Contente |
|---|---|---|
| NOME | Corda de estilo C terminada em nulo | Instale o nome da biblioteca |
| Dynl | Corda de estilo C terminada em nulo | Biblioteca dinâmica vinculada |
Lista de variáveis e lista de símbolos importados têm estruturas semelhantes às da lista de funções.
Existir apenas se o processo de construção metallib estiver configurado para incluir o código -fonte.
| Intervalo de bytes | Tipo | Contente |
|---|---|---|
| 0 ... 1 | Uint16 | Número de itens nesta seção |
| 2 ... n | Corda de estilo C terminada em nulo | Opções de link do arquivo metallib |
| n ... m | Corda de estilo C terminada em nulo | Diretório de trabalho |
| m ... | Grupo de tags | Tag SARC |
Nota "Diretório de trabalho" só existe no HSRD .
Nota SARC Tag usa tamanho de conteúdo 4-bytes ( UInt32 ).
Conteúdo da tag SARC :
| Intervalo de bytes | Tipo | Contente |
|---|---|---|
| 0 ... n | Corda de estilo C terminada em nulo | ID do arquivo de código -fonte |
| n ... | BZH | BZIP2 Arquivo de código -fonte comprimido |
| Valor | Tipo | Valor | Tipo |
|---|---|---|---|
| 0x00 | Nenhum | 0x01 | Estrutura |
| 0x02 | Variedade | 0x03 | Flutuador |
| 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 | Metade | 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 | Curto |
| 0x26 | Short2 | 0x27 | Short3 |
| 0x28 | Short4 | 0x29 | Ushort |
| 0x2a | Ushort2 | 0x2b | Ushort3 |
| 0x2c | Ushort4 | 0x2d | Char |
| 0x2e | Char2 | 0x2f | Char3 |
| 0x30 | Char4 | 0x31 | UChar |
| 0x32 | UChar2 | 0x33 | UChar3 |
| 0x34 | UChar4 | 0x35 | Bool |
| 0x36 | BOOL2 | 0x37 | BOOL3 |
| 0x38 | Bool4 | 0x3a | Textura |
| 0x3b | Amostrador | 0x3c | Ponteiro |
| 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 | Longo |
| 0x52 | Long2 | 0x53 | Long3 |
| 0x54 | Long4 | 0x55 | Ulong |
| 0x56 | ULONG2 | 0x57 | ULONG3 |
| 0x58 | ULONG4 | 0x59 | Dobro |
| 0x5a | Double2 | 0x5b | Duplo3 |
| 0x5c | Duplo4 | 0x5d | Float8 |
| 0x5e | Float16 | 0x5f | Half8 |
| 0x60 | Half16 | 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 | InterseçãoFunctionTable | 0x75 | PRIMITIVEACCELERATIONSTRUTURA |
| 0x76 | InstanceAcceLreationstructure | 0x77 | Bool8 |
| 0x78 | BOOL16 |
Se você acha que há um erro, abra um problema. Você também pode optar por abrir uma solicitação de tração com o teste de falha incluído.
Este projeto não teria começado sem a pesquisa de Zhuowei, que revelou o layout binário básico de um arquivo metallib , a lista de funções e a seção Bitcode. Obrigado, @zhuowei!
Tentei continuar a pesquisa para obter uma estrutura completa do arquivo metallib , mas achei muito difícil avançar com base apenas em suposições. Então, voltei minha atenção para o Metal.framework na esperança de descobrir como a estrutura carrega um arquivo metallib . Felizmente, não é muito difícil depois de arrastar Metal.framework/Metal .
Metal.framework usa MTLLibraryDataWithArchive::parseArchiveSync(...) para carregar arquivos metallib . Há muita informação escondida na montagem do MTLLibraryDataWithArchive . Por exemplo:
O arquivo começa com 0x424c544d (mtlb); O tamanho do arquivo é gravado no deslocamento 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 " ;
...
} Um valor Int16 no deslocamento 0x4 está relacionado à 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;Há uma "seção de extensão do cabeçalho" que contém informações sobre a "seção de cabeçalho dinâmico", "Lista de símbolos importados" e "Lista de variáveis":
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 ;
}O Bitcode é validado usando o 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);
...
}Muitos códigos de quatrocc:
// 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; Depois de alguns cavando, consegui uma visão geral da estrutura do arquivo metallib :
O arquivo possui um cabeçalho de 88 bytes que contém a versão do arquivo, a plataforma de destino, o tipo de biblioteca, os índices de seção etc.
Existem 4 seções registradas no cabeçalho do arquivo:
Lista de funções
Metadados públicos
Metadados particulares
Módulos Bitcode
Cada seção é registrada com um deslocamento e um tamanho. Isso significa que as seções podem não ser contíguas, o que permite que a Apple introduza novas seções sem quebrar a compatibilidade. E a Apple fez isso exatamente para a seção "Extensão do cabeçalho" - está entre a lista de funções e a seção de metadados públicos.
A maioria das seções (exceto a seção Bitcode) se assemelha a uma estrutura baseada em "tag":
O FourCharCode é usado como o nome/tipo da tag.
Um valor UInt16 (na maioria dos casos) do tamanho segue o nome da tag.
A tag de dados do arquivo de origem SARC usa sem surpresa um valor UInt32 para seu tamanho - um arquivo de origem pode exceder facilmente 65kb.
As tags são agrupadas:
Cada grupo representa um conjunto de propriedades de um item.
O grupo de tags termina com uma tag ENDT .
Em seguida, preciso descobrir quais informações cada tag/campo se mantém. Isso pode ser difícil de obter da montagem do Metal.framework porque:
Alguns campos podem ser projetados exclusivamente para ferramentas ou depuração, portanto, MTLLibraryDataWithArchive pode apenas ignorá -los.
A montagem depende da plataforma. Por exemplo, a versão iOS do MTLLibraryDataWithArchive pode verificar apenas se o metallib é construído para iOS e não sabe dizer se a biblioteca foi criada para o MacOS.
Alguns campos são apenas difíceis de analisar e seguir. Exemplos:
Existem 3 compensações na etiqueta OFFT da função, para onde eles estão apontando? E como eles finalmente são usados?
Quais são os valores possíveis do tipo de função? O que significa cada valor?
Parece que a maneira mais rápida de obter essas informações é através de experimentos.
Comecei compilando manualmente arquivos metal com diferentes shaders, opções e SDKs e depois inspecionando cada campo em que estava interessado. Minha área de trabalho foi rapidamente inundada com arquivos metallib e Windows Hexfiend, mas não encontrei muita informação útil. Preciso de algo que possa criar automaticamente metallib e me apresenta apenas o campo em que estou interessado.
Eu inventei o "adivinhamento orientado para o teste":
Escreva um analisador metallib com base na visão geral da estrutura binária em questão.
No analisador, registre o valor de um campo/tag (ou alguns campos relacionados) que são atualmente desconhecidos.
Crie testes que produzam arquivos metallib usando diferentes tipos de shaders e compilem opções que podem afetar o valor do campo e use o analisador para analisar os dados do arquivo.
Execute testes e analise o log para fazer hipóteses.
Atualize o analisador com base em hipóteses.
Execute testes novamente para verificar.
Depois de algumas rodadas, consegui obter a tabela de tipos de função, a tabela de destino do destino e o significado de 3 compensações na tag OFFT .
Eu também achei algumas coisas interessantes neste processo:
O Metal não suporta vigias, no entanto, é possível construir um WatchOS de direcionamento metallib . E a Apple inclui alguns metallib S no SDK WatchOS. (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 )
As versões antigas do metallib vazias são marcadas por engano como MacOS direcionadas.
Não posso construir um metallib que tenha o valor do sistema de destino 0x85 . No começo, pensei que poderia ser reservado para os realidade oculta, mas depois descobri que é mais provável para os Bridgeos.
10 de abril de 2022
Tags como LAYR , VATY , CNST , etc., contêm valores UInt8 dos tipos de dados de metal. A descrição correspondente para cada valor de tipo de dados pode ser recuperada usando uma classe privada em metal.framework - mtltypeinternal
id value = [[ NSClassFromString ( @" MTLTypeInternal " ) alloc ] initWithDataType: 0x06 ];
NSLog ( @" %@ " , value.description); // MTLDataTypeFloat4Criei uma ferramenta de linha de comando para gerar a tabela de tipos de dados 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 março de 2022
O air-lld ( Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/ios/bin/air-lld ) também fornece muitas informações sobre como o arquivo metallib é construído. Alguns nomes e descrições de seção são atualizados.
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;
}