1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix JIT TLS on MacOS

The dynamic loader, starting around version 1284, patches the thunk emitted for
thread local variables by the compiler, so that its format changes from

struct Thunk {
    void *func;
    size_t module;
    size_t offset;
}

to

struct Thunk_v2 {
     void *func;
     uint32_t module;
     uint32_t offset;
     // other fields
}

which has the same size, but not the same layout.

This is mentionned in
9307719dd8/libdyld/ThreadLocalVariables.h (L90)

As a result, access to thread specific variables in JIT is broken.

Fix by using the new layout when the new dynamic loader is in use.

Closes GH-20121
This commit is contained in:
Arnaud Le Blanc
2025-10-13 12:57:16 +02:00
parent c89f25bf69
commit 3abebf3e31
3 changed files with 42 additions and 4 deletions

2
NEWS
View File

@@ -18,6 +18,8 @@ PHP NEWS
- Opcache:
. Fixed bug GH-20081 (access to uninitialized vars in preload_load()).
(Arnaud)
. Fixed bug GH-20121 (JIT broken in ZTS builds on MacOS 15).
(Arnaud, Shivam Mathur)
- Phar:
. Fix memory leak of argument in webPhar. (nielsdos)

View File

@@ -51,6 +51,10 @@
#include <pthread.h>
#endif
#if defined(__APPLE__) && defined(__x86_64__)
# include <mach-o/dyld.h>
#endif
#ifdef ZTS
int jit_globals_id;
#else

View File

@@ -2826,6 +2826,25 @@ static zend_never_inline void zend_jit_set_sp_adj_vm(void)
}
#endif
#if defined(__APPLE__) && defined(__x86_64__)
/* Thunk format used since dydl 1284 (approx. MacOS 15)
* https://github.com/apple-oss-distributions/dyld/blob/9307719dd8dc9b385daa412b03cfceb897b2b398/libdyld/ThreadLocalVariables.h#L146 */
struct TLV_Thunkv2
{
void* func;
uint32_t key;
uint32_t offset;
};
/* Thunk format used in earlier versions */
struct TLV_Thunkv1
{
void* func;
size_t key;
size_t offset;
};
#endif
static int zend_jit_setup(void)
{
if (!zend_cpu_supports_sse2()) {
@@ -2889,12 +2908,25 @@ static int zend_jit_setup(void)
# elif defined(__APPLE__) && defined(__x86_64__)
tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();
if (tsrm_ls_cache_tcb_offset == 0) {
size_t *ti;
struct TLV_Thunkv2 *thunk;
__asm__(
"leaq __tsrm_ls_cache(%%rip),%0"
: "=r" (ti));
tsrm_tls_offset = ti[2];
tsrm_tls_index = ti[1] * 8;
: "=r" (thunk));
/* Detect dyld 1284: With dyld 1284, thunk->func will be _tlv_get_addr.
* Unfortunately this symbol is private, but we can find it
* as _tlv_bootstrap+8: https://github.com/apple-oss-distributions/dyld/blob/9307719dd8dc9b385daa412b03cfceb897b2b398/libdyld/threadLocalHelpers.s#L54
* In earlier versions, thunk->func will be tlv_get_addr, which is not
* _tlv_bootstrap+8.
*/
if (thunk->func == (void*)((char*)_tlv_bootstrap + 8)) {
tsrm_tls_offset = thunk->offset;
tsrm_tls_index = (size_t)thunk->key * 8;
} else {
struct TLV_Thunkv1 *thunkv1 = (struct TLV_Thunkv1*) thunk;
tsrm_tls_offset = thunkv1->offset;
tsrm_tls_index = thunkv1->key * 8;
}
}
# elif defined(__GNUC__) && defined(__x86_64__)
tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset();