Проверяя, если бинарный файл с "-статическим"

У меня есть двоичный файл в linux. Как я могу проверить, скомпилирован ли он "-статический" или нет?

Ответ 1

ldd /path/to/binary не следует перечислять какие-либо общие библиотеки, если бинарный файл статически компилируется.

Ответ 2

Вы также можете использовать команду fileobjdump также может быть полезно).

Ответ 3

Проверьте, имеет ли он заголовок программы типа INTERP

На нижнем уровне исполняемый файл является статическим, если у него нет заголовка программы с типом:

Elf32_Phd.p_type == PT_INTERP

Это упоминается в спецификации System V ABI.

Помните, что заголовки программ определяют сегменты ELF, включая сегменты типа PT_LOAD которые будут загружены в память и будут запущены.

Если этот заголовок присутствует, его содержимое точно соответствует пути динамического загрузчика.

readelf чеков

Мы можем наблюдать это с readelf. Сначала динамически скомпилируйте C hello world:

gcc -o main.out main.c

а потом:

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

выходы:

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

поэтому обратите внимание, что INTERP заголовок INTERP, и очень важно, чтобы readelf даже дал быстрый предварительный просмотр своего короткого 28 (0x1c) байтового содержимого: /lib64/ld-linux-x86-64.so.2, который является путем к динамический загрузчик (27 байт в длину + 1 для \0).

Обратите внимание, как это находится рядом с другими сегментами, включая, например, те, которые фактически загружаются в память, такие как: .text.

Затем мы можем более непосредственно извлечь эти байты без предварительного просмотра с помощью:

readelf -x .interp main.out

который дает:

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

как объяснено на: Как я могу проверить содержимое раздела данных файла ELF в Linux?

исходный код file

file 5.36 комментариев исходного кода в src/readelf.c утверждается, что он также проверяет PT_INTERP:

/*
 * Look through the program headers of an executable image, searching
 * for a PT_INTERP section; if one is found, it dynamically linked,
 * otherwise it 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";

найдено с помощью git grep statically из сообщения main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped.

Однако этот комментарий выглядит устаревшим по сравнению с кодом, который вместо этого проверяет PT_DYNAMIC:

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

Я не уверен, почему это сделано, и мне лень сейчас копаться в git log. В частности, это немного смутило меня, когда я попытался сделать статически связанный исполняемый файл PIE с --no-dynamic-linker как показано на: Как создать статически связанный независимый от позиции исполняемый файл ELF в Linux? который не имеет PT_INTERP но имеет PT_DYNAMIC, и который я не ожидаю использовать динамический загрузчик.

Исходный код ядра Linux

Ядро Linux 5.0 читает файл ELF во время системного вызова exec по адресу fs/binfmt_elf.c, как описано в разделе: Как ядро получает исполняемый двоичный файл, работающий под Linux?

Ядро перебирает заголовки программы в 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
             */

Я не прочитал код полностью, но я ожидал бы, что он использует динамический загрузчик, только если INTERP, иначе какой путь ему следует использовать?

PT_DYNAMIC не используется в этом файле.

Бонус: проверьте, использовался ли -pie

Я объяснил это подробно на: Почему GCC создает общий объект вместо исполняемого двоичного файла в соответствии с файлом?