From 2271ae9c06581ac3dc02102fd0400cad6fe53142 Mon Sep 17 00:00:00 2001 From: Mis012 Date: Fri, 17 Jan 2025 19:54:36 +0100 Subject: [PATCH] main: ensure bionic-linked libraries get a non-changning stack guard value --- meson.build | 2 +- src/main-executable/bionic_compat.c | 160 ++++++++++++++++++++++++++++ src/main-executable/r_debug.c | 36 ------- 3 files changed, 161 insertions(+), 37 deletions(-) create mode 100644 src/main-executable/bionic_compat.c delete mode 100644 src/main-executable/r_debug.c diff --git a/meson.build b/meson.build index 29f1c65d..2f152a77 100644 --- a/meson.build +++ b/meson.build @@ -158,8 +158,8 @@ resources = gnome.compile_resources('com.gitlab.android_translation_layer.androi executable('android-translation-layer', [ 'src/main-executable/main.c', - 'src/main-executable/r_debug.c', 'src/main-executable/back_button.c', + 'src/main-executable/bionic_compat.c', resources ], install: true, diff --git a/src/main-executable/bionic_compat.c b/src/main-executable/bionic_compat.c new file mode 100644 index 00000000..63f56135 --- /dev/null +++ b/src/main-executable/bionic_compat.c @@ -0,0 +1,160 @@ +#include +#include + +/* for getting _r_debug out of the dynamic section */ +/* this is needed by the shim bionic linker to register stuff with gdb */ +#include +#include + +/* the dynamic section */ +extern ElfW(Dyn) _DYNAMIC[]; + +extern struct r_debug *_r_debug_ptr; +/* this has to be called from the main executable, since that's the only one guaranteed to have the debug section filled in */ +void init__r_debug() { +#if defined(_r_debug) +/* _r_debug is defined by glibc and is declared as extern in link.h*/ + _r_debug_ptr = &_r_debug; +#else + int i = 0; + ElfW(Dyn) current; + + do { + current = _DYNAMIC[i]; + if(current.d_tag == DT_DEBUG) { + _r_debug_ptr = (struct r_debug *)current.d_un.d_ptr; + break; + } + i++; + } while(current.d_tag != 0); + + if(!_r_debug_ptr) { + fprintf(stderr, "error: no DEBUG tag in the dynamic section, treating this as fatal\n"); + exit(1); + } +#endif +} + +/* bionic stores some things relative to the thread pointer that are actually part of the ABI. + * Some of these locations we can install our data at, some we cannot. Hopefully all the ones + * any app decided are ABI are in the former category (or are compatible with glibc/musl ABI) + * + * So far, we've seen apps using -fstack-protect, resulting in accesses to (tp + 5). + * this is what gnueabi programs do on x86(_64) as well, and on aarch64 we can control + * the stack guard value (mainly to make sure it stays constant) + */ +#if defined(__arm__) || defined(__aarch64__) +/* this is the **ONLY** thread-local variable in the main executable, which means it has a well-known placement relative to the thread pointer */ +_Thread_local uintptr_t TLS[] = { + /* these are occupied by musl/glibc internal structures */ + /* (tp - 3) = 0xXXXXXXXXXXXXXXXX */ // TLS_SLOT_STACK_MTE + /* (tp - 2) = 0xXXXXXXXXXXXXXXXX */ // TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE + /* (tp - 1) = 0xXXXXXXXXXXXXXXXX */ // TLS_SLOT_BIONIC_TLS + /* dtv (per spec) */ + /* (tp + 0) = 0xXXXXXXXXXXXXXXXX */ // TLS_SLOT_DTV + /* internal on glibc, seems to be unused on musl */ + /* (tp + 1) = 0xXXXXXXXXXXXXXXXX */ // TLS_SLOT_THREAD_ID + /* PT_TLS of main executable gets copied here (so this array!) */ + /* (tp + 2) = */ 0x5555555555555555, // TLS_SLOT_APP + /* (tp + 3) = */ 0x5555555555555555, // TLS_SLOT_OPENGL + /* (tp + 4) = */ 0x5555555555555555, // TLS_SLOT_OPENGL_API + /* (tp + 5) = */ 0x5555555555555555, // TLS_SLOT_STACK_GUARD + /* (tp + 6) = */ 0x5555555555555555, // TLS_SLOT_SANITIZER + /* (tp + 7) = */ 0x5555555555555555, // TLS_SLOT_ART_THREAD_SELF +}; +#elif defined(__i386__) || defined(__x86_64__) + /* + * PT_TLS goes before the thread pointer but bionic's slots go after + * how fucked are we? let's see what an app will access on glibc/musl if it decides + * to consider a particular slot a part of the platform ABI: + */ + + /* glibc (64bit): */ + /* + * typedef struct + * { + * void *tcb; // (tp + 0) TLS_SLOT_SELF + * + * dtv_t *dtv; // (tp + 1) TLS_SLOT_THREAD_ID + * void *self; // (tp + 2) TLS_SLOT_APP + * int multiple_threads; // (tp + 3) TLS_SLOT_OPENGL + * int gscope_flag; // [cont] + * uintptr_t sysinfo; // (tp + 4) TLS_SLOT_OPENGL_API + * // it's not a coincidence that this matches, it was only on arm that google decided + * // they might as well have an incompatible -fstack-protect ABI + * uintptr_t stack_guard; // (tp + 5) TLS_SLOT_STACK_GUARD + * uintptr_t pointer_guard; // (tp + 6) TLS_SLOT_SANITIZER + * unsigned long int unused_vgetcpu_cache[2]; // (tp + 7/8) TLS_SLOT_ART_THREAD_SELF/TLS_SLOT_DTV + * + * unsigned int feature_1; // (tp + 9) TLS_SLOT_BIONIC_TLS + * int __glibc_unused1; // [cont] + * void *__private_tm[4]; // (tp + 10) TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE + * void *__private_ss; + * unsigned long long int ssp_base; + * __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32))); + * + * void *__padding[8]; + * } tcbhead_t; + */ + /* glibc (32bit): */ + /* + * typedef struct + * { + * void *tcb; // (tp + 0) TLS_SLOT_SELF + * + * dtv_t *dtv; // (tp + 1) TLS_SLOT_THREAD_ID + * void *self; // (tp + 2) TLS_SLOT_APP + * int multiple_threads; // (tp + 3) TLS_SLOT_OPENGL + * uintptr_t sysinfo; // (tp + 4) TLS_SLOT_OPENGL_API + * // it's not a coincidence that this matches, it was only on arm that google decided + * // they might as well have an incompatible -fstack-protect ABI + * uintptr_t stack_guard; // (tp + 5) TLS_SLOT_STACK_GUARD + * uintptr_t pointer_guard; // (tp + 6) TLS_SLOT_SANITIZER + * int gscope_flag; // (tp + 7) TLS_SLOT_ART_THREAD_SELF + * + * unsigned int feature_1; // (tp + 8) TLS_SLOT_DTV + * void *__private_tm[3]; // (tp + 9/10) TLS_SLOT_BIONIC_TLS/TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE + * void *__private_ss; + * unsigned long ssp_base; + * } tcbhead_t; + */ + /* musl: */ + /* + * struct pthread { + * struct pthread *self; // (tp + 0) TLS_SLOT_SELF + * uintptr_t *dtv; // (tp + 1) TLS_SLOT_THREAD_ID + * struct pthread *prev, *next; // (tp + 2/3) TLS_SLOT_APP/TLS_SLOT_OPENGL + * uintptr_t sysinfo; // (tp + 4) TLS_SLOT_OPENGL_API + * // it's not a coincidence that this matches, it was only on arm that google decided + * // they might as well have an incompatible -fstack-protect ABI + * uintptr_t canary; // (tp + 5) TLS_SLOT_STACK_GUARD + * + * int tid; // (tp + 6) TLS_SLOT_SANITIZER + * int errno_val; // [cont] + * volatile int detach_state; // (tp + 7) TLS_SLOT_ART_THREAD_SELF + * volatile int cancel; // [cont] + * volatile unsigned char canceldisable, cancelasync; // (tp + 8) TLS_SLOT_DTV + * unsigned char tsd_used:1; // [cont] + * unsigned char dlerror_flag:1; // [cont] + * unsigned char *map_base; // (tp + 9) TLS_SLOT_BIONIC_TLS + * size_t map_size; // (tp + 10) TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE + * void *stack; + * size_t stack_size; + * size_t guard_size; + * void *result; + * struct __ptcb *cancelbuf; + * void **tsd; + * struct { + * volatile void *volatile head; + * long off; + * volatile void *volatile pending; + * } robust_list; + * int h_errno_val; + * volatile int timer_id; + * locale_t locale; + * volatile int killlock[1]; + * char *dlerror_buf; + * void *stdio_locks; + * }; + */ +#endif diff --git a/src/main-executable/r_debug.c b/src/main-executable/r_debug.c deleted file mode 100644 index ccba7d12..00000000 --- a/src/main-executable/r_debug.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include - -// for getting _r_debug out of the dynamic section -// this is needed by the shim bionic linker to register stuff with gdb -#include -#include - -// the dynamic section -extern ElfW(Dyn) _DYNAMIC[]; - -extern struct r_debug *_r_debug_ptr; -// this has to be called from the main executable, since that's the only one guaranteed to have the debug section filled in -void init__r_debug() { -#if defined(_r_debug) -// _r_debug is defined by glibc and is declared as extern in link.h - _r_debug_ptr = &_r_debug; -#else - int i = 0; - ElfW(Dyn) current; - - do { - current = _DYNAMIC[i]; - if(current.d_tag == DT_DEBUG) { - _r_debug_ptr = (struct r_debug *)current.d_un.d_ptr; - break; - } - i++; - } while(current.d_tag != 0); - - if(!_r_debug_ptr) { - fprintf(stderr, "error: no DEBUG tag in the dynamic section, treating this as fatal\n"); - exit(1); - } -#endif -}