Как определяется адрес текстовой части исполняемого файла PIE в Linux?

Сначала я попытался немного перепроектировать его:

printf '
#include <stdio.h>
int main() {
    puts("hello world");
}
' > main.c
gcc -std=c99 -pie -fpie -ggdb3 -o pie main.c
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
readelf -s ./pie | grep -E 'main$'
gdb -batch -nh \
  -ex 'set disable-randomization off' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  -ex 'set disable-randomization on' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  ./pie \
;

Выход:

64: 000000000000063a    23 FUNC    GLOBAL DEFAULT   14 main
Temporary breakpoint 1, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x5575f5fd263e <main+4> and ends at 0x5575f5fd264f <main+21>.
Temporary breakpoint 2 at 0x5575f5fd263e: file main.c, line 4.

Temporary breakpoint 2, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x55e3fbc9363e <main+4> and ends at 0x55e3fbc9364f <main+21>.
Temporary breakpoint 3 at 0x55e3fbc9363e: file main.c, line 4.

Temporary breakpoint 3, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x55555555463e <main+4> and ends at 0x55555555464f <main+21>.
Temporary breakpoint 4 at 0x55555555463e: file main.c, line 4.

Temporary breakpoint 4, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x55555555463e <main+4> and ends at 0x55555555464f <main+21>.

что указывает, что это 0x555555554000 + случайное смещение + 63e.

Но затем я попытался найти ядро ​​Linux и исходный код glibc для 555555554, но совпадений не было.

Какая часть кода вычисляет этот адрес?

Я столкнулся с этим, отвечая: Что такое параметр -fPIE для независимых от позиции исполняемых файлов в gcc и ld?


person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 14.07.2018    source источник
comment
У Базиля было такое же предположение в 2015 году: stackoverflow.com/a/29856596/196561 поскольку 0x55555555, кажется, появляется в исходный код ядра, но теперь я не могу найти 55555555 в ядре Linux   -  person osgx    schedule 15.07.2018


Ответы (1)


Некоторый интернет-поиск по адресу 0x555555554000 дает подсказки: были проблемы с ThreadSanitizer https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual

Q: Когда я запускаю программу, она говорит: FATAL: ThreadSanitizer не может mmap теневой памяти (что-то отображается по адресу 0x555555554000 ‹ 0x7cf000000000). Что делать? Вам нужно включить ASLR:

 $ echo 2 >/proc/sys/kernel/randomize_va_space

Это может быть исправлено в будущих ядрах, см. https://bugzilla.kernel.org/show_bug.cgi?id=66721 ...

 $ gdb -ex 'set disable-randomization off' --args ./a.out

и https://lwn.net/Articles/730120/ "Стабильные обновления ядра". Опубликовано 7 августа 2017 г. в 20:40 UTC (пн) пользователем hmh (subscriber) https://marc.info/?t=150213704600001&r=1&w=2 (https://patchwork.kernel.org/patch/9886105/, commit c715b72c1ba4)

Перемещение базы PIE x86_64 и arm64 с 0x555555554000 на 0x000100000000 сломало AddressSanitizer. Это частичный возврат:

Восстановленный код был:

b/arch/arm64/include/asm/elf.h
 /*
  * This is the base location for PIE (ET_DYN with INTERP) loads. On
- * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * 64-bit, this is above 4GB to leave the entire 32-bit address   * space open for things that want to use the area for 32-bit pointers.   */
-#define ELF_ET_DYN_BASE      0x100000000UL
+#define ELF_ET_DYN_BASE      (2 * TASK_SIZE_64 / 3)


+++ b/arch/x86/include/asm/elf.h
 /*
  * This is the base location for PIE (ET_DYN with INTERP) loads. On
- * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * 64-bit, this is above 4GB to leave the entire 32-bit address
  * space open for things that want to use the area for 32-bit pointers.
  */
 #define ELF_ET_DYN_BASE      (mmap_is_ia32() ? 0x000400000UL : \
-                       0x100000000UL)
+                       (TASK_SIZE / 3 * 2))

Итак, адрес 0x555555554000 связан с ELF_ET_DYN_BASEмакросом (упоминается в fs/binfmt_elf.c для ET_DYN как нерандомизированный load_bias ), а для x86_64 и arm64 это примерно 2/3 от TASK_SIZE. Когда CONFIG_X86_32 нет, x86_64 имеет TASK_SIZE 2^47 - одна страница в arch/x86/include/asm/processor.h

/*
 * User space process size. 47bits minus one guard page.  The guard
 * page is necessary on Intel CPUs: if a SYSCALL instruction is at
 * the highest possible canonical userspace address, then that
 * syscall will enter the kernel with a non-canonical return
 * address, and SYSRET will explode dangerously.  We avoid this
 * particular problem by preventing anything from being mapped
 * at the maximum canonical address.
 */
#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

Старые версии:

/*
 * User space process size. 47bits minus one guard page.
 */
#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

Более новые версии также поддерживают 5level с __VIRTUAL_MASK_SHIFT из 56 битv4.17/source/arch/x86/include/asm/processor.h (но не хочу использовать его до включения пользователем + commit b569bab78d8d ".. Не все пространство пользователя готово для обработки широких адресов")).

Итак, 0x555555554000 округляется в меньшую сторону (на load_bias = ELF_PAGESTART(load_bias - vaddr);, vaddr равно нулю) по формуле (2^47-1page)*(2/3) (или 2^56 для большие системы):

$ echo 'obase=16; (2^47-4096)/3*2'| bc -q
555555554AAA
$ echo 'obase=16; (2^56-4096)/3*2'| bc -q
AAAAAAAAAAA000

Немного истории 2/3 * TASK_SIZE:

Почти все арки определяют ELF_ET_DYN_BASE как 2/3 от TASK_SIZE. Хотя кажется, что некоторые архитектуры делают это неправильно. Проблема в том, что 2*TASK_SIZE может переполнить 32-битное значение, поэтому реальный ELF_ET_DYN_BASE станет неправильным. Исправьте это переполнение, разделив TASK_SIZE перед умножением: (TASK_SIZE / 3 * 2)

diff --git a/include/asm-i386/elf.h b/include/asm-i386/elf.h
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   the loader.  We need to make sure that it is out of the way of the program
+   that it will "exec", and that there is sufficient room for the brk.  */
+
+#define ELF_ET_DYN_BASE         (2 * TASK_SIZE / 3)
person osgx    schedule 14.07.2018