4 minutos
Dependências e RPATH em bundles no macOS
O aviso é que se você programa em Swift ou utiliza Xcode, muito provavelmente não precisa dos itens relacionados abaixo, já que o próprio Xcode faz todo o processo, porém caso necessite, é possível adicionar um build target com comandos ou script shell que faça isso.
É mais comum projetos que utilizem processos de complicação e empacotamento fora do Xcode, precisem de fato do conteúdo deste post. Algo como CMake e/ou qmake, caso saiba de algum outro, te espero no @droposhado.
Comando otool
Este comando otool faz parte do llvm (compilador), sendo seu nome llvm-otool. Ele é parser para as saídas do comando llvm-objdump, sua saída contém informações muito úteis para desenvolvimento, apresentando ID e bibliotecas de dependência.
É interesante saber que o LLVM do Xcode é chamado de Apple LLVM.
Todo executável ou biblioteca (.dylib
) no macOS tem um ID, até mesmo
.frameworks
mencionados no post anterior, possuem binários que utilizam
ID, para obter esse ID use a flag -D
(desconsiderando um :
que sempre aparece):
$ otool -D <binario>
/Applications/<app-name>.app/Content/Frameworks/<lib-name>.dynlib:
De fato é um ID e toda vez que incluir ele como dependência de outro binário, seu caminho físico no disco tem de ser igual ao ID. Considerando o ID que vimos anteriormente, seu valor como dependência é:
/Applications/<app-name>.app/Content/Frameworks/<lib-name>.dynlib
Perceba que é hardcode onde encontrar dependência e o bundle em si (.app
) abre
em qualquer pasta, apenas exibindo a recomendação de ser movido para a pasta
/Application
, porém se o bundle (.app
) que tem a dependência estiver em
outro local, ele não encontra a dependência e ocorre um erro de execução. Sendo
assim, através deste post
vamos entender como fazer que o ID seja
relativo ao bundle (.app
).
A próxima flag -L
, lista o ID e todas as dependências com link dinânico a
esse binário:
$ otool -L <binario>
<path>/CoreFoundation (compatibility version 150.0.0, current version 1452.23.0)
<path>/Cocoa (compatibility version 1.0.0, current version 22.0.0)
@rpath/libvlc.dylib (compatibility version 12.0.0, current version 12.0.0)
Onde <path>
é o caminho no disco, só utilizei disso pra nao ficar imenso
horizontalmente e a saída a partir da segunda linha é sempre tabulação dupla
(\t\t
).
RPATH
É uma lista de paths
inserida no binário, onde ele procura as bibliotecas,
frameworks e dependências de link dinâmico em tempo de execução. Essa lista pode
ser obtida através do comando:
otool -l <binario>
Os RPATHs
são representados por LC_RPATH
, como é uma lista, pode existir
mais de uma entrada, esse é um dos paths
de busca:
Load command 55
cmd LC_RPATH
cmdsize 48
path @executable_path/../Frameworks (offset 12)
A representação de LC_LOAD_DYLIB
é a mesma de quando se usa a flag -L
, com
algumas informações relativas a dependência:
Load command 52
cmd LC_LOAD_DYLIB
cmdsize 56
name @rpath/libswiftObjectiveC.dylib (offset 24)
time stamp 2 Wed Dec 31 21:00:02 1969
current version 0.0.0
compatibility version 1.0.0
Como bundles tem dependências auto suficientes, utilizar elas com caminho absoluto pode não ser viável, considerando dependências que não são do sistema. Para essas existem modos relativos do executável principal encontrar as dependências.
@executable_path
Representa o executável do bundle (.app
) que se encontra dentro da pasta
apresentada abaixo:
<name>.app/Contents/MacOS
Isso quer dizer que para chegar na pasta Frameworks
o caminho seria:
@executable_path/../Frameworks/
@rpath
Os binários são procurados na lista de paths
inseridos na LC_RPATH
onde o
@rpath
é substitído por cada path
até que se finalize a lista encontrando ou
não o binário.
Como no exemplo a cima utilizando o binário @rpath/libswiftObjectiveC.dylib
, o
caminho final dele seria:
@executable_path/../Frameworks/libswiftObjectiveC.dylib
@loader_path
É mais direcionado ao uso em frameworks e plugins (com esse em especifíco não
tenho tanta experiência), porém o @loader_path
ao que entendi assume o valor do
path
de onde ele foi carregado, o valor de onde o binário que o invocou está.
Esta parte pode receber atualização.
Comando install_tool_name
Tudo visto até aqui, foi somente para visualizar e possívelmente encontrar um
erro relacionado a ID e paths
, porém é preciso corrigir e nesse
momento entram as funcionalidades do comando install_name_tool
.
ID
Como já foi dito, todo binário tem um ID e é possível fazer a troca. É
comum quando ele vem com caminho absoluto da máquina em que foi feito o
processo de compilação e se quer criar o bundle (.app
) com algo relativo.
Então é utilizado o comando:
$ install_name_tool -id <novo-id> <binario>
Alterar um RPATH
Caso já tenha registrado um path
no RPATH e queira alterar, utilize o comando:
$ install_name_tool -rpath <antigo> <novo> <binário>
Adicionar RPATH
Para adicionar um novo path
a lista de RPATH do binário, utilize o
comando:
$ install_name_tool -add_rpath <novo> <binário>
Remover RPATH
Para remover um path
da lista de RPATH do binário, utilize o
comando:
$ install_name_tool -delete_rpath <path> <binário>