Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

[4af254]: src / runtime / x86-arch.c Maximize Restore History

Download this file

x86-arch.c    357 lines (305 with data), 10.3 kB

  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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*
* This software is part of the SBCL system. See the README file for
* more information.
*
* This software is derived from the CMU CL system, which was
* written at Carnegie Mellon University and released into the
* public domain. The software is in the public domain and is
* provided with absolutely no warranty. See the COPYING and CREDITS
* files for more information.
*/
#include <stdio.h>
#include "runtime.h"
#include "globals.h"
#include "validate.h"
#include "os.h"
#include "sbcl.h"
#include "arch.h"
#include "lispregs.h"
#include "signal.h"
#include "alloc.h"
#include "interrupt.h"
#include "interr.h"
#include "breakpoint.h"
#include "monitor.h"
#include "thread.h"
#include "genesis/static-symbols.h"
#include "genesis/symbol.h"
#define BREAKPOINT_INST 0xcc /* INT3 */
unsigned long fast_random_state = 1;
void arch_init(void)
{}
/*
* hacking signal contexts
*
* (This depends both on architecture, which determines what we might
* want to get to, and on OS, which determines how we get to it.)
*/
int *
context_eflags_addr(os_context_t *context)
{
#if defined __linux__
/* KLUDGE: As of kernel 2.2.14 on Red Hat 6.2, there's code in the
* <sys/ucontext.h> file to define symbolic names for offsets into
* gregs[], but it's conditional on __USE_GNU and not defined, so
* we need to do this nasty absolute index magic number thing
* instead. */
return &context->uc_mcontext.gregs[16];
#elif defined __FreeBSD__
return &context->uc_mcontext.mc_eflags;
#elif defined __OpenBSD__
return &context->sc_eflags;
#else
#error unsupported OS
#endif
}
void arch_skip_instruction(os_context_t *context)
{
/* Assuming we get here via an INT3 xxx instruction, the PC now
* points to the interrupt code (a Lisp value) so we just move
* past it. Skip the code; after that, if the code is an
* error-trap or cerror-trap then skip the data bytes that follow. */
int vlen;
int code;
/* Get and skip the Lisp interrupt code. */
code = *(char*)(*os_context_pc_addr(context))++;
switch (code)
{
case trap_Error:
case trap_Cerror:
/* Lisp error arg vector length */
vlen = *(char*)(*os_context_pc_addr(context))++;
/* Skip Lisp error arg data bytes. */
while (vlen-- > 0) {
( (char*)(*os_context_pc_addr(context)) )++;
}
break;
case trap_Breakpoint: /* not tested */
case trap_FunEndBreakpoint: /* not tested */
break;
case trap_PendingInterrupt:
case trap_Halt:
/* only needed to skip the Code */
break;
default:
fprintf(stderr,"[arch_skip_inst invalid code %d\n]\n",code);
break;
}
FSHOW((stderr,
"/[arch_skip_inst resuming at %x]\n",
*os_context_pc_addr(context)));
}
unsigned char *
arch_internal_error_arguments(os_context_t *context)
{
return 1 + (unsigned char *)(*os_context_pc_addr(context));
}
boolean
arch_pseudo_atomic_atomic(os_context_t *context)
{
return SymbolValue(PSEUDO_ATOMIC_ATOMIC,arch_os_get_current_thread());
}
void
arch_set_pseudo_atomic_interrupted(os_context_t *context)
{
SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, make_fixnum(1),
arch_os_get_current_thread());
}
/*
* This stuff seems to get called for TRACE and debug activity.
*/
unsigned long
arch_install_breakpoint(void *pc)
{
unsigned long result = *(unsigned long*)pc;
*(char*)pc = BREAKPOINT_INST; /* x86 INT3 */
*((char*)pc+1) = trap_Breakpoint; /* Lisp trap code */
return result;
}
void
arch_remove_breakpoint(void *pc, unsigned long orig_inst)
{
*((char *)pc) = orig_inst & 0xff;
*((char *)pc + 1) = (orig_inst & 0xff00) >> 8;
}
/* When single stepping, single_stepping holds the original instruction
* PC location. */
unsigned int *single_stepping = NULL;
#ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG
unsigned int single_step_save1;
unsigned int single_step_save2;
unsigned int single_step_save3;
#endif
void
arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
{
unsigned int *pc = (unsigned int*)(*os_context_pc_addr(context));
/* Put the original instruction back. */
*((char *)pc) = orig_inst & 0xff;
*((char *)pc + 1) = (orig_inst & 0xff00) >> 8;
#ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG
/* Install helper instructions for the single step:
* pushf; or [esp],0x100; popf. */
single_step_save1 = *(pc-3);
single_step_save2 = *(pc-2);
single_step_save3 = *(pc-1);
*(pc-3) = 0x9c909090;
*(pc-2) = 0x00240c81;
*(pc-1) = 0x9d000001;
#else
*context_eflags_addr(context) |= 0x100;
#endif
single_stepping = (unsigned int*)pc;
#ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG
*os_context_pc_addr(context) = (char *)pc - 9;
#endif
}
void
sigtrap_handler(int signal, siginfo_t *info, void *void_context)
{
int code = info->si_code;
os_context_t *context = (os_context_t*)void_context;
unsigned int trap;
sigset_t ss;
if (single_stepping && (signal==SIGTRAP))
{
/* fprintf(stderr,"* single step trap %x\n", single_stepping); */
#ifdef CANNOT_GET_TO_SINGLE_STEP_FLAG
/* Un-install single step helper instructions. */
*(single_stepping-3) = single_step_save1;
*(single_stepping-2) = single_step_save2;
*(single_stepping-1) = single_step_save3;
#else
*context_eflags_addr(context) ^= 0x100;
#endif
/* Re-install the breakpoint if possible. */
if (*os_context_pc_addr(context) == (int)single_stepping + 1) {
fprintf(stderr, "warning: couldn't reinstall breakpoint\n");
} else {
*((char *)single_stepping) = BREAKPOINT_INST; /* x86 INT3 */
*((char *)single_stepping+1) = trap_Breakpoint;
}
single_stepping = NULL;
return;
}
/* This is just for info in case the monitor wants to print an
* approximation. */
current_control_stack_pointer =
(lispobj *)*os_context_sp_addr(context);
/* FIXME: CMUCL puts the float control restoration code here.
Thus, it seems to me that single-stepping won't restore the
float control. Since SBCL currently doesn't support
single-stepping (as far as I can tell) this is somewhat moot,
but it might be worth either moving this code up or deleting
the single-stepping code entirely. -- CSR, 2002-07-15 */
#ifdef LISP_FEATURE_LINUX
os_restore_fp_control(context);
#endif
/* On entry %eip points just after the INT3 byte and aims at the
* 'kind' value (eg trap_Cerror). For error-trap and Cerror-trap a
* number of bytes will follow, the first is the length of the byte
* arguments to follow. */
trap = *(unsigned char *)(*os_context_pc_addr(context));
switch (trap) {
case trap_PendingInterrupt:
FSHOW((stderr, "/<trap pending interrupt>\n"));
arch_skip_instruction(context);
sigemptyset(&ss);
sigaddset(&ss,SIGTRAP);
sigprocmask(SIG_UNBLOCK,&ss,0);
interrupt_handle_pending(context);
break;
case trap_Halt:
/* Note: the old CMU CL code tried to save FPU state
* here, and restore it after we do our thing, but there
* seems to be no point in doing that, since we're just
* going to lose(..) anyway. */
fake_foreign_function_call(context);
lose("%%PRIMITIVE HALT called; the party is over.");
case trap_Error:
case trap_Cerror:
FSHOW((stderr, "<trap error/cerror %d>\n", code));
interrupt_internal_error(signal, info, context, code==trap_Cerror);
break;
case trap_Breakpoint:
(char*)(*os_context_pc_addr(context)) -= 1;
handle_breakpoint(signal, info, context);
break;
case trap_FunEndBreakpoint:
(char*)(*os_context_pc_addr(context)) -= 1;
*os_context_pc_addr(context) =
(int)handle_fun_end_breakpoint(signal, info, context);
break;
default:
FSHOW((stderr,"/[C--trap default %d %d %x]\n",
signal, code, context));
interrupt_handle_now(signal, info, context);
break;
}
}
static void
sigill_handler(int signal, siginfo_t *siginfo, void *void_context) {
os_context_t *context = (os_context_t*)void_context;
fake_foreign_function_call(context);
monitor_or_something();
}
void
arch_install_interrupt_handlers()
{
SHOW("entering arch_install_interrupt_handlers()");
/* Note: The old CMU CL code here used sigtrap_handler() to handle
* SIGILL as well as SIGTRAP. I couldn't see any reason to do
* things that way. So, I changed to separate handlers when
* debugging a problem on OpenBSD, where SBCL wasn't catching
* SIGILL properly, but was instead letting the process be
* terminated with an "Illegal instruction" output. If this change
* turns out to break something (maybe breakpoint handling on some
* OS I haven't tested on?) and we have to go back to the old CMU
* CL way, I hope there will at least be a comment to explain
* why.. -- WHN 2001-06-07 */
undoably_install_low_level_interrupt_handler(SIGILL , sigill_handler);
undoably_install_low_level_interrupt_handler(SIGTRAP, sigtrap_handler);
SHOW("returning from arch_install_interrupt_handlers()");
}
/* This is implemented in assembly language and called from C: */
extern lispobj
call_into_lisp(lispobj fun, lispobj *args, int nargs);
/* These functions are an interface to the Lisp call-in facility.
* Since this is C we can know nothing about the calling environment.
* The control stack might be the C stack if called from the monitor
* or the Lisp stack if called as a result of an interrupt or maybe
* even a separate stack. The args are most likely on that stack but
* could be in registers depending on what the compiler likes. So we
* copy the args into a portable vector and let the assembly language
* call-in function figure it out. */
lispobj
funcall0(lispobj function)
{
lispobj *args = NULL;
FSHOW((stderr, "/entering funcall0(0x%lx)\n", (long)function));
return call_into_lisp(function, args, 0);
}
lispobj
funcall1(lispobj function, lispobj arg0)
{
lispobj args[1];
args[0] = arg0;
return call_into_lisp(function, args, 1);
}
lispobj
funcall2(lispobj function, lispobj arg0, lispobj arg1)
{
lispobj args[2];
args[0] = arg0;
args[1] = arg1;
return call_into_lisp(function, args, 2);
}
lispobj
funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
{
lispobj args[3];
args[0] = arg0;
args[1] = arg1;
args[2] = arg2;
return call_into_lisp(function, args, 3);
}