Implement the __builtin_nested_func_ptr_{created,deleted} functions for the x86_64-linux platform. This serves to exercise the infrastructure added in libgcc (--enable-off-stack-trampolines) and gcc (-foff-stack-trampolines) in supporting off-stack trampoline generation, and is intended primarily for demonstration and debugging purposes.
Co-authored-by: Andrew Burgess <andrew.burg...@embecosm.com> libgcc/ChangeLog: * config/i386/heap-trampoline.c: New file: Implement off-stack trampolines for x86_64. * config/i386/t-heap-trampoline: Add rule to build config/i386/heap-trampoline.c * config.host (x86_64-*-linux*): Handle --enable-off-stack-trampolines. * configure.ac (--enable-off-stack-trampolines): Permit setting for target x86_64-*-linux*. * configure: Regenerate. --- libgcc/config.host | 4 + libgcc/config/i386/heap-trampoline.c | 143 +++++++++++++++++++++++++++ libgcc/config/i386/t-heap-trampoline | 21 ++++ libgcc/configure | 3 + libgcc/configure.ac | 3 + 5 files changed, 174 insertions(+) create mode 100644 libgcc/config/i386/heap-trampoline.c create mode 100644 libgcc/config/i386/t-heap-trampoline diff --git a/libgcc/config.host b/libgcc/config.host index 168535b1780..163cd4c4161 100644 --- a/libgcc/config.host +++ b/libgcc/config.host @@ -753,6 +753,10 @@ x86_64-*-linux*) tmake_file="${tmake_file} i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" tm_file="${tm_file} i386/elf-lib.h" md_unwind_header=i386/linux-unwind.h + if test x$off_stack_trampolines = xyes; then + extra_parts="${extra_parts} heap-trampoline.o" + tmake_file="${tmake_file} i386/t-heap-trampoline" + fi ;; x86_64-*-kfreebsd*-gnu) extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" diff --git a/libgcc/config/i386/heap-trampoline.c b/libgcc/config/i386/heap-trampoline.c new file mode 100644 index 00000000000..6c202660c35 --- /dev/null +++ b/libgcc/config/i386/heap-trampoline.c @@ -0,0 +1,143 @@ +#include <unistd.h> +#include <sys/mman.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +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 __builtin_nested_func_ptr_created (void *chain, void *func, void **dst); +void __builtin_nested_func_ptr_deleted (void); + +struct tramp_ctrl_data; +struct tramp_ctrl_data +{ + struct tramp_ctrl_data *prev; + + int free_trampolines; + + /* This will be pointing to an executable mmap'ed page. */ + union ix86_trampoline *trampolines; +}; + +static const uint8_t trampoline_insns[] = { + /* movabs $<chain>,%r11 */ + 0x49, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* movabs $<func>,%r10 */ + 0x49, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* rex.WB jmpq *%r11 */ + 0x41, 0xff, 0xe3 +}; + +union ix86_trampoline { + uint8_t insns[sizeof(trampoline_insns)]; + + struct __attribute__((packed)) fields { + uint8_t insn_0[2]; + void *func_ptr; + uint8_t insn_1[2]; + void *chain_ptr; + uint8_t insn_2[3]; + } fields; +}; + +int +get_trampolines_per_page (void) +{ + return getpagesize() / sizeof(union ix86_trampoline); +} + +static _Thread_local struct tramp_ctrl_data *tramp_ctrl_curr = NULL; + +void * +allocate_trampoline_page (void) +{ + void *page; + + page = mmap (0, getpagesize (), PROT_WRITE | PROT_EXEC, + MAP_ANON | MAP_PRIVATE, 0, 0); + + 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; +} + +void +__builtin_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; + } + + union ix86_trampoline *trampoline + = &tramp_ctrl_curr->trampolines[get_trampolines_per_page () + - tramp_ctrl_curr->free_trampolines]; + + memcpy (trampoline->insns, trampoline_insns, + sizeof(trampoline_insns)); + trampoline->fields.func_ptr = func; + trampoline->fields.chain_ptr = chain; + + tramp_ctrl_curr->free_trampolines -= 1; + + __builtin___clear_cache ((void *)trampoline->insns, + ((void *)trampoline->insns + sizeof(trampoline->insns))); + + *dst = &trampoline->insns; +} + +void +__builtin_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; + } +} diff --git a/libgcc/config/i386/t-heap-trampoline b/libgcc/config/i386/t-heap-trampoline new file mode 100644 index 00000000000..d9dc9cfeac4 --- /dev/null +++ b/libgcc/config/i386/t-heap-trampoline @@ -0,0 +1,21 @@ +# Machine description for AArch64 architecture. +# Copyright (C) 2012-2021 Free Software Foundation, Inc. +# Contributed by ARM Ltd. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +LIB2ADD += $(srcdir)/config/i386/heap-trampoline.c diff --git a/libgcc/configure b/libgcc/configure index 2f469219e07..d9a74e3cc7c 100755 --- a/libgcc/configure +++ b/libgcc/configure @@ -2261,6 +2261,9 @@ fi if test "${enable_off_stack_trampolines+set}" = set; then : enableval=$enable_off_stack_trampolines; case "$target" in + x86_64-*-linux* ) + off_stack_trampolines=$enableval + ;; *) as_fn_error $? "Configure option --enable-off-stack-trampolines is not supported \ for this platform" "$LINENO" 5 diff --git a/libgcc/configure.ac b/libgcc/configure.ac index 97bbd4bd35c..4bec0a54493 100644 --- a/libgcc/configure.ac +++ b/libgcc/configure.ac @@ -72,6 +72,9 @@ AC_ARG_ENABLE([off-stack-trampolines], [AS_HELP_STRING([--enable-off-stack-trampolines] [Specify whether to support generating off-stack trampolines])],[ case "$target" in + x86_64-*-linux* ) + off_stack_trampolines=$enableval + ;; *) AC_MSG_ERROR([Configure option --enable-off-stack-trampolines is not supported \ for this platform]) -- 2.30.1 (Apple Git-130)