extern int _IO_cleanup (void) attribute_hidden;;
/* From dlfcn/dlerror.c */
extern void __libc_dlerror_result_free (void) attribute_hidden;
+/* From libc.so, arch specific. */
+#ifdef ARCH_THREAD_FREERES
+extern void ARCH_THREAD_FREERES (void) attribute_hidden;
+#endif
/* From either libc.so or libpthread.so */
extern void __libpthread_freeres (void) attribute_hidden;
void
__libc_thread_freeres (void)
{
+#ifdef ARCH_THREAD_FREERES
+ call_function_static_weak (ARCH_THREAD_FREERES);
+#endif
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_32)
__rpc_thread_destroy ();
#endif
#include <sysdep.h>
#include <stdarg.h>
#include <stdint.h>
+#include <stdlib.h>
#include <ucontext.h>
+#include <sys/mman.h>
#define GCS_MAGIC 0x47435300
return p;
}
+struct gcs_list {
+ struct gcs_list *next;
+ void *base;
+ size_t size;
+};
+
+static __thread struct gcs_list *gcs_list_head = NULL;
+
+static void
+record_gcs (void *base, size_t size)
+{
+ struct gcs_list *p = malloc (sizeof *p);
+ if (p == NULL)
+ abort ();
+ p->base = base;
+ p->size = size;
+ p->next = gcs_list_head;
+ gcs_list_head = p;
+}
+
+static void
+free_gcs_list (void)
+{
+ for (;;)
+ {
+ struct gcs_list *p = gcs_list_head;
+ if (p == NULL)
+ break;
+ gcs_list_head = p->next;
+ __munmap (p->base, p->size);
+ free (p);
+ }
+}
+
+/* Called during thread shutdown to free resources. */
+void
+__libc_aarch64_thread_freeres (void)
+{
+ free_gcs_list ();
+}
+
#ifndef __NR_map_shadow_stack
# define __NR_map_shadow_stack 453
#endif
if (base == (void *) -1)
/* ENOSYS, bad size or OOM. */
abort ();
+
+ record_gcs (base, size);
+
uint64_t *gcsp = (uint64_t *) ((char *) base + size);
/* Skip end of GCS token. */
gcsp--;
return gcsp + 1;
}
+void
+__free_makecontext_gcs (void *gcs)
+{
+ struct gcs_list *p = gcs_list_head;
+ struct gcs_list **q = &gcs_list_head;
+ for (;;)
+ {
+ if (p == NULL)
+ abort ();
+ if (gcs == p->base + p->size - 8)
+ break;
+ q = &p->next;
+ p = p->next;
+ }
+ *q = p->next;
+ __munmap (p->base, p->size);
+ free (p);
+}
+
/* makecontext sets up a stack and the registers for the
user context. The stack looks like this:
.text
ENTRY (__setcontext)
+ /* If x10 is set then old GCS is freed. */
+ mov x10, 0
+__setcontext_internal:
PTR_ARG (0)
/* Save a copy of UCP. */
mov x9, x0
ldr x3, [x2, #oGCSPR]
MRS_GCSPR (x2)
mov x4, x3
- /* x2: GCSPR now. x3, x4: target GCSPR. x5, x6: tmp regs. */
+ mov x1, x2
+ /* x1, x2: GCSPR now. x3, x4: target GCSPR. x5, x6: tmp regs. */
L(gcs_scan):
cmp x2, x4
b.eq L(gcs_pop)
GCSSS2 (xzr)
L(gcs_pop):
cmp x2, x3
- b.eq L(gcs_done)
+ b.eq L(gcs_free_old)
GCSPOPM (xzr)
add x2, x2, 8
b L(gcs_pop)
+L(gcs_free_old):
+ cbz x10, L(gcs_done)
+ mov x28, x0
+ mov x0, x1
+ bl __free_makecontext_gcs
+ mov x0, x28
+ ldp x28, x29, [x0, oX0 + 28 * SZREG]
+ ldr x30, [x0, oX0 + 30 * SZREG]
L(gcs_done):
2:
cfi_undefined (x30)
blr x20
mov x0, x19
- cbnz x0, __setcontext
+ mov x10, 1
+ cbnz x0, __setcontext_internal
1: b HIDDEN_JUMPTARGET (exit)
END (__startcontext)
#include <tls.h>
-/* In order to get __set_errno() definition in INLINE_SYSCALL. */
#ifndef __ASSEMBLER__
+/* Thread cleanup function. */
+#define ARCH_THREAD_FREERES __libc_aarch64_thread_freeres
+void __libc_aarch64_thread_freeres (void) attribute_hidden;
+
+/* In order to get __set_errno() definition in INLINE_SYSCALL. */
#include <errno.h>
#endif