mirror of
https://github.com/php/php-src.git
synced 2025-08-16 14:08:47 +02:00

We use linker relocations to fetch the TLS index and offset of _tsrm_ls_cache. When building Opcache statically, linkers may attempt to optimize that into a more efficient code sequence (relaxing from "General Dynamic" to "Local Exec" model [1]). Unfortunately, linkers will fail, rather than ignore our relocations, when they don't recognize the exact code sequence they are expecting. This results in errors as reported by GH-15074: TLS transition from R_X86_64_TLSGD to R_X86_64_GOTTPOFF against `_tsrm_ls_cache' at 0x12fc3 in section `.text' failed" Here I take a different approach: * Emit the exact full code sequence expected by linkers * Extract the TLS index/offset by inspecting the linked ASM code, rather than executing it (execution would give us the thread-local address). * We detect when the code was relaxed, in which case we can extract the TCB offset instead. * This is done in a conservative way so that if the linker did something we didn't expect, we fallback to a safer (but slower) mechanism. One additional benefit of that is we are now able to use the Local Exec model in more cases, in JIT'ed code. This makes non-glibc builds faster in these cases. Closes GH-18939. Related RFC: https://wiki.php.net/rfc/make_opcache_required. [1] https://www.akkadia.org/drepper/tls.pdf
48 lines
775 B
C
48 lines
775 B
C
#include <stdio.h>
|
|
#include <dlfcn.h>
|
|
|
|
#ifdef NO_SURPLUS
|
|
# include "def-vars.h"
|
|
DEF_VARS(main);
|
|
#endif
|
|
|
|
__thread int some_tls_var;
|
|
|
|
#ifndef DL_DECL
|
|
int decl(void);
|
|
#endif
|
|
|
|
int main(void) {
|
|
/* Ensure TLS vars are allocated */
|
|
some_tls_var = 1;
|
|
|
|
int (*decl_p)(void);
|
|
#ifdef DL_DECL
|
|
int flags = RTLD_LAZY | RTLD_GLOBAL;
|
|
# ifdef RTLD_DEEPBIND
|
|
flags |= RTLD_DEEPBIND;
|
|
# endif
|
|
void *handle = dlopen("./libdef.so", flags);
|
|
if (!handle) {
|
|
fprintf(stderr, "dlopen: %s\n", dlerror());
|
|
return 1;
|
|
}
|
|
|
|
decl_p = (int (*)(void)) dlsym(handle, "decl");
|
|
if (!decl_p) {
|
|
fprintf(stderr, "dlsym: %s\n", dlerror());
|
|
return 1;
|
|
}
|
|
#else
|
|
decl_p = decl;
|
|
#endif
|
|
|
|
int ret = decl_p();
|
|
if (!ret) {
|
|
fprintf(stderr, "FAIL\n");
|
|
} else {
|
|
fprintf(stderr, "OK\n");
|
|
}
|
|
|
|
return !ret;
|
|
}
|