Cosmetic: +Comment
[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;
36
37 static gboolean instr_mov_greg_to_fsmem(int greg,const void *fsmem,struct ucontext *ucontext)
38 {
39         if (fsmem==0x00000000) {        /* exception stack top pointer */
40                 /* moving from %esp is required to pass! */
41                 val_exceptionstack_top=ucontext->uc_mcontext.gregs[greg];
42                 return TRUE;
43                 }
44         g_return_val_if_reached(FALSE);
45 }
46
47 static gboolean instr_mov_fsmem_to_greg(const void *fsmem,int greg,struct ucontext *ucontext)
48 {
49         if (fsmem==0x00000000) {        /* exception stack top pointer */
50                 /* moving to %esp is required to pass! */
51                 ucontext->uc_mcontext.gregs[greg]=val_exceptionstack_top;
52                 return TRUE;
53                 }
54         g_return_val_if_reached(FALSE);
55 }
56
57 static int op_regcode_to_greg(guint8 regcode)
58 {
59         switch (regcode) {
60                 case 0x00: return REG_EAX;
61                 case 0x01: return REG_ECX;
62                 case 0x02: return REG_EDX;
63                 case 0x03: return REG_EBX;
64                 case 0x04: return REG_ESP;
65                 case 0x05: return REG_EBP;
66                 case 0x06: return REG_ESI;
67                 case 0x07: return REG_EDI;
68                 }
69         g_return_val_if_reached(REG_EAX);
70 }
71
72 static void sigaction_SIGSEGV(int signo,siginfo_t *siginfo,struct ucontext *ucontext)
73 {
74 const guint8 *reg_eip;
75 const void *reg_eip_aligned;
76
77         g_return_if_fail(signo==SIGSEGV);
78         g_return_if_fail(siginfo->si_signo==SIGSEGV);
79         /* siginfo->si_code is weird, seen to have value 128 */
80
81         reg_eip=(void *)ucontext->uc_mcontext.gregs[REG_EIP];
82
83         /* 'reg_eip' is not yet PAGE_SIZE-aligned but we need the aligned ptr for captive_mmap_map_get().
84          * glib NOTE: YOU MAY NOT STORE POINTERS IN INTEGERS.
85          */
86         reg_eip_aligned=(const void *)(((char *)reg_eip)-(GPOINTER_TO_UINT(reg_eip)&(PAGE_SIZE-1)));
87         g_assert(reg_eip_aligned!=NULL);
88         g_return_if_fail(!(captive_mmap_map_get(reg_eip_aligned)&PROT_EXEC));
89         
90         /* all instruction notation comments are written in AT&T 'instr src,dest' syntax! */
91         if (*reg_eip==0x64) {   /* prefix '%fs:' */
92                 reg_eip++;
93                 /* TODO:thread; %fs: is CPU-dependent */
94                 if (*reg_eip==0xA3) {   /* 'mov %eax,%fs:{reg_eip[1..4]}' */
95                         reg_eip++;
96                         if (instr_mov_greg_to_fsmem(REG_EAX,*(const void **)reg_eip,ucontext)) {
97                                 reg_eip+=4;
98                                 goto ok;
99                                 }
100                         g_assert_not_reached();
101                         }
102                 if (*reg_eip==0x89) {   /* prefix 0x89 */
103                         reg_eip++;
104                         if ((*reg_eip & ~0x38)==0x05)   { /* 'mov %{op_regcode_to_greg(*reg_eip[b3..b5])},%fs:{reg_eip[1..4]} */
105                                 reg_eip++;
106                                 if (instr_mov_greg_to_fsmem(op_regcode_to_greg(reg_eip[-1]>>3U),*(const void **)reg_eip,ucontext)) {
107                                         reg_eip+=4;
108                                         goto ok;
109                                         }
110                                 g_assert_not_reached();
111                                 }
112                         g_assert_not_reached();
113                         }
114                 if (*reg_eip==0xA1) {   /* 'mov %fs:{reg_eip[1..4]},%eax' */
115                         reg_eip++;
116                         if (instr_mov_fsmem_to_greg(*(const void **)reg_eip,REG_EAX,ucontext)) {
117                                 reg_eip+=4;
118                                 goto ok;
119                                 }
120                         g_assert_not_reached();
121                         }
122                 if (*reg_eip==0x8B) {   /* prefix 0x8B */
123                         reg_eip++;
124                         if ((*reg_eip & ~0x38)==0x05) { /* 'mov %fs:{reg_eip[1..4]},%{op_regcode_to_greg(*reg_eip[b3..b5])} */
125                                 reg_eip++;
126                                 if (instr_mov_fsmem_to_greg(*(const void **)reg_eip,op_regcode_to_greg(reg_eip[-1]>>3U),ucontext)) {
127                                         reg_eip+=4;
128                                         goto ok;
129                                         }
130                                 g_assert_not_reached();
131                                 }
132                         g_assert_not_reached();
133                         }
134                 g_assert_not_reached();
135                 }
136         g_assert_not_reached();
137
138 ok:
139         ucontext->uc_mcontext.gregs[REG_EIP]=(greg_t)reg_eip;
140         /* success */
141 }
142
143 /**
144  * captive_signal_init:
145  *
146  * Initialize UNIX signal handling to be able to emulate foreign W32
147  * instructions. These instructions must be located inside address
148  * space of foreign W32 binary code which is identified by successful
149  * call to captive_mmap_map_get() returning #PROT_EXEC bit set.
150  * This bit should be set from MmAllocateSection() called from
151  * ntoskrnl/ldr/loader.c/LdrPEProcessModule().
152  *
153  * Currently emulated set is the access to %fs register offset %0
154  * where the exception stack top pointer is located.
155  *
156  * Returns: %TRUE if successful.
157  */
158 gboolean captive_signal_init(void)
159 {
160 gint errint;
161 struct sigaction sigaction_struct;
162 sigset_t sigset;
163
164         CAPTIVE_MEMZERO(&sigaction_struct);     /* this structure may have unpredictable fields */
165
166         /* Init 'sigaction_struct.sa_mask'. */
167         errint=sigemptyset(&sigaction_struct.sa_mask);
168         g_return_val_if_fail(errint==0,FALSE);
169         errint=sigaddset(&sigaction_struct.sa_mask,SIGSEGV);
170         g_return_val_if_fail(errint==0,FALSE);
171
172         /* Set the signal sigaction handler. */
173         sigaction_struct.sa_sigaction=(void (*)(int,siginfo_t *,void *))sigaction_SIGSEGV;
174         sigaction_struct.sa_flags=0
175                         |SA_SIGINFO;    /* Use 'sa_sigaction' (not 'sa_handler') */
176         errint=sigaction(SIGSEGV,
177                         &sigaction_struct,      /* act */
178                         NULL);  /* oldact */
179         g_return_val_if_fail(errint==0,FALSE);
180
181         /* Enable SIGSEGV signal (should be default). */
182         errint=sigemptyset(&sigset);
183         g_return_val_if_fail(errint==0,FALSE);
184         errint=sigaddset(&sigset,SIGSEGV);
185         g_return_val_if_fail(errint==0,FALSE);
186         errint=sigprocmask(SIG_UNBLOCK,
187                         &sigset,        /* set */
188                         NULL);  /* oldset */
189         g_return_val_if_fail(errint==0,FALSE);
190
191         return TRUE;
192 }