gcc/libgcc/config/aarch64/heap-trampoline.c
Sergey Bugaev 46c91665f4 libgcc: Add basic support for aarch64-gnu (GNU/Hurd on AArch64)
There is currently no unwinding implementation.

libgcc/ChangeLog:

	* config.host: Recognize aarch64*-*-gnu* hosts.
	* config/aarch64/gnu-unwind.h: New file.
	* config/aarch64/heap-trampoline.c
	(allocate_trampoline_page): Support GNU/Hurd.

Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
2024-04-09 09:23:33 +02:00

186 lines
4.5 KiB
C

/* Copyright The GNU Toolchain Authors. */
/* libc is required to allocate trampolines. */
#ifndef inhibit_libc
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if __APPLE__
/* For pthread_jit_write_protect_np */
#include <pthread.h>
#endif
/* HEAP_T_ATTR is provided to allow targets to build the exported functions
as weak definitions. */
#ifndef HEAP_T_ATTR
# define HEAP_T_ATTR
#endif
void *allocate_trampoline_page (void);
int get_trampolines_per_page (void);
struct tramp_ctrl_data *allocate_tramp_ctrl (struct tramp_ctrl_data *parent);
void *allocate_trampoline_page (void);
void __gcc_nested_func_ptr_created (void *chain, void *func, void *dst);
void __gcc_nested_func_ptr_deleted (void);
#if defined(__linux__) || defined(__gnu_hurd__)
static const unsigned char aarch64_trampoline_insns[6][4] = {
{0x5f, 0x24, 0x03, 0xd5}, /* hint 34 */
{0xb1, 0x00, 0x00, 0x58}, /* ldr x17, .+20 */
{0xd2, 0x00, 0x00, 0x58}, /* ldr x18, .+24 */
{0x20, 0x02, 0x1f, 0xd6}, /* br x17 */
{0x9f, 0x3f, 0x03, 0xd5}, /* dsb sy */
{0xdf, 0x3f, 0x03, 0xd5} /* isb */
};
#elif __APPLE__
static const unsigned char aarch64_trampoline_insns[6][4] = {
{0x5f, 0x24, 0x03, 0xd5}, /* hint 34 */
{0xb1, 0x00, 0x00, 0x58}, /* ldr x17, .+20 */
{0xd0, 0x00, 0x00, 0x58}, /* ldr x16, .+24 */
{0x20, 0x02, 0x1f, 0xd6}, /* br x17 */
{0x9f, 0x3f, 0x03, 0xd5}, /* dsb sy */
{0xdf, 0x3f, 0x03, 0xd5} /* isb */
};
#else
#error "Unsupported AArch64 platform for heap trampolines"
#endif
struct aarch64_trampoline {
unsigned char insns[6][4];
void *func_ptr;
void *chain_ptr;
};
struct tramp_ctrl_data
{
struct tramp_ctrl_data *prev;
int free_trampolines;
/* This will be pointing to an executable mmap'ed page. */
struct aarch64_trampoline *trampolines;
};
int
get_trampolines_per_page (void)
{
return getpagesize() / sizeof(struct aarch64_trampoline);
}
static _Thread_local struct tramp_ctrl_data *tramp_ctrl_curr = NULL;
void *
allocate_trampoline_page (void)
{
void *page;
#if defined(__linux__) || defined(__gnu_hurd__)
page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE, 0, 0);
#elif __APPLE__
page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE | MAP_JIT, 0, 0);
#else
page = MAP_FAILED;
#endif
return page;
}
struct tramp_ctrl_data *
allocate_tramp_ctrl (struct tramp_ctrl_data *parent)
{
struct tramp_ctrl_data *p = malloc (sizeof (struct tramp_ctrl_data));
if (p == NULL)
return NULL;
p->trampolines = allocate_trampoline_page ();
if (p->trampolines == MAP_FAILED)
return NULL;
p->prev = parent;
p->free_trampolines = get_trampolines_per_page();
return p;
}
HEAP_T_ATTR
void
__gcc_nested_func_ptr_created (void *chain, void *func, void *dst)
{
if (tramp_ctrl_curr == NULL)
{
tramp_ctrl_curr = allocate_tramp_ctrl (NULL);
if (tramp_ctrl_curr == NULL)
abort ();
}
if (tramp_ctrl_curr->free_trampolines == 0)
{
void *tramp_ctrl = allocate_tramp_ctrl (tramp_ctrl_curr);
if (!tramp_ctrl)
abort ();
tramp_ctrl_curr = tramp_ctrl;
}
struct aarch64_trampoline *trampoline
= &tramp_ctrl_curr->trampolines[get_trampolines_per_page ()
- tramp_ctrl_curr->free_trampolines];
#if __APPLE__
/* Disable write protection for the MAP_JIT regions in this thread (see
https://developer.apple.com/documentation/apple-silicon/porting-just-in-time-compilers-to-apple-silicon) */
pthread_jit_write_protect_np (0);
#endif
memcpy (trampoline->insns, aarch64_trampoline_insns,
sizeof(aarch64_trampoline_insns));
trampoline->func_ptr = func;
trampoline->chain_ptr = chain;
#if __APPLE__
/* Re-enable write protection. */
pthread_jit_write_protect_np (1);
#endif
tramp_ctrl_curr->free_trampolines -= 1;
__builtin___clear_cache ((void *)trampoline->insns,
((void *)trampoline->insns + sizeof(trampoline->insns)));
*(void **) dst = &trampoline->insns;
}
HEAP_T_ATTR
void
__gcc_nested_func_ptr_deleted (void)
{
if (tramp_ctrl_curr == NULL)
abort ();
tramp_ctrl_curr->free_trampolines += 1;
if (tramp_ctrl_curr->free_trampolines == get_trampolines_per_page ())
{
if (tramp_ctrl_curr->prev == NULL)
return;
munmap (tramp_ctrl_curr->trampolines, getpagesize());
struct tramp_ctrl_data *prev = tramp_ctrl_curr->prev;
free (tramp_ctrl_curr);
tramp_ctrl_curr = prev;
}
}
#endif /* !inhibit_libc */