sobrescrever /lib64/ld-linux-x86-64.so.2 padrão para chamar executáveis

8

Preciso de uma Glibc mais recente do que o meu sistema fornece um binário que quero executar. Então, eu tenho um diretório /my_libscom a maioria dos arquivos lib de um Ubuntu recente, incluindo o novo Glibc ( libc.so.6).

Agora eu exportei LD_LIBRARY_PATH=/my_libs:$LD_LIBRARY_PATHe tentei executar o meu executável. Isso falhará com SEGFAULT. De fato, todo binário falhará agora com SEGFAULT (mesmo coisas simples como /bin/ls). Então eu acho que há alguma confusão entre diferentes bibliotecas /my_libse o meu sistema principal. Eu traçado com LD_DEBUG=libs(ou LD_DEBUG=all) e resolvi tudo, exceto de ld-linux-x86-64.so.2. Não importa o que eu fiz até agora, ele sempre usou /lib64/ld-linux-x86-64.so.2e não /my_libs/ld-linux-x86-64.so.2.

Existe uma maneira de definir o caminho ld-linux? Pelo que entendi, essa biblioteca, que também é um executável, é sempre usada para executar qualquer programa, e meu ambiente usa /lib64/ld-linux-x86-64.so.2.

Se eu executar diretamente /my_libs/ld-linux-x86-64.so.2 /bin/ls, funcionará. Também posso executar meu novo binário dessa maneira - só preciso ter certeza de que todas as bibliotecas são fornecidas /my_libsou de que as bibliotecas do sistema são compatíveis.

Então, o que posso fazer /bin/lsusando apenas a chamada direta /my_libs/ld-linux-x86-64.so.2?

Algumas discussões relacionadas estão aqui .

Observe que não quero corrigir /bin/lsou outros binários para fazer esse trabalho.

Albert
fonte

Respostas:

8

Tl: dr : Se você precisar continuar usando várias versões da libc , como muitos de nós, um utilitário importante a ser usado é o PatchElf .

Normalmente, se tudo o que você deseja é forçar sua própria versão do interpretador de programa ao criar seu próprio programa, tudo o que você precisa fazer é compilar com as seguintes opções:

gcc -Wl,-dynamic-linker,/my/lib/ld-linux.so.2 ...

Mas isso não funciona com utilitários de terceiros e com alguns objetos compartilhados que são no entanto executável por si mesmos, para que invocar patchelf em um determinado programa ELF como segue:

patchelf --set-interpreter /my/lib/my-ld-linux.so.2 someprogram

A razão pela qual não se trata apenas de editar com um editor hexadecimal o antigo executável e substituir o endereço antigo do intérprete pelo novo, é que os dois não precisam ter o mesmo tamanho; patchelf cuida de ampliar seu executável para você.

Você também pode alterar a variável rpath , da seguinte maneira:

patchelf --set-rpath /my_libs:$LD_LIBRARY_PATH someprogram 

Acho isso muito mais prático do que usar os invólucros comuns em torno de comandos com LD_LIBRARY_PATH .

Quanto ao execve , deve ser apenas a chamada de sistema mais fundamental no Unix: execve (filename) executa o nome do arquivo especificado. Por exemplo, seu shell executa um comando por meio de uma chamada da família execve : primeiro se bifurca e, em seguida, o processo filho gera o comando execve ( ls, cd ,, ... o nome dele). A maioria dos programas precisa de bibliotecas vinculadas dinamicamente, por exemplo, para ls :

$ ldd $(which ls)
    linux-vdso.so.1 =>  (0x00007ffe3b0e7000)
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f1423dda000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1423a11000)
    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f14237a0000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f142359c000)
    /lib64/ld-linux-x86-64.so.2 (0x0000563800043000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f142337f000)

Quando você carrega ls , é não o ponto de entrada normal de binário que é usado no início, mas ld-linux em vez disso: ele cuida de carregar todos os não resolvidos, as bibliotecas necessárias, e então transfere o controle para a aplicação real, ls neste caso. Faz isso executando o arquivo do programa.

Não sou capaz de dizer por que exatamente seu programa trava., Mas tentaria verificar se todos os programas invocados exigem o mesmo intérprete; você faz isso verificando a saída de:

    $ readelf -l / bin / ls

    O tipo de arquivo Elf é EXEC (arquivo executável)
    Ponto de entrada 0x4049a0
    Existem 9 cabeçalhos de programa, começando no deslocamento 64

    Cabeçalhos do programa:
      Tipo Offset VirtAddr PhysAddr
                     Alinhamento de sinalizadores FileSiz MemSiz
      PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
                     0x00000000000001f8 0x00000000000001f8 RE 8
      INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
                     0x000000000000001c 0x000000000000001c R 1
          [Solicitando intérprete de programa: /lib64/ld-linux-x86-64.so.2]
    ...........................

(a ênfase é minha ...).

EDIT : uma excelente discussão de um dos autores da chamada do sistema execveat pode ser encontrada aqui , e o código do kernel relevante está aqui . O código mostra que nenhuma variável do sistema é lida e a rotina load_elf_binary examina o interpretador de programa PT_INTERP .

MariusMatutiae
fonte
Não tenho acesso de gravação aos meus binários (como /bin/ls) e não acho que seja uma boa ideia corrigir os binários do sistema para fazer isso funcionar. Não é possível substituir o interpretador de programa solicitado por algum env var? Onde está o código relacionado no kernel do Linux, onde ele lê o interpretador de programa solicitado e o executa?
Albert
11
@ Albert Você não deve substituir os binários do sistema. Você deve copiar para um de seus diretórios aqueles de que precisa e modificar aqueles in situ , ou seja, a cópia em sua posse.
MariusMatutiae
Mas eu quero fazer isso para muitos binários do sistema. Não quero copiá-los em algum lugar em que possa corrigi-los. Não posso simplesmente colocá-los no lugar? Esp, minhas principais perguntas: Não é possível substituir apenas o intérprete de programa solicitado por algum env var? Onde está o código relacionado no kernel do Linux, onde ele lê o interpretador de programa solicitado e o executa?
Albert
@ Albert Não, você não pode usar uma variável de sistema para programas e algumas bibliotecas em formato binário, porque elas contêm as informações de INTERP acima, que são impermeáveis ​​às variáveis ​​do sistema. Se você quiser tentar um hack (eu nunca tentei). como o interpretador de programa (= runtime linker) ld-linux-x86-64.so.2 é um link simbólico para a versão específica do vinculador que agora está atual, você pode alterá-lo para apontar para o seu próprio intérprete de programa. YMMV. Quanto ao código do kernel, ele está aqui, lxr.free-electrons.com/source/fs/binfmt_elf.c?v=3.18
MariusMatutiae
Ah, obrigado pelo link. Eu acho que a função relevante é load_elf_binary, e depois a verificação p_type == PT_INTERP? Sim, pelo código que vejo, ele usará apenas o que está escrito no arquivo e não examinará nenhum env var. Você deve apontar isso na sua resposta. Para mim, isso significa que minha solução é realmente usar sempre o carregador ld-linux explicitamente (sem correção).
28416 Albert Albert