Inspetor de bibliotecas estáticas .a
Um ficheiro .a é um arquivo ar: oito bytes de assinatura e, por cada membro, um cabeçalho ASCII de largura fixa com 60 bytes —16 de nome, 12 de data de modificação, 6 de uid, 6 de gid, 8 de permissões em octal e 10 de tamanho em decimal— seguido dos bytes do membro, alinhados até um deslocamento par. Esta ferramenta lê tudo isso a partir dos bytes e mostra cada membro com o deslocamento do seu cabeçalho, o deslocamento onde os dados realmente começam, o tamanho, a data guardada em UTC, o dono e as permissões. Um nome maior do que o campo de 16 bytes vai para outro sítio, e as duas convenções fazem-no de maneira diferente: o GNU junta os nomes longos num membro chamado // e escreve /deslocamento no cabeçalho, enquanto o BSD e o macOS escrevem #1/comprimento e colocam o nome nos primeiros bytes dos dados do próprio membro, pelo que os dados não começam onde o cabeçalho acaba e o campo de tamanho conta também o nome. Quinze caracteres ainda cabem como "nome/", dezasseis já não; ler mal essa fronteira desloca todos os deslocamentos seguintes, por isso a convenção é indicada explicitamente e cada membro diz de onde veio o seu nome. A razão de existir da ferramenta, porém, é o índice de símbolos. O membro chamado / (ou /SYM64/, ou __.SYMDEF no BSD) liga cada símbolo ao deslocamento do membro que o define, e não passa de uma cache: nada o mantém em dia a não ser o ranlib. Se o arquivo for editado no sítio, montado por um script ou aumentado sem reconstruir o índice, este pode apontar para um membro que já não define aquele símbolo, ou falhar um símbolo que está mesmo ali — e o linker, que confia no índice, passa a relatar uma referência indefinida a um símbolo que o arquivo contém. Por isso leem-se os dois lados: o índice e a tabela de símbolos ELF de cada membro. As discordâncias são listadas uma a uma. Cada membro ELF mostra ainda os símbolos globais que define, com a letra do nm, e os que deixa indefinidos, que é o que revela a ordem de dependência entre membros. Os símbolos com definição forte em dois membros são recolhidos à parte, porque é esse o "duplicate symbol" com que uma ligação falha; definições fracas, comuns e únicas repetem-se legitimamente e não são assinaladas. Membros que não são objetos ELF —um Mach-O ou um COFF de outra plataforma, um arquivo aninhado, um ficheiro de texto acrescentado por engano— são identificados como tal em vez de interpretados a meio, e um arquivo fino, cujos membros são referências a ficheiros no disco, é reportado como tal porque os seus símbolos simplesmente não estão lá dentro. O que não se consegue dizer é que membros uma ligação concreta vai puxar: isso depende da ordem de ligação e do que ainda está indefinido quando se chega ao arquivo.
Como usar
- Solte um ficheiro .a sobre a caixa. Nada é enviado: o arquivo é lido pelo JavaScript da página.
- Veja no resumo a convenção (// do GNU ou #1/ do BSD), o tipo de índice de símbolos e quantos bytes do ficheiro são índice e tabela de nomes em vez de código.
- Comece pelo painel do índice em face dos membros. Uma linha verde quer dizer que a cache e os membros concordam; uma vermelha nomeia o símbolo e o membro que discordam.
- Percorra a tabela de membros para ver deslocamentos, tamanhos e datas guardadas: uma compilação reprodutível grava data, uid e gid a zero, por isso qualquer outro valor indica um arquivo feito com ar U.
- Abra um membro em "símbolos por membro" para ver o que define e o que ainda precisa, e use a lista de duplicados para explicar um erro de ligação por símbolo duplicado.
Perguntas frequentes
- O que significa "archive has no index; run ranlib to add one"?
- Que o arquivo foi criado sem a sua tabela de símbolos, normalmente com ar rcS, acrescentando com ar q, ou por uma ferramenta que escreve o formato ar por conta própria. Sem índice, o linker teria de abrir todos os membros para saber o que cada um define, por isso a maioria recusa e pede ranlib, que acrescenta um primeiro membro chamado / com o mapa. Correr ranlib sobre o ficheiro, ou ar s, resolve no sítio, e ar rcs constrói-o logo de início. Esta ferramenta mostra "índice de símbolos: nenhum" nesse caso, e a lista de membros continua perfeitamente legível, porque o índice é um acelerador e não faz parte dos membros.
- Como pode o índice estar errado se o ar o mantém atualizado?
- O ar reconstrói o índice sempre que reescreve o arquivo, por isso os comandos habituais são seguros. O índice fica desatualizado quando outra coisa mexe no ficheiro: um script que o remenda no sítio, um sistema de compilação que acrescenta com ar q e nunca corre o ranlib, um arquivo montado à mão ou por uma ferramenta que escreve o formato, ou um copiado entre máquinas e editado. Como o índice guarda um deslocamento em bytes e não um nome, um membro que muda sem o índice ser refeito deixa uma entrada a apontar para bytes que já não definem aquele símbolo. O linker confia no índice, por isso a falha aparece muito longe, como uma referência indefinida a um símbolo bem visível no nm.
- Qual é a diferença entre um .a do GNU e um do BSD, e o que é um arquivo fino?
- Apenas a forma de guardar nomes com mais de 16 caracteres e o índice de símbolos. O GNU põe os nomes longos num membro // e refere-os por deslocamento, e chama ao índice / ou /SYM64/ na forma de 64 bits; o BSD escreve #1/comprimento no campo do nome e guarda o nome nos primeiros bytes dos dados do membro, e chama ao índice __.SYMDEF ou __.SYMDEF SORTED. Os dados do membro começam, portanto, num sítio diferente em cada formato, e é essa a maneira mais comum de ler mal um arquivo. Um arquivo fino é uma terceira variante, criada por ar T: começa por !<thin> e guarda só cabeçalhos, deixando os dados nos .o originais do disco, pelo que mover apenas o .a o parte.
- Porque é que um membro lista símbolos indefinidos? A biblioteca está partida?
- Não, é normal e é justamente o objetivo da listagem. Cada membro é um ficheiro objeto separado, e um símbolo indefinido é um que ele espera que outro membro, outra biblioteca ou o próprio programa forneça. Lê-los em conjunto revela a ordem de dependência dentro do arquivo, o que importa porque uma biblioteca estática é procurada e não fundida: o linker percorre a linha de comandos uma só vez, puxa apenas os membros que resolvem algo indefinido naquele momento, e um membro puxado tarde pode deixar indefinido um símbolo que uma biblioteca anterior teria fornecido. É por isso que a ordem de ligação importa e por isso existe o --start-group.
- Porque é que só alguns símbolos repetidos são reportados como duplicados?
- Porque só alguns partem uma ligação. Uma definição forte do mesmo nome em dois membros é o clássico erro de símbolo duplicado: o segundo membro que o linker puxar colide com o primeiro. Mas as definições fracas (letras W e V do nm), os símbolos comuns (C) e os únicos do GNU (u) existem para se repetir: funções inline, templates e tabelas virtuais de C++ são emitidas em cada objeto que as usa e o linker guarda uma cópia. Uma libstdc++.a real tem milhares dessas repetições e nenhum erro, por isso listá-las todas enterraria a que interessa. Aqui só se recolhem os nomes com definição forte em mais do que um membro.
- O arquivo é enviado para algum lado?
- Não. É lido com a File API do navegador e analisado pelo JavaScript da página; não existe servidor para onde o enviar. Com uma biblioteca estática isso conta mais do que com outros ficheiros, porque um .a de um fornecedor ou de uma compilação interna é muitas vezes algo que não se pode enviar. Pode desligar a rede depois de a página carregar e a ferramenta continua a funcionar; o ficheiro nunca sai do separador.
Ferramentas relacionadas
Inspetor de .npy e .npz do NumPy
Leia o cabeçalho de um .npy ou .npz no navegador: dtype, shape, ordem de bytes, campos e uma prévia de valores, sem NumPy e sem upload.
Visualizador de arquivos mbox
Divide um arquivo .mbox no navegador: limites de mensagem, cabeçalhos decodificados, estrutura MIME, tópicos e Message-ID duplicados. Nada é enviado.
Inspetor de ZIP
Solte um ZIP e veja cada arquivo dentro — tamanhos, conteúdo e download individual — sem desempacotar localmente.
Inspetor de arquivos HAR (visualizador HTTP Archive)
Solte um .har das DevTools do Chrome, Firefox ou Safari e veja cada requisição — método, status, tamanho, tempo — com tabelas das mais lentas e maiores.
Validador de arquivos de zona DNS
Cole um arquivo de zona BIND e veja o que ele diz de verdade: cada nome completo, o SOA explicado e as armadilhas que um analisador não aponta.
Inspetor de pickle do Python
Desmonte um .pkl no navegador: cada opcode, o memo, o valor que ele remonta e se carregá-lo executaria código.