summaryrefslogtreecommitdiff
path: root/translate/grt/config/linux.c
blob: 6b73cb476bbd57785a5b748c9d9f17f5785c7427 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*  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 <stdlib.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;
};

/* 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
/* This is the current process being run.  */
extern struct stack_context *grt_get_current_process (void);

/* 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_get_current_process () == 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_get_current_process ()->cur_sp;
  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

/* Context for the main stack.  */
#ifdef USE_THREADS
#define THREAD __thread
#else
#define THREAD
#endif
static THREAD struct stack_context main_stack_context;

extern void grt_set_main_stack (struct stack_context *stack);

void
grt_stack_new_thread (void)
{
  main_stack_context.cur_sp = NULL;
  main_stack_context.cur_length = 0;
  grt_set_main_stack (&main_stack_context);
}

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_set_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

#ifdef __ia64__
  /* Also allocate BSP.  */
  if (mmap (base, page_size, PROT_READ | PROT_WRITE,
	    MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, MMAP_FILEDES, 0) != base)
    return NULL;
#endif

  res->cur_sp = (void *)res;
  res->cur_length = stack_size;
  return res;
}

#include <setjmp.h>
static int run_env_en;
static jmp_buf run_env;

void
__ghdl_maybe_return_via_longjump (int val)
{
  if (run_env_en)
    longjmp (run_env, val);
}

int
__ghdl_run_through_longjump (int (*func)(void))
{
  int res;

  run_env_en = 1;
  res = setjmp (run_env);
  if (res == 0)
    res = (*func)();
  run_env_en = 0;
  return res;
}