|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <elf.h>
|
|
|
|
|
#include "pthread_impl.h"
|
|
|
|
|
#include "libc.h"
|
|
|
|
|
|
|
|
|
|
#define HWCAP_TLS (1 << 15)
|
|
|
|
|
|
|
|
|
|
extern const unsigned char __attribute__((__visibility__("hidden")))
|
|
|
|
|
__a_barrier_dummy[], __a_barrier_oldkuser[],
|
|
|
|
|
__a_barrier_v6[], __a_barrier_v7[],
|
|
|
|
|
__a_cas_dummy[], __a_cas_v6[], __a_cas_v7[],
|
fix __aeabi_read_tp oversight in arm atomics/tls overhaul
calls to __aeabi_read_tp may be generated by the compiler to access
TLS on pre-v6 targets. previously, this function was hard-coded to
call the kuser helper, which would crash on kernels with kuser helper
removed.
to fix the problem most efficiently, the definition of __aeabi_read_tp
is moved so that it's an alias for the new __a_gettp. however, on v7+
targets, code to initialize the runtime choice of thread-pointer
loading code is not even compiled, meaning that defining
__aeabi_read_tp would have caused an immediate crash due to using the
default implementation of __a_gettp with a HCF instruction.
fortunately there is an elegant solution which reduces overall code
size: putting the native thread-pointer loading instruction in the
default code path for __a_gettp, so that separate default/native code
paths are not needed. this function should never be called before
__set_thread_area anyway, and if it is called early on pre-v6
hardware, the old behavior (crashing) is maintained.
ideally __aeabi_read_tp would not be called at all on v7+ targets
anyway -- in fact, prior to the overhaul, the same problem existed,
but it was never caught by users building for v7+ with kuser disabled.
however, it's possible for calls to __aeabi_read_tp to end up in a v7+
binary if some of the object files were built for pre-v7 targets, e.g.
in the case of static libraries that were built separately, so this
case needs to be handled.
12 years ago
|
|
|
__a_gettp_dummy[];
|
|
|
|
|
|
|
|
|
|
#define __a_barrier_kuser 0xffff0fa0
|
|
|
|
|
#define __a_cas_kuser 0xffff0fc0
|
|
|
|
|
#define __a_gettp_kuser 0xffff0fe0
|
|
|
|
|
|
|
|
|
|
extern uintptr_t __attribute__((__visibility__("hidden")))
|
|
|
|
|
__a_barrier_ptr, __a_cas_ptr, __a_gettp_ptr;
|
|
|
|
|
|
|
|
|
|
#define SET(op,ver) (__a_##op##_ptr = \
|
|
|
|
|
(uintptr_t)__a_##op##_##ver - (uintptr_t)__a_##op##_dummy)
|
|
|
|
|
|
|
|
|
|
int __set_thread_area(void *p)
|
|
|
|
|
{
|
|
|
|
|
#if !__ARM_ARCH_7A__ && !__ARM_ARCH_7R__ && __ARM_ARCH < 7
|
|
|
|
|
if (__hwcap & HWCAP_TLS) {
|
|
|
|
|
size_t *aux;
|
|
|
|
|
SET(cas, v7);
|
|
|
|
|
SET(barrier, v7);
|
|
|
|
|
for (aux=libc.auxv; *aux; aux+=2) {
|
|
|
|
|
if (*aux != AT_PLATFORM) continue;
|
|
|
|
|
const char *s = (void *)aux[1];
|
|
|
|
|
if (s[0]!='v' || s[1]!='6' || s[2]-'0'<10u) break;
|
|
|
|
|
SET(cas, v6);
|
|
|
|
|
SET(barrier, v6);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int ver = *(int *)0xffff0ffc;
|
|
|
|
|
SET(gettp, kuser);
|
|
|
|
|
SET(cas, kuser);
|
|
|
|
|
SET(barrier, kuser);
|
|
|
|
|
if (ver < 2) a_crash();
|
|
|
|
|
if (ver < 3) SET(barrier, oldkuser);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return __syscall(0xf0005, p);
|
|
|
|
|
}
|