1 #define _GNU_SOURCE
2
3 #ifndef __linux__
4 #error This only compiles under Linux
5 #endif
6
7 #include <assert.h>
8 #include <ctype.h>
9 #include <string.h>
10
11 #include <limits.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <sys/types.h>
16 #include <ucontext.h>
17 #include <unistd.h>
18
19 #include "pocl_cache.h"
20 #include "pocl_cl.h"
21 #include "pocl_llvm.h"
22 #include "pocl_util.h"
23
24 extern unsigned long buffer_c;
25 extern unsigned long svm_buffer_c;
26 extern unsigned long queue_c;
27 extern unsigned long context_c;
28 extern unsigned long image_c;
29 extern unsigned long kernel_c;
30 extern unsigned long program_c;
31 extern unsigned long sampler_c;
32 extern unsigned long uevent_c;
33 extern unsigned long event_c;
34
35 static struct sigaction sigusr2_action, old_sigusr2_action;
36
37 #define FORMATTED_ULONG_MAX_LEN 20
38 /* formats a number to a buffer, like snprintf which can't be called from
39 * signal handler returs an offset into "out" where the number starts */
40 static unsigned
format_int(unsigned long num,char * out)41 format_int (unsigned long num, char *out)
42 {
43 out[FORMATTED_ULONG_MAX_LEN] = 0x0A;
44 unsigned i = 0;
45 for (; i < FORMATTED_ULONG_MAX_LEN; ++i)
46 {
47 unsigned dig = num % 10;
48 num = num / 10;
49 out[FORMATTED_ULONG_MAX_LEN - 1 - i] = 48 + dig;
50 if (num == 0)
51 break;
52 }
53 return (FORMATTED_ULONG_MAX_LEN - 1 - i);
54 }
55
56 #pragma GCC diagnostic push
57 #pragma GCC diagnostic ignored "-Wunused-result"
58
59 #define SIGNAL_HANDLER_PRINTF(NUMBER, HEADER) \
60 { \
61 char store[FORMATTED_ULONG_MAX_LEN + 1]; \
62 unsigned offset; \
63 write (STDERR_FILENO, HEADER, strlen (HEADER)); \
64 offset = format_int (NUMBER, store); \
65 write (STDERR_FILENO, store + offset, \
66 (FORMATTED_ULONG_MAX_LEN + 1 - offset)); \
67 }
68
69 static void
sigusr2_signal_handler(int signo,siginfo_t * si,void * data)70 sigusr2_signal_handler (int signo, siginfo_t *si, void *data)
71 {
72 if (signo == SIGUSR2)
73 {
74 SIGNAL_HANDLER_PRINTF (context_c, "Contexts: \n");
75 SIGNAL_HANDLER_PRINTF (queue_c, "Queues: \n");
76 SIGNAL_HANDLER_PRINTF (buffer_c, "Buffers: \n");
77 SIGNAL_HANDLER_PRINTF (image_c, "Images: \n");
78 SIGNAL_HANDLER_PRINTF (program_c, "Programs: \n");
79 SIGNAL_HANDLER_PRINTF (kernel_c, "Kernels: \n");
80 SIGNAL_HANDLER_PRINTF (sampler_c, "Samplers \n");
81 SIGNAL_HANDLER_PRINTF (uevent_c, "UserEvents: \n");
82 SIGNAL_HANDLER_PRINTF (event_c, "Events: \n");
83 }
84 else
85 (*old_sigusr2_action.sa_sigaction) (signo, si, data);
86 }
87 #pragma GCC diagnostic pop
88
89 void
pocl_install_sigusr2_handler()90 pocl_install_sigusr2_handler ()
91 {
92 POCL_MSG_PRINT_GENERAL ("Installing SIGUSR2 handler...\n");
93
94 sigusr2_action.sa_flags = SA_RESTART | SA_SIGINFO;
95 sigusr2_action.sa_sigaction = sigusr2_signal_handler;
96 int res = sigaction (SIGUSR2, &sigusr2_action, &old_sigusr2_action);
97 assert (res == 0);
98 }
99
100 /* This ugly hack is required because:
101 *
102 * OpenCL 1.2 specification, 6.3 Operators :
103 *
104 * A divide by zero with integer types does not cause an exception
105 * but will result in an unspecified value. Division by zero for
106 * floating-point types will result in ±infinity or NaN as
107 * prescribed by the IEEE-754 standard.
108 *
109 * FPU exceptions are masked by default on x86 linux, but integer divide
110 * is not and there doesn't seem any sane way to mask it.
111 *
112 * This *might* be possible to fix with a LLVM pass (either check divisor
113 * for 0, or perhaps some vector extension has a suitable instruction), but
114 * it's likely to ruin the performance.
115 */
116
117 #ifdef __x86_64__
118
119 #define DIV_OPCODE_SIZE 1
120 #define DIV_OPCODE_MASK 0xf6
121
122 /* F6 /6, F6 /7, F7 /6, F7 /7 */
123 #define DIV_OPCODE_1 0xf6
124 #define DIV_OPCODE_2 0xf7
125 #define DIV_MODRM_OPCODE_EXT_1 0x38 // /7
126 #define DIV_MODRM_OPCODE_EXT_2 0x30 // /6
127
128 #define MODRM_SIZE 1
129 #define MODRM_MASK 0xC0
130 #define REG2_MASK 0x38
131 #define REG1_MASK 0x07
132 #define ADDR_MODE_INDIRECT_ONE_BYTE_OFFSET 0x40
133 #define ADDR_MODE_INDIRECT_FOUR_BYTE_OFFSET 0x80
134 #define ADDR_MODE_INDIRECT 0x0
135 #define ADDR_MODE_REGISTER_ONLY 0xC0
136 #define REG_SP 0x4
137 #define REG_BP 0x5
138 #define SIB_BYTE 1
139 #define IP_RELATIVE_INDEXING 4
140
141 static struct sigaction sigfpe_action, old_sigfpe_action;
142
143 static void
sigfpe_signal_handler(int signo,siginfo_t * si,void * data)144 sigfpe_signal_handler (int signo, siginfo_t *si, void *data)
145 {
146 ucontext_t *uc;
147 uc = (ucontext_t *)data;
148 unsigned char *eip = (unsigned char *)(uc->uc_mcontext.gregs[REG_RIP]);
149
150 if ((signo == SIGFPE)
151 && ((si->si_code == FPE_INTDIV) || (si->si_code == FPE_INTOVF)))
152 {
153 /* Luckily for us, div-by-0 exceptions do NOT advance the IP register,
154 * so we have to disassemble the instruction (to know its length)
155 * and move IP past it. */
156 unsigned n = 0;
157
158 /* skip all prefixes */
159 while ((n < 4) && ((eip[n] & DIV_OPCODE_MASK) != DIV_OPCODE_MASK))
160 ++n;
161
162 /* too much prefixes = decoding failed */
163 if (n >= 4)
164 goto ORIGINAL_HANDLER;
165
166 /* check opcode */
167 unsigned opcode = eip[n];
168 if ((opcode != DIV_OPCODE_1) && (opcode != DIV_OPCODE_2))
169 goto ORIGINAL_HANDLER;
170 n += DIV_OPCODE_SIZE;
171
172 unsigned modrm = eip[n];
173 unsigned modmask = modrm & MODRM_MASK;
174 unsigned reg1mask = modrm & REG1_MASK;
175 unsigned reg2mask = modrm & REG2_MASK;
176 /* check opcode extension in ModR/M reg2 */
177 if ((reg2mask != DIV_MODRM_OPCODE_EXT_1)
178 && (reg2mask != DIV_MODRM_OPCODE_EXT_2))
179 goto ORIGINAL_HANDLER;
180 n += MODRM_SIZE;
181
182 /* handle immediates/registers */
183 if (modmask == ADDR_MODE_INDIRECT_ONE_BYTE_OFFSET)
184 n += 1;
185 if (modmask == ADDR_MODE_INDIRECT_FOUR_BYTE_OFFSET)
186 n += 4;
187 if (modmask == ADDR_MODE_INDIRECT)
188 n += 0;
189 if (modmask != ADDR_MODE_REGISTER_ONLY)
190 {
191 if (reg1mask == REG_SP)
192 n += SIB_BYTE;
193 if (reg1mask == REG_BP)
194 n += IP_RELATIVE_INDEXING;
195 }
196
197 uc->uc_mcontext.gregs[REG_RIP] += n;
198 return;
199 }
200 else
201 {
202 ORIGINAL_HANDLER:
203 (*old_sigfpe_action.sa_sigaction) (signo, si, data);
204 }
205 }
206
207 #endif
208
209 void
pocl_install_sigfpe_handler()210 pocl_install_sigfpe_handler ()
211 {
212 #ifdef __x86_64__
213
214 #ifdef ENABLE_LLVM
215 /* This is required to force LLVM to register its signal
216 * handlers, before pocl registers its own SIGFPE handler.
217 * LLVM otherwise calls this via
218 * pocl_llvm_build_program ->
219 * clang::PrintPreprocessedAction ->
220 * CreateOutputFile -> RemoveFileOnSignal
221 * Registering our handlers before LLVM creates its sigaltstack
222 * leads to interesting crashes & bugs later.
223 */
224 char random_empty_file[POCL_FILENAME_LENGTH];
225 pocl_cache_tempname (random_empty_file, NULL, NULL);
226 pocl_llvm_remove_file_on_signal (random_empty_file);
227 #endif
228
229 POCL_MSG_PRINT_GENERAL ("Installing SIGFPE handler...\n");
230
231 sigfpe_action.sa_flags = SA_RESTART | SA_SIGINFO;
232 sigfpe_action.sa_sigaction = sigfpe_signal_handler;
233 int res = sigaction (SIGFPE, &sigfpe_action, &old_sigfpe_action);
234 assert (res == 0);
235 #endif
236 }
237