Cosmetic: Fixed gtk-doc 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                 if (*reg_eip==0xA3) {   /* 'mov %eax,%fs:{reg_eip[1..4]}' */
94                         reg_eip++;
95                         if (instr_mov_greg_to_fsmem(REG_EAX,*(const void **)reg_eip,ucontext)) {
96                                 reg_eip+=4;
97                                 goto ok;
98                                 }
99                         g_assert_not_reached();
100                         }
101                 if (*reg_eip==0x89) {   /* prefix 0x89 */
102                         reg_eip++;
103                         if ((*reg_eip & ~0x38)==0x05)   { /* 'mov %{op_regcode_to_greg(*reg_eip[b3..b5])},%fs:{reg_eip[1..4]} */
104                                 reg_eip++;
105                                 if (instr_mov_greg_to_fsmem(op_regcode_to_greg(reg_eip[-1]>>3U),*(const void **)reg_eip,ucontext)) {
106                                         reg_eip+=4;
107                                         goto ok;
108                                         }
109                                 g_assert_not_reached();
110                                 }
111                         g_assert_not_reached();
112                         }
113                 if (*reg_eip==0xA1) {   /* 'mov %fs:{reg_eip[1..4]},%eax' */
114                         reg_eip++;
115                         if (instr_mov_fsmem_to_greg(*(const void **)reg_eip,REG_EAX,ucontext)) {
116                                 reg_eip+=4;
117                                 goto ok;
118                                 }
119                         g_assert_not_reached();
120                         }
121                 if (*reg_eip==0x8B) {   /* prefix 0x8B */
122                         reg_eip++;
123                         if ((*reg_eip & ~0x38)==0x05) { /* 'mov %fs:{reg_eip[1..4]},%{op_regcode_to_greg(*reg_eip[b3..b5])} */
124                                 reg_eip++;
125                                 if (instr_mov_fsmem_to_greg(*(const void **)reg_eip,op_regcode_to_greg(reg_eip[-1]>>3U),ucontext)) {
126                                         reg_eip+=4;
127                                         goto ok;
128                                         }
129                                 g_assert_not_reached();
130                                 }
131                         g_assert_not_reached();
132                         }
133                 g_assert_not_reached();
134                 }
135         g_assert_not_reached();
136
137 ok:
138         ucontext->uc_mcontext.gregs[REG_EIP]=(greg_t)reg_eip;
139         /* success */
140 }
141
142 /**
143  * captive_signal_init:
144  *
145  * Initialize UNIX signal handling to be able to emulate foreign W32
146  * instructions. These instructions must be located inside address
147  * space of foreign W32 binary code which is identified by successful
148  * call to captive_mmap_map_get() returning #PROT_EXEC bit set.
149  * This bit should be set from MmAllocateSection() called from
150  * ntoskrnl/ldr/loader.c/LdrPEProcessModule().
151  *
152  * Currently emulated set is the access to %fs register offset %0
153  * where the exception stack top pointer is located.
154  *
155  * Returns: %TRUE if successful.
156  */
157 gboolean captive_signal_init(void)
158 {
159 gint errint;
160 struct sigaction sigaction_struct;
161 sigset_t sigset;
162
163         CAPTIVE_MEMZERO(&sigaction_struct);     /* this structure may have unpredictable fields */
164
165         /* Init 'sigaction_struct.sa_mask'. */
166         errint=sigemptyset(&sigaction_struct.sa_mask);
167         g_return_val_if_fail(errint==0,FALSE);
168         errint=sigaddset(&sigaction_struct.sa_mask,SIGSEGV);
169         g_return_val_if_fail(errint==0,FALSE);
170
171         /* Set the signal sigaction handler. */
172         sigaction_struct.sa_sigaction=(void (*)(int,siginfo_t *,void *))sigaction_SIGSEGV;
173         sigaction_struct.sa_flags=0
174                         |SA_SIGINFO;    /* Use 'sa_sigaction' (not 'sa_handler') */
175         errint=sigaction(SIGSEGV,
176                         &sigaction_struct,      /* act */
177                         NULL);  /* oldact */
178         g_return_val_if_fail(errint==0,FALSE);
179
180         /* Enable SIGSEGV signal (should be default). */
181         errint=sigemptyset(&sigset);
182         g_return_val_if_fail(errint==0,FALSE);
183         errint=sigaddset(&sigset,SIGSEGV);
184         g_return_val_if_fail(errint==0,FALSE);
185         errint=sigprocmask(SIG_UNBLOCK,
186                         &sigset,        /* set */
187                         NULL);  /* oldset */
188         g_return_val_if_fail(errint==0,FALSE);
189
190         return TRUE;
191 }