X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=src%2Frseq.c;h=ae72eaa22f556b64a0d763eaa179e9e4960c0162;hb=c0de0012017aff7b1f310b9e34bac2d842d46a2b;hp=e904b443e1212fc63b95da1ada172e50db241612;hpb=baa98a34eddcc4a09895aad6edb1af483e4164b7;p=librseq.git diff --git a/src/rseq.c b/src/rseq.c index e904b44..ae72eaa 100644 --- a/src/rseq.c +++ b/src/rseq.c @@ -21,6 +21,7 @@ #include #include +#include "smp.h" #ifndef AT_RSEQ_FEATURE_SIZE # define AT_RSEQ_FEATURE_SIZE 27 @@ -70,6 +71,12 @@ static int rseq_reg_success; /* At least one rseq registration has succeded. */ /* Original struct rseq allocation size is 32 bytes. */ #define ORIG_RSEQ_ALLOC_SIZE 32 +/* + * The alignment on RSEQ_THREAD_AREA_ALLOC_SIZE guarantees that the + * rseq_abi structure allocated size is at least + * RSEQ_THREAD_AREA_ALLOC_SIZE bytes to hold extra space for yet unknown + * kernel rseq extensions. + */ static __thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"), aligned(RSEQ_THREAD_AREA_ALLOC_SIZE))) = { .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED, @@ -167,16 +174,31 @@ unsigned int get_rseq_feature_size(void) return ORIG_RSEQ_FEATURE_SIZE; } +/* + * Initialize the public symbols for the rseq offset, size, feature size and + * flags prior to registering threads. If glibc owns the registration, get the + * values from its public symbols. + */ static void rseq_init(void) { + /* Ensure initialization is only done once. */ if (RSEQ_READ_ONCE(init_done)) return; + /* + * Take the mutex, check the initialization flag again and atomically + * set it to ensure we are the only thread doing the initialization. + */ pthread_mutex_lock(&init_lock); if (init_done) goto unlock; RSEQ_WRITE_ONCE(init_done, 1); + + /* + * Check for glibc rseq support, if the 3 public symbols are found and + * the rseq_size is not zero, glibc owns the registration. + */ libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset"); libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size"); libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags"); @@ -187,18 +209,42 @@ void rseq_init(void) rseq_size = *libc_rseq_size_p; rseq_flags = *libc_rseq_flags_p; rseq_feature_size = get_rseq_feature_size(); + + /* + * The registered rseq area could be smaller than the feature + * size reported by the kernel auxval. Cap it to the rseq size + * so we don't try to access features past the end of the rseq + * area. + */ if (rseq_feature_size > rseq_size) rseq_feature_size = rseq_size; goto unlock; } + + /* librseq owns the registration */ rseq_ownership = 1; + + /* Calculate the offset of the rseq area from the thread pointer. */ + rseq_offset = (uintptr_t)&__rseq_abi - (uintptr_t)rseq_thread_pointer(); + + /* rseq flags are deprecated, always set to 0. */ + rseq_flags = 0; + + /* + * Check if the rseq syscall is available, if not set the size and + * feature_size to 0. + */ if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { rseq_size = 0; rseq_feature_size = 0; goto unlock; } - rseq_offset = (uintptr_t)&__rseq_abi - (uintptr_t)rseq_thread_pointer(); - rseq_flags = 0; + + /* + * If the feature size matches the original ABI (20), set the size to + * match the original ABI allocation (32), otherwise use the allocated + * size. + */ rseq_feature_size = get_rseq_feature_size(); if (rseq_feature_size == ORIG_RSEQ_FEATURE_SIZE) rseq_size = ORIG_RSEQ_ALLOC_SIZE; @@ -243,3 +289,8 @@ int32_t rseq_fallback_current_node(void) } return (int32_t) node_id; } + +int rseq_get_max_nr_cpus(void) +{ + return get_possible_cpus_array_len(); +}