Posso alterar o 'rpath' em um binário já compilado?

92

Tenho um executável antigo que está programado para a pilha de sucata, mas ainda não está lá. Ele depende de algumas bibliotecas que foram removidas do meu ambiente, mas eu tenho algumas bibliotecas stub em algum lugar onde funciona bem. Eu gostaria de apontar este executável para essas libs stub. Sim, eu poderia definir LD_LIBRARY_PATH, mas este executável é chamado de muitos scripts e muitos usuários e eu adoraria corrigi-lo em um local.

Não tenho fonte para isso e seria difícil obtê-la. Eu estava pensando - posso editar este arquivo, usando um editor compatível com ELF, e adicionar um PATH simples ao rpath para que ele chegue às novas bibliotecas? Isso é possível ou, depois de criar um binário ELF, você conserta as coisas em locais e elas não podem ser movidas?

Rich Homolka
fonte
3
Envolva-o em um shellscript que define LD_LIBRARY_PATH e chama o binário. Coloque o script de shell em um local que esteja no PATH do chamador.
wildplasser
LD_LIBRARY_PATH é herdado por processos filho. Você pode não querer isso.
Será
1
@will sim isso e eu já disse que não quero fazer isso. :)
Rich Homolka

Respostas:

78

Existe uma ferramenta chamada chrpathque pode fazer isso - provavelmente está disponível nos pacotes de sua distribuição.

TomH
fonte
9
Apenas uma observação para usuários de mac, install_name_toolpode fazer isso com o -rpathsinalizador
Kevin Tonon
10
Se receber o erro <binary>: no rpath or runpath tag found.chrpathpatchelfpatchelf --set-rpath /path/to/libaries <binary>
:,
Eu prefiro chrpath se possível, pois, embora seja mais universal, patchelf tem algum bug antigo que aumenta drasticamente o tamanho de suas bibliotecas / executáveis.
taranaki
156

Existe uma ferramenta mais universal do que a chrpathchamada patchelf. Ele foi originalmente criado para uso na criação de pacotes para Nix e NixOS (sistema de empacotamento e uma distribuição GNU / Linux).

Caso não haja rpath em um binário (aqui chamado de rdsamp), chrpathfalha:

chrpath -r '$ORIGIN/../lib64' rdsamp 
rdsamp: no rpath or runpath tag found.

Por outro lado,

patchelf --set-rpath '$ORIGIN/../lib64' rdsamp

consegue muito bem.

usuário7610
fonte
9
Especialmente, patchelfé capaz de adicionar um rpath a um binário que ainda não contém um rpath - onde chrpathapenas parece ser capaz de modificar uma entrada já presente.
maxschlepzig de
4
Como uma observação geral, vale a pena entender a distinção sutil entre rpathe runpath. Basicamente, um pode cancelar LD_LIBRARY_PATHe o outro não. Para obter detalhes, consulte blog.tremily.us/posts/rpath
Stuart Berg
6
O chato é que ambos chrpathe patchelfsão desleixados com sua terminologia. Por exemplo, o patchelfcomando mostrado acima mudará, runpathmas não a rpathmenos que você também forneça a --force-rpathopção.
Stuart Berg
10
@superbatfish Sim, mas a diferença geralmente não importa. Esta entrada do CHANGELOG de patchelfexplica: " --set-rpath, --shrink-rpathe --print-rpathagora preferem DT_RUNPATHmais DT_RPATH, que é obsoleto Quando a atualização, se ambos estiverem presentes, ambos são atualizados Se apenas DT_RPATH está presente, ele é convertido para.. DT_RUNPATHA não ser que --force-rpathseja especificado Se nenhum estiver presente. , a DT_RUNPATHé adicionado a menos que --force-rpathseja especificado, caso em que a DT_RPATHé adicionado. " O nome da opção provavelmente foi mantido inalterado por motivos de compatibilidade.
user7610
2
De longe a melhor resposta, mas esta deve ser a resposta aceita!
Kenneth Hoste
12

Assim como @ user7610 disse, o caminho certo é a patchelfferramenta.

Mas sinto que posso dar uma resposta mais abrangente, abrangendo todos os comandos necessários para fazer exatamente isso.

Para um artigo completo sobre o assunto, clique aqui

Em primeiro lugar, muitos desenvolvedores falam RPATH, mas eles realmente querem dizer RUNPATH. Essas são duas seções dinâmicas opcionais diferentes e o carregador as trata de maneiras muito diferentes. Você pode ler mais sobre a diferença entre eles no link que mencionei antes.

Por enquanto, lembre-se:

  • Se RUNPATHestiver definido, RPATHé ignorado
  • RPATH está obsoleto e deve ser evitado
  • RUNPATH é preferido porque pode ser substituído por LD_LIBRARY_PATH

Veja o R [UN] PATH atual

readelf -d <path-to-elf> | egrep "RPATH|RUNPATH"

Limpe o R [UN] PATH

patchelf --remove-rpath <path-to-elf>

Notas:

  • Remove ambos RPATHeRUNPATH

Adicione valores a R [UN] PATH

patchelf [--force-rpath] --set-rpath "<desired-rpath>" <path-to-elf>

Notas:

  • <desired-path> é uma lista de diretórios separados por vírgulas, por exemplo: /my/libs:/my/other/libs
  • Se você especificar --force-rpath, define RPATH, caso contrário, defineRUNPATH
Daniel Trugman
fonte
1
-Wl,-R,<desired-rpath> -Wl,--enable-new-dtagsconjuntos DT_RUNPATH, e é o que a maioria das pessoas deve usar. RUNPATHpode ser substituído por LD_LIBRARY_PATH, portanto, as pessoas não devem usar --force-rpath.
jww
@jww Vejo que não adicionei um comentário sobre a descontinuação do RPATH, então adicionei um agora. Obrigado!
Daniel Trugman
Observe que o exemplo <desired-path>usa dois-pontos; deve ser uma vírgula (ou seja:) /my/libs,/my/other/libs.
Alan De Smet
@AlanDeSmet, não sei sobre a vírgula, mas os dois pontos funcionam para mim.
Daniel Trugman
0

Isso funcionou para mim, substituindo XORIGIN por $ ORIGIN.

chrpath -r '\$\ORIGIN/../lib64' httpd

vikram kedlaya
fonte