Rev 18 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | // Based on https://gist.github.com/jvranish/4441299 |
2 | |||
8 | pmbaty | 3 | #if defined(__linux__) || defined(__FreeBSD__) // Pierre-Marie Baty -- added compile guard |
1 | pmbaty | 4 | |
5 | #define _GNU_SOURCE |
||
6 | #include "harness/os.h" |
||
7 | #include <assert.h> |
||
8 | #include <ctype.h> |
||
9 | #include <dirent.h> |
||
10 | #include <err.h> |
||
11 | #include <errno.h> |
||
12 | #include <execinfo.h> |
||
13 | #include <fcntl.h> |
||
14 | #include <libgen.h> |
||
15 | #include <limits.h> |
||
16 | #include <link.h> |
||
17 | #include <signal.h> |
||
18 | #include <stdbool.h> |
||
19 | #include <stdint.h> |
||
20 | #include <stdio.h> |
||
21 | #include <stdlib.h> |
||
22 | #include <string.h> |
||
23 | #include <sys/stat.h> |
||
24 | #include <sys/types.h> |
||
25 | #include <termios.h> |
||
26 | #include <time.h> |
||
27 | #include <unistd.h> |
||
28 | |||
29 | #define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0])) |
||
30 | #define MAX_STACK_FRAMES 64 |
||
31 | #define TRACER_PID_STRING "TracerPid:" |
||
32 | |||
33 | static int stack_nbr = 0; |
||
34 | static char _program_name[1024]; |
||
35 | |||
36 | static void* stack_traces[MAX_STACK_FRAMES]; |
||
37 | static char name_buf[4096]; |
||
38 | |||
39 | struct dl_iterate_callback_data { |
||
40 | int initialized; |
||
41 | intptr_t start; |
||
42 | } dethrace_dl_data; |
||
43 | |||
44 | static int dl_iterate_callback(struct dl_phdr_info* info, size_t size, void* data) { |
||
45 | struct dl_iterate_callback_data* callback_data = data; |
||
46 | |||
47 | if (strcmp(info->dlpi_name, "") == 0) { |
||
48 | callback_data->start = info->dlpi_addr; |
||
49 | } |
||
50 | return 0; |
||
51 | } |
||
52 | |||
53 | static intptr_t get_dethrace_offset(void) { |
||
54 | if (!dethrace_dl_data.initialized) { |
||
55 | dethrace_dl_data.initialized = 1; |
||
56 | dl_iterate_phdr(dl_iterate_callback, &dethrace_dl_data); |
||
57 | } |
||
58 | return dethrace_dl_data.start; |
||
59 | } |
||
60 | |||
61 | // Resolve symbol name and source location given the path to the executable and an address |
||
62 | int addr2line(char const* const program_name, void const* const addr) { |
||
14 | pmbaty | 63 | static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead |
1 | pmbaty | 64 | |
65 | /* have addr2line map the address to the related line in the code */ |
||
14 | pmbaty | 66 | sprintf(addr2line_cmd, "addr2line -f -p -e \"%s\" %p", program_name, addr - get_dethrace_offset()); // Pierre-Marie Baty -- enclosed program_name within quotes to support pathnames with spaces |
1 | pmbaty | 67 | |
68 | fprintf(stderr, "%d: ", stack_nbr++); |
||
69 | return system(addr2line_cmd); |
||
70 | } |
||
71 | |||
72 | static void print_stack_trace(void) { |
||
73 | int i, trace_size = 0; |
||
74 | char** messages = (char**)NULL; |
||
75 | |||
76 | fputs("\nStack trace:\n", stderr); |
||
77 | |||
78 | trace_size = backtrace(stack_traces, MAX_STACK_FRAMES); |
||
79 | messages = backtrace_symbols(stack_traces, trace_size); |
||
80 | |||
81 | /* skip the first couple stack frames (as they are this function and |
||
82 | our handler) and also skip the last frame as it's (always?) junk. */ |
||
83 | for (i = 3; i < (trace_size - 1); ++i) { |
||
84 | if (addr2line(_program_name, stack_traces[i]) != 0) { |
||
85 | printf(" error determining line # for: %s\n", messages[i]); |
||
86 | } |
||
87 | } |
||
88 | if (messages) { |
||
89 | free(messages); |
||
90 | } |
||
91 | } |
||
92 | |||
93 | static void signal_handler(int sig, siginfo_t* siginfo, void* context) { |
||
94 | (void)context; |
||
95 | fputs("\n******************\n", stderr); |
||
96 | |||
97 | switch (sig) { |
||
98 | case SIGSEGV: |
||
99 | fputs("Caught SIGSEGV\n", stderr); |
||
100 | break; |
||
101 | case SIGINT: |
||
102 | fputs("Caught SIGINT\n", stderr); |
||
103 | break; |
||
104 | case SIGFPE: |
||
105 | switch (siginfo->si_code) { |
||
106 | case FPE_INTDIV: |
||
107 | fputs("Caught SIGFPE: FPE_INTDIV\n", stderr); |
||
108 | break; |
||
109 | case FPE_INTOVF: |
||
110 | fputs("Caught SIGFPE: FPE_INTOVF\n", stderr); |
||
111 | break; |
||
112 | case FPE_FLTDIV: |
||
113 | fputs("Caught SIGFPE: FPE_FLTDIV\n", stderr); |
||
114 | break; |
||
115 | case FPE_FLTOVF: |
||
116 | fputs("Caught SIGFPE: FPE_FLTOVF\n", stderr); |
||
117 | break; |
||
118 | case FPE_FLTUND: |
||
119 | fputs("Caught SIGFPE: FPE_FLTUND\n", stderr); |
||
120 | break; |
||
121 | case FPE_FLTRES: |
||
122 | fputs("Caught SIGFPE: FPE_FLTRES\n", stderr); |
||
123 | break; |
||
124 | case FPE_FLTINV: |
||
125 | fputs("Caught SIGFPE: FPE_FLTINV\n", stderr); |
||
126 | break; |
||
127 | case FPE_FLTSUB: |
||
128 | fputs("Caught SIGFPE: FPE_FLTSUB\n", stderr); |
||
129 | break; |
||
130 | default: |
||
131 | fputs("Caught SIGFPE: Arithmetic Exception\n", stderr); |
||
132 | break; |
||
133 | } |
||
134 | break; |
||
135 | case SIGILL: |
||
136 | switch (siginfo->si_code) { |
||
137 | case ILL_ILLOPC: |
||
138 | fputs("Caught SIGILL: ILL_ILLOPC\n", stderr); |
||
139 | break; |
||
140 | case ILL_ILLOPN: |
||
141 | fputs("Caught SIGILL: ILL_ILLOPN\n", stderr); |
||
142 | break; |
||
143 | case ILL_ILLADR: |
||
144 | fputs("Caught SIGILL: ILL_ILLADR\n", stderr); |
||
145 | break; |
||
146 | case ILL_ILLTRP: |
||
147 | fputs("Caught SIGILL: ILL_ILLTRP\n", stderr); |
||
148 | break; |
||
149 | case ILL_PRVOPC: |
||
150 | fputs("Caught SIGILL: ILL_PRVOPC\n", stderr); |
||
151 | break; |
||
152 | case ILL_PRVREG: |
||
153 | fputs("Caught SIGILL: ILL_PRVREG\n", stderr); |
||
154 | break; |
||
155 | case ILL_COPROC: |
||
156 | fputs("Caught SIGILL: ILL_COPROC\n", stderr); |
||
157 | break; |
||
158 | case ILL_BADSTK: |
||
159 | fputs("Caught SIGILL: ILL_BADSTK\n", stderr); |
||
160 | break; |
||
161 | default: |
||
162 | fputs("Caught SIGILL: Illegal Instruction\n", stderr); |
||
163 | break; |
||
164 | } |
||
165 | break; |
||
166 | case SIGTERM: |
||
167 | fputs("Caught SIGTERM\n", stderr); |
||
168 | break; |
||
169 | case SIGABRT: |
||
170 | fputs("Caught SIGABRT\n", stderr); |
||
171 | break; |
||
172 | default: |
||
173 | break; |
||
174 | } |
||
175 | fputs("******************\n", stderr); |
||
176 | print_stack_trace(); |
||
177 | exit(1); |
||
178 | } |
||
179 | |||
180 | void resolve_full_path(char* path, const char* argv0) { |
||
181 | if (argv0[0] == '/') { // run with absolute path |
||
182 | strcpy(path, argv0); |
||
20 | pmbaty | 183 | } else { // run with relative path |
1 | pmbaty | 184 | if (NULL == getcwd(path, PATH_MAX)) { |
185 | perror("getcwd error"); |
||
186 | return; |
||
187 | } |
||
188 | strcat(path, "/"); |
||
189 | strcat(path, argv0); |
||
190 | } |
||
191 | } |
||
192 | |||
193 | void OS_InstallSignalHandler(char* program_name) { |
||
194 | resolve_full_path(_program_name, program_name); |
||
195 | |||
196 | /* setup alternate stack */ |
||
197 | { |
||
198 | stack_t ss = {}; |
||
199 | ss.ss_sp = malloc(2 * MINSIGSTKSZ); |
||
200 | if (ss.ss_sp == NULL) { |
||
201 | err(1, "malloc"); |
||
202 | } |
||
203 | ss.ss_size = 2 * MINSIGSTKSZ; |
||
204 | ss.ss_flags = 0; |
||
205 | |||
206 | if (sigaltstack(&ss, NULL) != 0) { |
||
207 | err(1, "sigaltstack"); |
||
208 | } |
||
209 | } |
||
210 | |||
211 | /* register our signal handlers */ |
||
212 | { |
||
213 | struct sigaction sig_action = {}; |
||
214 | sig_action.sa_sigaction = signal_handler; |
||
215 | sigemptyset(&sig_action.sa_mask); |
||
216 | |||
217 | sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK; |
||
218 | |||
219 | if (sigaction(SIGSEGV, &sig_action, NULL) != 0) { |
||
220 | err(1, "sigaction"); |
||
221 | } |
||
222 | if (sigaction(SIGFPE, &sig_action, NULL) != 0) { |
||
223 | err(1, "sigaction"); |
||
224 | } |
||
225 | if (sigaction(SIGINT, &sig_action, NULL) != 0) { |
||
226 | err(1, "sigaction"); |
||
227 | } |
||
228 | if (sigaction(SIGILL, &sig_action, NULL) != 0) { |
||
229 | err(1, "sigaction"); |
||
230 | } |
||
231 | if (sigaction(SIGTERM, &sig_action, NULL) != 0) { |
||
232 | err(1, "sigaction"); |
||
233 | } |
||
234 | if (sigaction(SIGABRT, &sig_action, NULL) != 0) { |
||
235 | err(1, "sigaction"); |
||
236 | } |
||
237 | } |
||
238 | } |
||
239 | |||
240 | FILE* OS_fopen(const char* pathname, const char* mode) { |
||
241 | FILE* f = fopen(pathname, mode); |
||
242 | if (f != NULL) { |
||
243 | return f; |
||
244 | } |
||
245 | char buffer[512]; |
||
246 | char buffer2[512]; |
||
247 | strcpy(buffer, pathname); |
||
248 | strcpy(buffer2, pathname); |
||
249 | char* pDirName = dirname(buffer); |
||
250 | char* pBaseName = basename(buffer2); |
||
251 | DIR* pDir = opendir(pDirName); |
||
252 | if (pDir == NULL) { |
||
253 | return NULL; |
||
254 | } |
||
255 | for (struct dirent* pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) { |
||
256 | if (strcasecmp(pBaseName, pDirent->d_name) == 0) { |
||
257 | strcat(pDirName, "/"); |
||
258 | strcat(pDirName, pDirent->d_name); |
||
259 | f = fopen(pDirName, mode); |
||
260 | break; |
||
261 | } |
||
262 | } |
||
263 | closedir(pDir); |
||
20 | pmbaty | 264 | if (f == NULL) { |
265 | fprintf(stderr, "Failed to open \"%s\" (%s)\n", pathname, strerror(errno)); |
||
11 | pmbaty | 266 | } |
1 | pmbaty | 267 | return f; |
268 | } |
||
269 | |||
270 | size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen) { |
||
271 | struct termios old, new; |
||
272 | char c; |
||
273 | size_t len; |
||
274 | |||
275 | tcgetattr(STDIN_FILENO, &old); |
||
276 | new = old; |
||
277 | new.c_lflag &= ~(ICANON | ECHO); |
||
278 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &new); |
||
279 | |||
280 | len = 0; |
||
281 | pBuffer[len] = '\0'; |
||
282 | while (1) { |
||
283 | if (read(STDIN_FILENO, &c, 1) == 1) { |
||
284 | if (c == 0x7f) { |
||
285 | if (len > 0) { |
||
286 | len--; |
||
287 | pBuffer[len] = '\0'; |
||
288 | printf("\033[1D \033[1D"); |
||
289 | fflush(stdout); |
||
290 | continue; |
||
291 | } |
||
292 | } else if (c == '\r' || c == '\n') { |
||
293 | printf("\n"); |
||
294 | fflush(stdout); |
||
295 | break; |
||
296 | } else if (len < pBufferLen - 1) { |
||
297 | if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { |
||
298 | pBuffer[len] = c; |
||
299 | printf("*"); |
||
300 | fflush(stdout); |
||
301 | len++; |
||
302 | pBuffer[len] = '\0'; |
||
303 | } |
||
304 | } |
||
305 | } |
||
306 | } |
||
307 | |||
308 | tcsetattr(STDIN_FILENO, TCSANOW, &old); |
||
309 | return len; |
||
310 | } |
||
311 | |||
312 | char* OS_Dirname(const char* path) { |
||
313 | strcpy(name_buf, path); |
||
314 | return dirname(name_buf); |
||
315 | } |
||
316 | |||
317 | char* OS_Basename(const char* path) { |
||
318 | strcpy(name_buf, path); |
||
319 | return basename(name_buf); |
||
320 | } |
||
321 | |||
322 | #endif // __linux__ |