GNU/Linux >> Linux Esercitazione >  >> Linux

controllando se un binario è stato compilato con -static

Puoi anche usare il file comando (e objdump potrebbe anche essere utile).


Controlla se ha un'intestazione di programma di tipo INTERP

Al livello inferiore, un eseguibile è statico se non ha un'intestazione di programma con tipo:

Elf32_Phd.p_type == PT_INTERP

Questo è menzionato nelle specifiche ABI di System V.

Ricorda che le intestazioni di programma determinano i segmenti ELF, inclusi quelli di tipo PT_LOAD che verrà caricato in memoria ed eseguito.

Se quell'intestazione è presente, i suoi contenuti sono esattamente il percorso del caricatore dinamico.

readelf controlla

Possiamo osservarlo con readelf . Per prima cosa compila dinamicamente un C hello world:

gcc -o main.out main.c

e poi:

readelf --program-headers --wide main.out

uscite:

Elf file type is DYN (Shared object file)
Entry point 0x1050
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R   0x8
  INTERP         0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000560 0x000560 R   0x1000
  LOAD           0x001000 0x0000000000001000 0x0000000000001000 0x0001bd 0x0001bd R E 0x1000
  LOAD           0x002000 0x0000000000002000 0x0000000000002000 0x000150 0x000150 R   0x1000
  LOAD           0x002db8 0x0000000000003db8 0x0000000000003db8 0x000258 0x000260 RW  0x1000
  DYNAMIC        0x002dc8 0x0000000000003dc8 0x0000000000003dc8 0x0001f0 0x0001f0 RW  0x8
  NOTE           0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x00200c 0x000000000000200c 0x000000000000200c 0x00003c 0x00003c R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x002db8 0x0000000000003db8 0x0000000000003db8 0x000248 0x000248 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   06     .dynamic
   07     .note.ABI-tag .note.gnu.build-id
   08     .eh_frame_hdr
   09
   10     .init_array .fini_array .dynamic .got

quindi nota il INTERP header c'è, ed è così importante che readelf ha anche fornito una rapida anteprima del suo breve contenuto di 28 (0x1c) byte:/lib64/ld-linux-x86-64.so.2 , che è il percorso del caricatore dinamico (lungo 27 byte + 1 per \0 ).

Nota come questo risieda fianco a fianco con gli altri segmenti, inclusi ad es. quelli che effettivamente vengono caricati in memoria come:.text .

Possiamo quindi estrarre più direttamente quei byte senza l'anteprima con:

readelf -x .interp main.out

che dà:

Hex dump of section '.interp':
  0x000002a8 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux-
  0x000002b8 7838362d 36342e73 6f2e3200          x86-64.so.2.

come spiegato in:Come posso esaminare i contenuti di una sezione dati di un file ELF su Linux?

file codice sorgente

file I commenti del codice sorgente 5.36 su src/readelf.c affermano che controlla anche PT_INTERP :

/*
 * Look through the program headers of an executable image, searching
 * for a PT_INTERP section; if one is found, it's dynamically linked,
 * otherwise it's statically linked.
 */
private int
dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
    int num, size_t size, off_t fsize, int sh_num, int *flags,
    uint16_t *notecount)
{
    Elf32_Phdr ph32;
    Elf64_Phdr ph64;
    const char *linking_style = "statically";

trovato con git grep statically dal messaggio main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped .

Tuttavia, questo commento sembra essere obsoleto rispetto al codice, che invece controlla PT_DYNAMIC :

    case PT_DYNAMIC:
        linking_style = "dynamically";
        doread = 1;
        break;

Non sono sicuro del motivo per cui è stato fatto e sono pigro per scavare in git log adesso. In particolare, questo mi ha confuso un po' quando ho provato a rendere eseguibile un PIE linkato staticamente con --no-dynamic-linker come mostrato in:Come creare un ELF eseguibile indipendente dalla posizione collegato staticamente in Linux? che non ha PT_INTERP ma ha PT_DYNAMIC , e che non mi aspetto di utilizzare il caricatore dinamico.

Ho finito per fare un'analisi più approfondita della fonte per -fPIE at:Perché GCC crea un oggetto condiviso invece di un binario eseguibile in base al file? la risposta è probabilmente anche lì.

Codice sorgente del kernel Linux

Il kernel Linux 5.0 legge il file ELF durante la chiamata di sistema exec in fs/binfmt_elf.c come spiegato in:In che modo il kernel ottiene un file binario eseguibile in esecuzione in Linux?

Il kernel esegue un ciclo sulle intestazioni del programma in load_elf_binary

    for (i = 0; i < loc->elf_ex.e_phnum; i++) {
        if (elf_ppnt->p_type == PT_INTERP) {
            /* This is the program interpreter used for
             * shared libraries - for now assume that this
             * is an a.out format binary
             */

Non ho letto completamente il codice, ma mi aspetto che utilizzi solo il caricatore dinamico se INTERP viene trovato, altrimenti quale percorso dovrebbe usare?

PT_DYNAMIC non è utilizzato in quel file.

Bonus:controlla se -pie è stato utilizzato

L'ho spiegato in dettaglio in:Perché GCC crea un oggetto condiviso invece di un binario eseguibile in base al file?


ldd /path/to/binary non dovrebbe elencare alcuna libreria condivisa se il file binario è compilato staticamente.


Linux
  1. Non è possibile compilare un semplice programma di thread C++?

  2. Carica file come ASCII o binari con FTP

  3. Come creare thread in Linux (con un programma di esempio C)

  4. Il mio bootloader non può essere compilato con gcc 4.6 e 4.7 ... solo 4.5

  5. apri un file con il programma predefinito in node-webkit

Bash For Loop con esempi pratici

Gestisci macchine virtuali KVM con il programma Virsh

Posso cambiare 'rpath' in un binario già compilato?

Linux:chiudi un programma con la riga di comando (non uccidilo)

Esecuzione di uno script bash o di un binario c su un file system con l'opzione noexec

Come configuro monit per avviare un processo con un utente specifico?