Здесь есть две разные вещи:
- Запрос интерпретатора ELF (ld.so) или нет.
Как и #!/bin/sh
, но для двоичных файлов выполняется до вашего _start
.
В этом разница между stati c и dynamici c исполняемым . - Список динамически связанных библиотек для загрузки ld.so оказывается пустым.
Это, очевидно, то, что ldd
называет "статически связанными", т. Е. Что любые библиотеки, которые вы могли связать во время сборки, были stati c библиотеками.
Другими инструментами, такими как file
и readelf
дает больше информации и использует терминологию, которая соответствует ожидаемой.
Ваш G CC настроен на , поэтому -pie
является значением по умолчанию и g cc не делает ставку c -p ie для особого случая отсутствия динамических c библиотек.
gcc -nostdlib
просто делает P IE, который не ссылка на любые библиотеки, но в остальном идентична обычной P IE, с указанием интерпретатора ELF.
ldd
вводит в заблуждение это «статически связано».
file
: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2 ...
gcc -nostdlib -static
переопределяет значение по умолчанию -pie
и делает истинный исполняемый файл c.
file
: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked ...
gcc -nostdlib -no-pie
также решает сделать исполняемый файл stati c в качестве оптимизации для случая, когда вообще нет динамических библиотек c. Так как не-P IE исполняемый файл в любом случае не мог быть ASLRed, это имеет смысл. Байт за байтом, идентичный регистру -static
. gcc -nostdlib -static-pie
делает исполняемый файл ASLRable без интерпретатора ELF. G CC не делает этого по умолчанию для gcc -pie -nostdlib
, в отличие от случая no-p ie, когда он выбирает обход в сторону ld.so
, когда не задействованы динамически связанные библиотеки.
file
: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked ...
-static-pie
неясен, редко используется, и более старый file
не идентифицирует его как статически связанный.
-nostdlib
не подразумевает -no-pie
или -static
, и -static-pie
должен быть явно указан для получения этого.
gcc -static-pie
вызывает ld -static -pie
, поэтому ld
должен знать, что это значит. В отличие от случая, отличного от P IE, когда вам не нужно явно запрашивать динамический c исполняемый файл, вы просто получите его, если передадите ld
любую .so
библиотеку. Я думаю, именно поэтому вы случайно получаете исполняемый файл c от gcc -nostdlib -no-pie
- G CC не должен делать ничего особенного, он просто ld
делает эту оптимизацию.
Но ld
не включает -static
неявным образом, когда указано -pie
, даже когда нет общих библиотек для ссылки.
Подробности
Примеры, сгенерированные с помощью gcc --version
g cc (Arch Linux 9.3.0-1) 9.3.0
ld --version
GNU ld (GNU Binutils) 2,34 (также readelf is binutils)
ldd --version
ldd (GNU lib c) 2,31
file --version
file-5.38 - обратите внимание, что обнаружение stati c -p ie изменилось в последних патчах, когда Ubuntu cherry выбирает невыпущенный патч. (Спасибо @Joseph за детективную работу) - это в 2019 обнаружено Dynami c = наличие PT_INTERP для обработки stati c -p ie, но оно было возвращено определять на основе PT_DYNAMI C, поэтому общие библиотеки считаются dynamic
. ошибка Debian # 948269 . static-pie
является малопонятной редко используемой функцией.
G CC заканчивается выполнением ld -pie exit.o
с указанным динамическим c компоновщиком пути и без библиотек. (И множество других опций для поддержки возможной оптимизации времени соединения LTO, но ключи здесь -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie
. collect2
- это просто оболочка вокруг ld
.)
$ gcc -nostdlib exit.s -v # output manually line wrapped with \ for readability
...
COLLECT_GCC_OPTIONS='-nostdlib' '-v' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/collect2 \
-plugin /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper \
-plugin-opt=-fresolution=/tmp/ccoNx1IR.res \
--build-id --eh-frame-hdr --hash-style=gnu \
-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie \
-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0 \
-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../lib -L/lib/../lib \
-L/usr/lib/../lib \
-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../.. \
/tmp/cctm2fSS.o
Вы получаете Dynami c P IE без каких-либо зависимостей от других библиотек. Запуск его по-прежнему вызывает «ELF-интерпретатор» /lib64/ld-linux-x86-64.so.2
, который запускается перед тем, как перейти к вашему _start
. (Хотя ядро уже сопоставило сегменты ELF исполняемого файла с виртуальными адресами ASLR вместе с текстом / data / bss ld.so).
file
и readelf являются более информативными.
P IE non-stati c, исполняемый с gcc -nostdlib
$ gcc -nostdlib exit.s -o exit-default
$ ls -l exit-default
-rwxr-xr-x 1 peter peter 13536 May 2 02:15 exit-default
$ ldd exit-default
statically linked
$ file exit-default
exit-default: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=05a4d1bdbc94d6f91cca1c9c26314e1aa227a3a5, not stripped
$ readelf -a exit-default
...
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1000
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R 0x8
INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000002b1 0x00000000000002b1 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000009 0x0000000000000009 R E 0x1000
... (the Read+Exec segment to be mapped at virt addr 0x1000 is where your text section was linked.)
Если вы выпустите его, вы также увидите различия:
$ gcc -nostdlib exit.s -o exit-default
$ strace ./exit-default
execve("./exit-default", ["./exit-default"], 0x7ffe1f526040 /* 51 vars */) = 0
brk(NULL) = 0x5617eb1e4000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcea703380) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9ff5b3e000
arch_prctl(ARCH_SET_FS, 0x7f9ff5b3ea80) = 0
mprotect(0x5617eabac000, 4096, PROT_READ) = 0
exit(0) = ?
+++ exited with 0 +++
против. -static
и -static-pie
первая инструкция, выполняемая в пространстве пользователя, - это ваша _start
(которую вы также можете проверить с помощью GDB, используя starti
).
$ strace ./exit-static-pie
execve("./exit-static-pie", ["./exit-static-pie"], 0x7ffcdac96dd0 /* 51 vars */) = 0
exit(0) = ?
+++ exited with 0 +++
gcc -nostdlib -static-pie
$ gcc -nostdlib -static-pie exit.s -o exit-static-pie
$ ls -l exit-static-pie
-rwxr-xr-x 1 peter peter 13440 May 2 02:18 exit-static-pie
peter@volta:/tmp$ ldd exit-static-pie
statically linked
peter@volta:/tmp$ file exit-static-pie
exit-static-pie: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=daeb4a8f11bec1bb1aaa13cd48d24b5795af638e, not stripped
$ readelf -a exit-static-pie
...
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1000
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000229 0x0000000000000229 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000009 0x0000000000000009 R E 0x1000
... (no Interp header, but still a read+exec text segment)
Обратите внимание, что адреса по-прежнему относительно базы изображений, оставляя ASLR вплоть до ядра.
Удивительно, но ldd
не говорит, что это не динамически c исполняемый файл. Это может быть ошибка или побочный эффект некоторых деталей реализации.
gcc -nostdlib -static
традиционный не P IE old-school stati c исполняемый файл
$ gcc -nostdlib -static exit.s -o exit-static
$ ls -l exit-static
-rwxr-xr-x 1 peter peter 4744 May 2 02:26 exit-static
peter@volta:/tmp$ ldd exit-static
not a dynamic executable
peter@volta:/tmp$ file exit-static
exit-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1b03e3d05709b7288fe3006b4696fd0c11fb1cb2, not stripped
peter@volta:/tmp$ readelf -a exit-static
ELF Header:
...
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x401000
... (Note the absolute entry-point address nailed down at link time)
(And that the ELF type is EXEC, not DYN)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000010c 0x000000000000010c R 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x0000000000000009 0x0000000000000009 R E 0x1000
NOTE 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
0x0000000000000024 0x0000000000000024 R 0x4
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id
01 .text
02 .note.gnu.build-id
...
Это все заголовки программы; в отличие от p ie / stati c -p ie Я не пропускаю ничего, только другие целые части вывода readelf -a
.
Также обратите внимание на абсолютные виртуальные адреса в программе Заголовки, которые не дают ядру выбора, где в виртуальном адресном пространстве отображать файл. В этом разница между типами ELF типов EXE C и DYN. Исполняемые файлы P IE являются общими объектами с точкой входа, что позволяет нам получить ASLR для основного исполняемого файла. Фактические исполняемые файлы EXE C имеют макет памяти, выбранный во время компоновки.
ldd
, по-видимому, только сообщает "не исполняемый файл Dynami c", когда оба:
- нет интерпретатора ELF (динамический c компоновщик) путь
- тип ELF = EXE C