Ответ 1
ldd /path/to/binary
не следует перечислять какие-либо общие библиотеки, если бинарный файл статически компилируется.
У меня есть двоичный файл в linux. Как я могу проверить, скомпилирован ли он "-статический" или нет?
ldd /path/to/binary
не следует перечислять какие-либо общие библиотеки, если бинарный файл статически компилируется.
Вы также можете использовать команду file
(и objdump
также может быть полезно).
Проверьте, имеет ли он заголовок программы типа 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 создает общий объект вместо исполняемого двоичного файла в соответствии с файлом?