sig.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
sig.c (10170B)
---
1 #include <sys/signal.h>
2 #include <ucontext.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6 #include <string.h>
7 #include <signal.h>
8
9 #include "vx32.h"
10 #include "vx32impl.h"
11
12 static void sighandler(int signo, siginfo_t *si, void *ucontext)
13 {
14 if (vx32_sighandler(signo, si, ucontext)){
15 // Back into the fire.
16 return;
17 }
18
19 // vx32_sighandler thought that vx32 wasn't
20 // running or otherwise didn't know what to do.
21 // TODO: If there was a handler registered before us, call it?
22 // If the signal is not important, ignore it.
23 if (signo == SIGVTALRM || signo == SIGALRM)
24 return;
25
26 // Otherwise, deregister the handler and let the signal
27 // happen again -- this time we'll crash and get a core dump.
28 vxprint("Unregistering signal handler %d\n", signo);
29 signal(signo, SIG_DFL);
30 }
31
32 // vx32_sighandler saves execution context, finds emu,
33 // and calls vxemu_sighandler. If we're here, the fs segment
34 // register pointed at a valid emu, but we still might not have
35 // been executing actual vx32 translations.
36 // Trapeip is 0xffffffff if the other segment registers
37 // suggest we weren't inside vxrun_setup ... vxrun_cleanup.
38 int vxemu_sighandler(vxemu *emu, uint32_t trapeip)
39 {
40 if (vx32_debugxlate)
41 vxprint("vxemu_sighandler %p %#x\n", emu, trapeip);
42
43 if (emu->cpu_trap == 0)
44 return VXSIG_ERROR;
45
46 // Not in vx32 code: assume registers saved already and trap.
47 if (trapeip == 0xffffffff)
48 return VXSIG_TRAP;
49
50 if (emu->ininst) {
51 // In the middle of translating an instruction,
52 // so the registers are already saved.
53 // The signal hander knows the exact fault address,
54 // which may be a bit after emu->ininst.
55 // In fact, the fault address may be one or two
56 // instructions past emu->cpu.eip, and a real
57 // processor would not throw the trap until it
58 // actually got to the unreadable instruction,
59 // but there's not really any harm in trapping now instead.
60 return VXSIG_TRAP;
61 }
62
63 // Single-stepping? Use the original trap code.
64 if (emu->cpu_trap == VXTRAP_SINGLESTEP && emu->saved_trap){
65 emu->cpu_trap = emu->saved_trap;
66 emu->saved_trap = 0;
67 }
68
69 // In vx32 runtime code (rts.S, run32/64.S), the registers are in flux.
70 // Single-step until we can get out; then they'll be safe to look at.
71 // This only makes sense if the trap is an external trap like a timer.
72 char *eip = (char*)(uintptr_t)trapeip;
73 if ((emu->cpu_trap&VXTRAP_CATEGORY) != VXTRAP_CPU){
74 // The check for run32/64.S doesn't look for the entire file.
75 // Instead it looks for anywhere in the file except
76 // vxrun_cleanup. If we single-step out of vxrun_cleanup,
77 // then vx32_sighandler will get a single-step trap when
78 // the vx32 segment register doesn't point at emu and
79 // won't know what to do. If we're in vxrun_cleanup, then
80 // all the cpu registers are known to be saved.
81 extern char vx_rts_S_start[], vx_rts_S_end[];
82 extern char vx_run_S_start[];
83 if ((vx_rts_S_start <= eip && eip < vx_rts_S_end)
84 || (vx_run_S_start <= eip && eip < (char*)vxrun_cleanup)){
85 SingleStep:
86 if(++emu->nsinglestep > 500){
87 // Give up: something is wrong.
88 vxprint("vx32: single-stepping but stuck\n");
89 return VXSIG_ERROR;
90 }
91 emu->saved_trap = emu->cpu_trap;
92 emu->cpu_trap = 0;
93 return VXSIG_SINGLESTEP;
94 }
95 }
96 emu->nsinglestep = 0;
97
98 // In translated code buffer? Find original eip.
99 if ((char*)emu->codebuf <= eip && eip < (char*)emu->codefree){
100 // Binary search for the translated chunk in which the trap occurred.
101 // NB: frags appear in the opposite order as the code itself.
102 uint32_t *codetab = emu->codetab;
103 unsigned lofrag = 0;
104 unsigned hifrag = (uint32_t*)emu->codetop - codetab - 1;
105 while (hifrag > lofrag) {
106 unsigned midfrag = (lofrag + hifrag) / 2;
107 uint32_t mideip = codetab[midfrag];
108 if (trapeip >= mideip)
109 hifrag = midfrag;
110 else
111 lofrag = midfrag + 1;
112 }
113 struct vxfrag *frag = (vxfrag*)(intptr_t)codetab[lofrag];
114
115 // The eip is known to be in the translation buffer,
116 // but the buffer contains both fragment headers and
117 // fragment code. Make sure it's not in a fragment header.
118 if (trapeip < (uint32_t)(intptr_t)FRAGCODE(frag))
119 return VXSIG_ERROR;
120 unsigned ofs = trapeip - (uint32_t)(intptr_t)FRAGCODE(frag);
121
122 // The very first instruction in each fragment is the one that
123 // restores ebx. It is not included in the table, because it
124 // doesn't correspond to any original source instruction.
125 // If we're there, pretend we're at the first instruction in the
126 // fragment but don't save ebx -- it's already saved.
127 if (ofs < frag->insn[0].dstofs) {
128 emu->cpu.eip = frag->eip + frag->insn[0].srcofs;
129 return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
130 }
131
132 // Binary search through this fragment's instruction table
133 // for the actual faulting translated instruction,
134 // and compute the corresponding EIP in the original vx32 code.
135 unsigned ninsn = frag->ninsn;
136 unsigned loinsn = 0;
137 unsigned hiinsn = ninsn - 1;
138 while (hiinsn > loinsn) {
139 unsigned midinsn = (loinsn + hiinsn + 1) / 2;
140 unsigned midofs = frag->insn[midinsn].dstofs;
141 if (ofs >= midofs)
142 loinsn = midinsn;
143 else
144 hiinsn = midinsn - 1;
145 }
146 struct vxinsn *insn = &frag->insn[loinsn];
147
148 if (ofs < insn->dstofs) {
149 // How did that happen?
150 // We checked for ofs < frag->insn[0].dstofs above.
151 assert(0);
152 }
153 emu->cpu.eip = frag->eip + insn->srcofs;
154
155 // At the beginning of a translated instruction (before the
156 // translation has begun to execute) all registers are valid.
157 if (ofs == insn->dstofs)
158 return VXSIG_TRAP | VXSIG_SAVE_ALL;
159
160 // But some translations end up being more than one instruction,
161 // and we have to handle those specially, if they've executed
162 // only some of the instructions.
163 int r;
164 switch (insn->itype) {
165 default:
166 assert(0);
167
168 case VXI_JUMP:
169 case VXI_ENDFRAG:
170 // Direct jumps don't trash any registers while
171 // making their way into vxrun_lookup_backpatch.
172 return VXSIG_TRAP | VXSIG_SAVE_ALL;
173
174 case VXI_CALL:
175 // Call pushes a return address onto the stack
176 // as the first instruction of the translation.
177 // We can't just pop it back off, because we would
178 // still need to restore the value that got overwritten.
179 // The target eip is stored in the word at byte 26 in
180 // the translation.
181 // Call does not trash any registers on the way
182 // into vxrun_lookup_backpatch.
183 emu->cpu.eip = *(uint32_t*)(FRAGCODE(frag)+insn->dstofs+26);
184 return VXSIG_TRAP | VXSIG_SAVE_ALL;
185
186 case VXI_JUMPIND:
187 // Indirect jumps save ebx as their first instruction,
188 // and then they trash it. Since we're not at the first
189 // instruction (see above), it might or might not be
190 // trashed but is definitely already saved.
191 return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
192
193 case VXI_CALLIND:
194 // Indirect call is like indirect jump except that it
195 // pushes a return address onto the stack in the
196 // instruction that ends at dstlen-5. Until that point,
197 // only ebx has been trashed (but saved) and the stack
198 // is unmodified. At that point, though, the stack is now
199 // modified and we must commit the instruction:
200 // ebx contains the target eip.
201 r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
202 if (ofs >= insn->dstofs + insn->dstlen - 5)
203 r |= VXSIG_SAVE_EBX_AS_EIP;
204 return r;
205
206 case VXI_RETURN:
207 case VXI_RETURN_IMM:;
208 // Return is like indirect jump, but we have to treat it
209 // as committed once the pop ebx happens.
210 // Pop ebx happens at byte 7 of the translation.
211 r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
212 if (ofs - insn->dstofs > 7) {
213 r |= VXSIG_SAVE_EBX_AS_EIP;
214
215 if (insn->itype == VXI_RETURN_IMM) {
216 // Return immediate is like return but if we've committed
217 // to popping ebx we also have to commit to popping
218 // the immediate number of bytes off the stack.
219 // The immediate is a 32-bit word at byte 10 of
220 // the translation, but it was originally only 16 bits
221 // in the original return instruction, so it's okay to
222 // truncate.
223 r |= VXSIG_ADD_COUNT_TO_ESP;
224 r |= *(uint16_t*)(FRAGCODE(frag)+insn->dstofs+10) << VXSIG_COUNT_SHIFT;
225 }
226 }
227 return r;
228
229 case VXI_TRAP:
230 // Traps save eax as their first instruction and then
231 // they trash it. Since we're not at the first instruction
232 // (see above), it might or might not be trashed but
233 // is definitely already saved.
234 return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EAX);
235
236 case VXI_LOOP:
237 case VXI_LOOPZ:
238 case VXI_LOOPNZ:
239 // The first instruction in the loop translation decrements ecx.
240 // The rest figure out whether to jump.
241 // We can back out by re-incrementing ecx.
242 // Untested (most loops translate into actual loop instructions).
243 return VXSIG_TRAP | VXSIG_SAVE_ALL | VXSIG_INC_ECX;
244 }
245 }
246
247 // Let's see.
248 // The fs segment register pointed at a vxemu, so we're in vxproc_run.
249 // The eip is not in rts.S, nor in run32/64.S.
250 // The eip is not in a fragment translation.
251 // We're not translating an instruction (emu->ininst == nil).
252 // That pretty much means we're in some interstitial instruction.
253 // Assume the registers are already saved.
254 return VXSIG_TRAP;
255 }
256
257 int vx32_siginit(void)
258 {
259 stack_t ss;
260 void *stk;
261 struct sigaction sa;
262
263 // See if there's already an alternate signal stack.
264 if (sigaltstack(NULL, &ss) < 0)
265 return -1;
266
267 assert(!(ss.ss_flags & SS_ONSTACK));
268 if (ss.ss_flags & SS_DISABLE) {
269 // Allocate an alternate signal stack.
270 ss.ss_size = 64*1024;
271 stk = malloc(ss.ss_size);
272 if (stk == NULL)
273 return -1;
274 ss.ss_flags = 0;
275 ss.ss_sp = stk;
276 if (sigaltstack(&ss, NULL) < 0) {
277 free(stk);
278 return -1;
279 }
280 }
281
282 // Register our signal handler.
283 memset(&sa, 0, sizeof sa);
284 sa.sa_handler = (void*)sighandler;
285 sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
286 if (sigemptyset(&sa.sa_mask) < 0)
287 return -1;
288 if (sigaction(SIGSEGV, &sa, NULL) < 0)
289 return -1;
290 if (sigaction(SIGBUS, &sa, NULL) < 0)
291 return -1;
292 if (sigaction(SIGFPE, &sa, NULL) < 0)
293 return -1;
294 if (sigaction(SIGVTALRM, &sa, NULL) < 0)
295 return -1;
296 if (sigaction(SIGTRAP, &sa, NULL) < 0)
297 return -1;
298 return 0;
299 }
300