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

5

Eu preciso de um Glibc mais recente do que o meu sistema fornece para um binário que eu quero rodar. Então eu tenho um diretório /my_libs com 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_PATH e tento executar meu executável. Isso irá falhar com o SEGFAULT. De fato, todo binário irá falhar agora com o SEGFAULT (mesmo coisas simples como /bin/ls ). Então, eu acho que algumas misturas entre bibliotecas diferentes de /my_libs e meu sistema principal. Rastrei com LD_DEBUG=libs (ou LD_DEBUG=all ) e resolvi tudo, exceto ld-linux-x86-64.so.2 . Não importa o que eu fiz até agora, sempre usei /lib64/ld-linux-x86-64.so.2 e não /my_libs/ld-linux-x86-64.so.2 .

Existe uma maneira de definir o caminho para 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 , isso funcionará. Eu também posso executar meu novo binário dessa maneira - eu só tenho que ter certeza que todas as libs são fornecidas em /my_libs , ou que as libs do sistema são compatíveis.

Então, o que posso fazer que apenas chamar /bin/ls diretamente usará /my_libs/ld-linux-x86-64.so.2 ?

Algumas discussões relacionadas são aqui .

Note que não quero corrigir /bin/ls ou outros binários para que isso funcione.

    
por Albert 11.11.2016 / 15:33

1 resposta

6

Tl: dr : Se você precisar continuar usando várias versões do libc , como muitos de nós, então um utilitário chave para usar é PatchElf .

Normalmente, se tudo o que você quer é forçar sua própria versão do interpretador de programas 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, no entanto, são executáveis por si sós, portanto, você invoca patchelf em um determinado programa ELF da seguinte forma:

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

A razão pela qual isso não é apenas uma questão de editar com um editor hex o antigo executável e sobrescrever o antigo endereço do intérprete com o novo, é que os dois não precisam ter o mesmo comprimento; patchelf cuida de ampliar seu executável para você.

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

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 a execve , ele deve ser praticamente a chamada mais fundamental do sistema no Unix: execve (filename) executa o nome do arquivo dado. Por exemplo, seu shell executa um comando por meio de uma chamada da família execve : primeiro forks, então o processo filho gerou execve o comando ( > 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 binário que é usado inicialmente, mas ld-linux em vez disso: cuida de carregar todas as bibliotecas necessárias não resolvidas e, em seguida, transfere o controle para o aplicativo real, ls nesse caso. Isso é feito por execve usando o arquivo do programa.

Eu sou incapaz de dizer por que exatamente o seu programa trava, mas eu tentaria verificar se todos os programas invocados requerem o mesmo interpretador de programa; você faz isso verificando a saída de:

    $ readelf -l /bin/ls

    Elf file type is EXEC (Executable file)
    Entry point 0x4049a0
    There are 9 program headers, starting at offset 64

    Program Headers:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                     0x00000000000001f8 0x00000000000001f8  R E    8
      INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                     0x000000000000001c 0x000000000000001c  R      1
          [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
    ...........................

(a ênfase é minha ...).

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

    
por 28.12.2016 / 11:08

Tags