aboutsummaryrefslogtreecommitdiffstats
path: root/translate/grt/config/linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'translate/grt/config/linux.c')
-rw-r--r--translate/grt/config/linux.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/translate/grt/config/linux.c b/translate/grt/config/linux.c
new file mode 100644
index 000000000..047cfd1b4
--- /dev/null
+++ b/translate/grt/config/linux.c
@@ -0,0 +1,268 @@
+/* GRT stacks implementation for linux and other *nix.
+ Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold.
+
+ GHDL 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 2, or (at your option) any later
+ version.
+
+ GHDL 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 COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/ucontext.h>
+//#include <stdint.h>
+
+/* On x86, the stack growns downward. */
+#define STACK_GROWNS_DOWNWARD 1
+
+#ifdef linux
+/* If set, SIGSEGV is caught in order to automatically grow the stacks. */
+#define EXTEND_STACK 1
+#endif
+
+/* Defined in Grt.Stacks. */
+extern unsigned int stack_size;
+extern unsigned int stack_max_size;
+
+/* Size of a memory page. */
+static size_t page_size;
+
+extern void grt_stack_error_grow_failed (void);
+extern void grt_stack_error_null_access (void);
+extern void grt_stack_error_memory_access (void);
+extern void grt_overflow_error (void);
+
+/* Definitions:
+ The base of the stack is the address before the first available byte on the
+ stack. If the stack grows downward, the base is equal to the high bound.
+*/
+
+/* Per stack context.
+ This context is allocated at the top (or bottom if the stack grows
+ upward) of the stack.
+ Therefore, the base of the stack can be easily deduced from the context. */
+struct stack_context
+{
+ /* The current stack pointer. */
+ void *cur_sp;
+ /* The current stack length. */
+ size_t cur_length;
+};
+
+/* Context for the main stack. */
+static struct stack_context main_stack_context;
+
+extern struct stack_context *grt_stack_main_stack;
+
+/* If MAP_ANONYMOUS is not defined, use /dev/zero. */
+#ifndef MAP_ANONYMOUS
+#define USE_DEV_ZERO
+static int dev_zero_fd;
+#define MAP_ANONYMOUS 0
+#define MMAP_FILEDES dev_zero_fd
+#else
+#define MMAP_FILEDES -1
+#endif
+
+#if EXTEND_STACK
+/* Defined in Grt.Processes (body).
+ This is the current process being run.
+ FIXME: this won't work with pthread! */
+extern void **grt_cur_proc;
+
+/* Stack used for signals.
+ The stack must be different from the running stack, because we want to be
+ able to extend the running stack. When the stack need to be extended, the
+ current stack pointer does not point to a valid address. Therefore, the
+ stack cannot be used or else a second SIGSEGV is generated while the
+ arguments are pushed. */
+static unsigned long sig_stack[SIGSTKSZ / sizeof (long)];
+
+/* Signal stack descriptor. */
+static stack_t sig_stk;
+
+static struct sigaction prev_sigsegv_act;
+static struct sigaction sigsegv_act;
+
+/* The following code assumes stack grows downward. */
+#if !STACK_GROWNS_DOWNWARD
+#error "Not implemented"
+#endif
+
+/* Handler for SIGSEGV signal, which grow the stack. */
+static void grt_sigsegv_handler (int signo, siginfo_t *info, void *ptr)
+{
+ static int in_handler;
+ void *addr;
+ struct stack_context *ctxt;
+ void *stack_high;
+ void *stack_low;
+ void *n_low;
+ size_t n_len;
+ ucontext_t *uctxt = (ucontext_t *)ptr;
+
+ in_handler++;
+
+#ifdef __i386__
+ /* Linux generates a SIGSEGV (!) for an overflow exception. */
+ if (uctxt->uc_mcontext.gregs[REG_TRAPNO] == 4)
+ {
+ grt_overflow_error ();
+ }
+#endif
+
+ if (info == NULL || grt_cur_proc == NULL || in_handler > 1)
+ {
+ /* We loose. */
+ sigaction (SIGSEGV, &prev_sigsegv_act, NULL);
+ return;
+ }
+
+ addr = info->si_addr;
+
+ /* Check ADDR belong to the stack. */
+ ctxt = *grt_cur_proc;
+ stack_high = (void *)(ctxt + 1);
+ stack_low = stack_high - stack_max_size;
+ if (addr > stack_high || addr < stack_low)
+ {
+ /* Out of the stack. */
+ if (addr < (void *)page_size)
+ grt_stack_error_null_access ();
+ else
+ grt_stack_error_memory_access ();
+ }
+ /* Compute the address of the faulting page. */
+ n_low = (void *)((unsigned long)addr & ~(page_size - 1));
+
+ /* Should not happen. */
+ if (n_low < stack_low)
+ abort ();
+
+ /* Allocate one more page, if possible. */
+ if (n_low != stack_low)
+ n_low -= page_size;
+
+ /* Compute the new length. */
+ n_len = stack_high - n_low;
+
+ if (mmap (n_low, n_len - ctxt->cur_length, PROT_READ | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, MMAP_FILEDES, 0)
+ != n_low)
+ {
+ /* Cannot grow the stack. */
+ grt_stack_error_grow_failed ();
+ }
+
+ ctxt->cur_length = n_len;
+
+ sigaction (SIGSEGV, &sigsegv_act, NULL);
+
+ in_handler--;
+
+ /* Hopes we can resume! */
+ return;
+}
+
+static void grt_signal_setup (void)
+{
+ sigsegv_act.sa_sigaction = &grt_sigsegv_handler;
+ sigemptyset (&sigsegv_act.sa_mask);
+ sigsegv_act.sa_flags = SA_ONESHOT | SA_ONSTACK | SA_SIGINFO;
+
+ /* Use an alternate stack during signals. */
+ sig_stk.ss_sp = sig_stack;
+ sig_stk.ss_size = sizeof (sig_stack);
+ sig_stk.ss_flags = 0;
+ sigaltstack (&sig_stk, NULL);
+
+ /* We don't care about the return status.
+ If the handler is not installed, then some feature are lost. */
+ sigaction (SIGSEGV, &sigsegv_act, &prev_sigsegv_act);
+}
+#endif
+
+void
+grt_stack_init (void)
+{
+ size_t pg_round;
+
+ page_size = getpagesize ();
+ pg_round = page_size - 1;
+
+ /* Align size. */
+ stack_size = (stack_size + pg_round) & ~pg_round;
+ stack_max_size = (stack_max_size + pg_round) & ~pg_round;
+
+ /* Set mimum values. */
+ if (stack_size < 2 * page_size)
+ stack_size = 2 * page_size;
+ if (stack_max_size < (stack_size + 2 * page_size))
+ stack_max_size = stack_size + 2 * page_size;
+
+ /* Initialize the main stack context. */
+ main_stack_context.cur_sp = NULL;
+ main_stack_context.cur_length = 0;
+ grt_stack_main_stack = &main_stack_context;
+
+#ifdef USE_DEV_ZERO
+ dev_zero_fd = open ("/dev/zero", O_RDWR);
+ if (dev_zero_fd < 0)
+ abort ();
+#endif
+
+#if EXTEND_STACK
+ grt_signal_setup ();
+#endif
+}
+
+/* Allocate a stack.
+ Called by i386.S */
+struct stack_context *
+grt_stack_allocate (void)
+{
+ struct stack_context *res;
+ void *r;
+ void *base;
+
+ /* Allocate the stack, but without any rights. This is a guard. */
+ base = (void *)mmap (NULL, stack_max_size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, MMAP_FILEDES, 0);
+
+ if (base == (void *)-1)
+ return NULL;
+
+ /* Set rights on the allocated stack. */
+#if STACK_GROWNS_DOWNWARD
+ r = base + stack_max_size - stack_size;
+#else
+ r = base;
+#endif
+ if (mmap (r, stack_size, PROT_READ | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, MMAP_FILEDES, 0)
+ != r)
+ return NULL;
+
+#if STACK_GROWNS_DOWNWARD
+ res = (struct stack_context *)
+ (base + stack_max_size - sizeof (struct stack_context));
+#else
+ res = (struct stack_context *)(base + sizeof (struct stack_context));
+#endif
+
+ res->cur_sp = (void *)res;
+ res->cur_length = stack_size;
+ return res;
+}