+_abnormal_termination()
[captive.git] / src / libcaptive / ps / signal.c
1 /* $Id$
2  * UNIX signal handling for processor emulation for support of ntoskrnl of libcaptive
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #define _GNU_SOURCE 1   /* for sys/ucontext.h for REG_* */
21
22 #include "config.h"
23
24 #include "captive/signal.h"     /* self */
25 #include <glib/gtypes.h>
26 #include <glib/gmessages.h>
27 #include <signal.h>
28 #include "captive/macros.h"
29 #include <sys/ucontext.h>       /* for struct ucontext */
30 #include "captive/mm.h" /* for captive_mmap_map_get() */
31 #include <sys/mman.h>
32 #include "reactos/internal/mm.h"  /* for PAGE_SIZE */
33
34
35 static greg_t val_exceptionstack_top=0;
36
37
38
39 /**
40  * _abnormal_termination:
41  *
42  * This call can be also accessed as AbnormalTermination() or abnormal_termination().
43  *
44  * Returns whether some exception occured (FIXME: in what scope?).
45  * Exception handlers are registered from W32 binary in stack frames stored in "fs:[0x00000000]"
46  * value which gets mapped by libcaptive/ps/signal.c to #val_exceptionstack_top
47  * variable.
48  *
49  * libcaptive currently does not raise any exceptions therefore this call always returns value %0.
50  * See RtlpDispatchException().
51  *
52  * Returns: non-zero if some exception is now registered and pending.
53  */
54 int _abnormal_termination(void)
55 {
56         return 0;
57 }
58
59
60 /**
61  * RtlpDispatchException:
62  * @ExceptionRecord: Ignored by libcaptive.
63  * @Context: Ignored by libcaptive.
64  *
65  * Function definition to prevent inclusion of real RtlpDispatchException() implementation.
66  * Currently libcaptive never raises any exception - fix _abnormal_termination() if it changes.
67  *
68  * Returns: Never returns. Value %0 if it returns although it is impossible.
69  */
70 ULONG RtlpDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,IN PCONTEXT Context)
71 {
72         g_assert_not_reached();
73         g_return_val_if_reached(0);
74 }
75
76
77 static gboolean instr_mov_greg_to_fsmem(int greg,const void *fsmem,struct ucontext *ucontext)
78 {
79         if (fsmem==0x00000000) {        /* exception stack top pointer */
80                 /* moving from %esp is required to pass! */
81                 val_exceptionstack_top=ucontext->uc_mcontext.gregs[greg];
82                 return TRUE;
83                 }
84         g_return_val_if_reached(FALSE);
85 }
86
87 static gboolean instr_mov_fsmem_to_greg(const void *fsmem,int greg,struct ucontext *ucontext)
88 {
89         if (fsmem==0x00000000) {        /* exception stack top pointer */
90                 /* moving to %esp is required to pass! */
91                 ucontext->uc_mcontext.gregs[greg]=val_exceptionstack_top;
92                 return TRUE;
93                 }
94         g_return_val_if_reached(FALSE);
95 }
96
97 static int op_regcode_to_greg(guint8 regcode)
98 {
99         switch (regcode) {
100                 case 0x00: return REG_EAX;
101                 case 0x01: return REG_ECX;
102                 case 0x02: return REG_EDX;
103                 case 0x03: return REG_EBX;
104                 case 0x04: return REG_ESP;
105                 case 0x05: return REG_EBP;
106                 case 0x06: return REG_ESI;
107                 case 0x07: return REG_EDI;
108                 }
109         g_return_val_if_reached(REG_EAX);
110 }
111
112 static void sigaction_SIGSEGV(int signo,siginfo_t *siginfo,struct ucontext *ucontext)
113 {
114 const guint8 *reg_eip;
115 const void *reg_eip_aligned;
116
117         g_return_if_fail(signo==SIGSEGV);
118         g_return_if_fail(siginfo->si_signo==SIGSEGV);
119         /* siginfo->si_code is weird, seen to have value 128 */
120
121         reg_eip=(void *)ucontext->uc_mcontext.gregs[REG_EIP];
122
123         /* 'reg_eip' is not yet PAGE_SIZE-aligned but we need the aligned ptr for captive_mmap_map_get().
124          * glib NOTE: YOU MAY NOT STORE POINTERS IN INTEGERS.
125          */
126         reg_eip_aligned=(const void *)(((char *)reg_eip)-(GPOINTER_TO_UINT(reg_eip)&(PAGE_SIZE-1)));
127         g_assert(reg_eip_aligned!=NULL);
128         g_return_if_fail(!(captive_mmap_map_get(reg_eip_aligned)&PROT_EXEC));
129         
130         /* all instruction notation comments are written in AT&T 'instr src,dest' syntax! */
131         if (*reg_eip==0x64) {   /* prefix '%fs:' */
132                 reg_eip++;
133                 /* TODO:thread; %fs: is CPU-dependent */
134                 if (*reg_eip==0xA3) {   /* 'mov %eax,%fs:{reg_eip[1..4]}' */
135                         reg_eip++;
136                         if (instr_mov_greg_to_fsmem(REG_EAX,*(const void **)reg_eip,ucontext)) {
137                                 reg_eip+=4;
138                                 goto ok;
139                                 }
140                         g_assert_not_reached();
141                         }
142                 if (*reg_eip==0x89) {   /* prefix 0x89 */
143                         reg_eip++;
144                         if ((*reg_eip & ~0x38)==0x05)   { /* 'mov %{op_regcode_to_greg(*reg_eip[b3..b5])},%fs:{reg_eip[1..4]} */
145                                 reg_eip++;
146                                 if (instr_mov_greg_to_fsmem(op_regcode_to_greg(reg_eip[-1]>>3U),*(const void **)reg_eip,ucontext)) {
147                                         reg_eip+=4;
148                                         goto ok;
149                                         }
150                                 g_assert_not_reached();
151                                 }
152                         g_assert_not_reached();
153                         }
154                 if (*reg_eip==0xA1) {   /* 'mov %fs:{reg_eip[1..4]},%eax' */
155                         reg_eip++;
156                         if (instr_mov_fsmem_to_greg(*(const void **)reg_eip,REG_EAX,ucontext)) {
157                                 reg_eip+=4;
158                                 goto ok;
159                                 }
160                         g_assert_not_reached();
161                         }
162                 if (*reg_eip==0x8B) {   /* prefix 0x8B */
163                         reg_eip++;
164                         if ((*reg_eip & ~0x38)==0x05) { /* 'mov %fs:{reg_eip[1..4]},%{op_regcode_to_greg(*reg_eip[b3..b5])} */
165                                 reg_eip++;
166                                 if (instr_mov_fsmem_to_greg(*(const void **)reg_eip,op_regcode_to_greg(reg_eip[-1]>>3U),ucontext)) {
167                                         reg_eip+=4;
168                                         goto ok;
169                                         }
170                                 g_assert_not_reached();
171                                 }
172                         g_assert_not_reached();
173                         }
174                 g_assert_not_reached();
175                 }
176         g_assert_not_reached();
177
178 ok:
179         ucontext->uc_mcontext.gregs[REG_EIP]=(greg_t)reg_eip;
180         /* success */
181 }
182
183 /**
184  * captive_signal_init:
185  *
186  * Initialize UNIX signal handling to be able to emulate foreign W32
187  * instructions. These instructions must be located inside address
188  * space of foreign W32 binary code which is identified by successful
189  * call to captive_mmap_map_get() returning #PROT_EXEC bit set.
190  * This bit should be set from MmAllocateSection() called from
191  * ntoskrnl/ldr/loader.c/LdrPEProcessModule().
192  *
193  * Currently emulated set is the access to %fs register offset %0
194  * where the exception stack top pointer is located.
195  *
196  * Returns: %TRUE if successful.
197  */
198 gboolean captive_signal_init(void)
199 {
200 gint errint;
201 struct sigaction sigaction_struct;
202 sigset_t sigset;
203
204         CAPTIVE_MEMZERO(&sigaction_struct);     /* this structure may have unpredictable fields */
205
206         /* Init 'sigaction_struct.sa_mask'. */
207         errint=sigemptyset(&sigaction_struct.sa_mask);
208         g_return_val_if_fail(errint==0,FALSE);
209         errint=sigaddset(&sigaction_struct.sa_mask,SIGSEGV);
210         g_return_val_if_fail(errint==0,FALSE);
211
212         /* Set the signal sigaction handler. */
213         sigaction_struct.sa_sigaction=(void (*)(int,siginfo_t *,void *))sigaction_SIGSEGV;
214         sigaction_struct.sa_flags=0
215                         |SA_SIGINFO;    /* Use 'sa_sigaction' (not 'sa_handler') */
216         errint=sigaction(SIGSEGV,
217                         &sigaction_struct,      /* act */
218                         NULL);  /* oldact */
219         g_return_val_if_fail(errint==0,FALSE);
220
221         /* Enable SIGSEGV signal (should be default). */
222         errint=sigemptyset(&sigset);
223         g_return_val_if_fail(errint==0,FALSE);
224         errint=sigaddset(&sigset,SIGSEGV);
225         g_return_val_if_fail(errint==0,FALSE);
226         errint=sigprocmask(SIG_UNBLOCK,
227                         &sigset,        /* set */
228                         NULL);  /* oldset */
229         g_return_val_if_fail(errint==0,FALSE);
230
231         return TRUE;
232 }