1 //
2 // Copyright (C) 2011-2018 Nick Gasson
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
16 //
17
18 #if defined(__MINGW32__)
19 #define WINVER 0x0A00
20 #define _WIN32_WINNT 0x0A00
21 #include <windows.h>
22 #include <DbgHelp.h>
23 #include <fileapi.h>
24 #include <psapi.h>
25 #endif
26
27 #include "util.h"
28 #include "ident.h"
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdbool.h>
36 #ifdef HAVE_EXECINFO_H
37 #include <execinfo.h>
38 #endif
39 #include <signal.h>
40 #include <stdint.h>
41 #include <unistd.h>
42 #include <ctype.h>
43 #include <assert.h>
44 #include <limits.h>
45 #include <time.h>
46
47 #include <sys/types.h>
48 #include <sys/time.h>
49 #ifdef HAVE_SYS_PTRACE_H
50 #include <sys/ptrace.h>
51 #endif
52 #ifdef HAVE_SYS_SYSCTL_H
53 #include <sys/sysctl.h>
54 #endif
55 #ifndef __MINGW32__
56 #include <sys/mman.h>
57 #include <sys/wait.h>
58 #include <sys/resource.h>
59 #include <sys/file.h>
60 #include <sys/stat.h>
61 #include <fcntl.h>
62 #endif
63
64 #ifdef HAVE_SYS_PRCTL_H
65 #include <sys/prctl.h>
66 #endif
67
68 #ifdef __CYGWIN__
69 #include <process.h>
70 #endif
71
72 #if defined(HAVE_UCONTEXT_H)
73 #include <ucontext.h>
74 #elif defined(HAVE_SYS_UCONTEXT_H)
75 #include <sys/ucontext.h>
76 #endif
77
78 #ifdef HAVE_LIBDW
79 #include <elfutils/libdw.h>
80 #include <elfutils/libdwfl.h>
81 #include <dwarf.h>
82 #include <unwind.h>
83 #endif
84
85 #ifdef __MACH__
86 #include <mach/clock.h>
87 #include <mach/mach.h>
88 #endif
89
90 #define N_TRACE_DEPTH 16
91 #define ERROR_SZ 1024
92 #define PAGINATE_RIGHT 72
93 #define TRACE_MAX_LINE 256
94
95 #define ANSI_RESET 0
96 #define ANSI_BOLD 1
97 #define ANSI_FG_BLACK 30
98 #define ANSI_FG_RED 31
99 #define ANSI_FG_GREEN 32
100 #define ANSI_FG_YELLOW 33
101 #define ANSI_FG_BLUE 34
102 #define ANSI_FG_MAGENTA 35
103 #define ANSI_FG_CYAN 36
104 #define ANSI_FG_WHITE 37
105
106 #define MAX_FMT_BUFS 32
107 #define MAX_PRINTF_BUFS 8
108
109 typedef void (*print_fn_t)(const char *fmt, ...);
110
111 static void def_error_fn(const char *msg, const loc_t *loc);
112 static void show_hint(void);
113
114 typedef struct guard guard_t;
115 typedef struct option option_t;
116 typedef struct hint hint_t;
117
118 typedef enum {
119 OPTION_INT,
120 OPTION_STRING
121 } option_kind_t;
122
123 typedef union {
124 int i;
125 char *s;
126 } optval_t;
127
128 struct option {
129 option_t *next;
130 option_kind_t kind;
131 ident_t key;
132 optval_t value;
133 };
134
135 struct hint {
136 hint_fn_t func;
137 char *str;
138 void *context;
139 loc_t loc;
140 hint_t *next;
141 };
142
143 struct color_escape {
144 const char *name;
145 int value;
146 };
147
148 struct guard {
149 const char *tag;
150 uintptr_t base;
151 uintptr_t limit;
152 guard_t *next;
153 };
154
155 struct text_buf {
156 char *buf;
157 size_t alloc;
158 size_t len;
159 };
160
161 static error_fn_t error_fn = def_error_fn;
162 static fatal_fn_t fatal_fn = NULL;
163 static bool want_color = false;
164 static bool error_force_plain = false;
165 static struct option *options = NULL;
166 static guard_t *guards;
167 static message_style_t message_style = MESSAGE_FULL;
168 static hint_t *hints = NULL;
169
170 static const struct color_escape escapes[] = {
171 { "", ANSI_RESET },
172 { "bold", ANSI_BOLD },
173 { "black", ANSI_FG_BLACK },
174 { "red", ANSI_FG_RED },
175 { "green", ANSI_FG_GREEN },
176 { "yellow", ANSI_FG_YELLOW },
177 { "blue", ANSI_FG_BLUE },
178 { "magenta", ANSI_FG_MAGENTA },
179 { "cyan", ANSI_FG_CYAN },
180 { "white", ANSI_FG_WHITE },
181 };
182
filter_color(const char * str,bool force_plain)183 static char *filter_color(const char *str, bool force_plain)
184 {
185 // Replace color strings like "$red$foo$$bar" with ANSI escaped
186 // strings like "\033[31mfoo\033[0mbar"
187
188 const size_t maxlen = strlen(str) * 2;
189 char *copy = xmalloc(maxlen);
190 char *p = copy;
191 char *eptr = copy + maxlen;
192
193 const char *escape_start = NULL;
194
195 while (*str != '\0') {
196 if (*str == '$') {
197 if (escape_start == NULL)
198 escape_start = str;
199 else {
200 const char *e = escape_start + 1;
201 const size_t len = str - e;
202
203 if (want_color && !force_plain) {
204 bool found = false;
205 for (int i = 0; i < ARRAY_LEN(escapes); i++) {
206 if (strncmp(e, escapes[i].name, len) == 0) {
207 p += snprintf(p, eptr - p, "\033[%dm", escapes[i].value);
208 found = true;
209 break;
210 }
211 }
212
213 if (!found) {
214 strncpy(p, escape_start, len + 1);
215 p += len + 1;
216 escape_start = str;
217 }
218 else
219 escape_start = NULL;
220 }
221 else
222 escape_start = NULL;
223 }
224 }
225 else if (escape_start == NULL)
226 *p++ = *str;
227
228 ++str;
229 }
230
231 if (escape_start != NULL) {
232 const size_t len = str - escape_start;
233 strncpy(p, escape_start, len + 1);
234 p += len + 1;
235 }
236
237 *p = '\0';
238
239 return copy;
240 }
241
paginate_msg(const char * fmt,va_list ap,int start,int left,int right)242 static void paginate_msg(const char *fmt, va_list ap,
243 int start, int left, int right)
244 {
245 char *strp = xvasprintf(fmt, ap);
246
247 char *filtered = filter_color(strp, false);
248
249 const char *p = filtered;
250 int col = start;
251 bool escape = false;
252 while (*p != '\0') {
253 if ((*p == '\n') || (*p == '\r') || (isspace((int)*p) && col >= right)) {
254 // Can break line here
255 fputc('\n', stderr);
256 if (*p == '\r')
257 col = 0;
258 else {
259 for (col = 0; col < left; col++)
260 fputc(' ', stderr);
261 }
262 }
263 else {
264 fputc(*p, stderr);
265 if (*p == '\033')
266 escape = true;
267 else if (escape) {
268 if (*p == 'm')
269 escape = false;
270 }
271 else
272 ++col;
273 }
274 ++p;
275 }
276 fputc('\n', stderr);
277
278 #ifdef __MINGW32__
279 fflush(stderr);
280 #endif
281
282 free(filtered);
283 free(strp);
284 }
285
set_attr(int attr)286 static void set_attr(int attr)
287 {
288 if (want_color)
289 fprintf(stderr, "\033[%dm", attr);
290 }
291
xmalloc(size_t size)292 void *xmalloc(size_t size)
293 {
294 void *p = malloc(size);
295 if (p == NULL)
296 fatal("memory exhausted (malloc %lu)", (long unsigned)size);
297 return p;
298 }
299
xcalloc(size_t size)300 void *xcalloc(size_t size)
301 {
302 void *p = calloc(1, size);
303 if (p == NULL)
304 fatal("memory exhausted (calloc %lu)", (long unsigned)size);
305 return p;
306 }
307
xrealloc(void * ptr,size_t size)308 void *xrealloc(void *ptr, size_t size)
309 {
310 ptr = realloc(ptr, size);
311 if (ptr == NULL)
312 fatal("memory exhausted (realloc %lu)", (long unsigned)size);
313 return ptr;
314 }
315
xstrdup(const char * str)316 char *xstrdup(const char *str)
317 {
318 char *copy = strdup(str);
319 if (copy == NULL)
320 fatal("memory exhausted (strdup %p)", str);
321 return copy;
322 }
323
xvasprintf(const char * fmt,va_list ap)324 char *xvasprintf(const char *fmt, va_list ap)
325 {
326 char *strp = NULL;
327 if (vasprintf(&strp, fmt, ap) < 0)
328 abort();
329 return strp;
330 }
331
xasprintf(const char * fmt,...)332 char *xasprintf(const char *fmt, ...)
333 {
334 va_list ap;
335 va_start(ap, fmt);
336 char *strp = xvasprintf(fmt, ap);
337 va_end(ap);
338 return strp;
339 }
340
fmt_color(int color,const char * prefix,const char * fmt,va_list ap)341 static void fmt_color(int color, const char *prefix,
342 const char *fmt, va_list ap)
343 {
344 set_attr(color);
345 if (message_style == MESSAGE_COMPACT)
346 fprintf(stderr, "%c%s: ", tolower((int)prefix[0]), prefix + 1);
347 else
348 fprintf(stderr, "** %s: ", prefix);
349 set_attr(ANSI_RESET);
350 paginate_msg(fmt, ap, strlen(prefix) + 5, 10,
351 (message_style == MESSAGE_COMPACT) ? INT_MAX : PAGINATE_RIGHT);
352 }
353
errorf(const char * fmt,...)354 void errorf(const char *fmt, ...)
355 {
356 va_list ap;
357 va_start(ap, fmt);
358 fmt_color(ANSI_FG_RED, "Error", fmt, ap);
359 va_end(ap);
360 }
361
warnf(const char * fmt,...)362 void warnf(const char *fmt, ...)
363 {
364 va_list ap;
365 va_start(ap, fmt);
366 fmt_color(ANSI_FG_YELLOW, "Warning", fmt, ap);
367 va_end(ap);
368 }
369
notef(const char * fmt,...)370 void notef(const char *fmt, ...)
371 {
372 va_list ap;
373 va_start(ap, fmt);
374 fmt_color(ANSI_RESET, "Note", fmt, ap);
375 va_end(ap);
376 }
377
fatalf(const char * fmt,...)378 static void fatalf(const char *fmt, ...)
379 {
380 va_list ap;
381 va_start(ap, fmt);
382 fmt_color(ANSI_FG_RED, "Fatal", fmt, ap);
383 va_end(ap);
384 }
385
def_error_fn(const char * msg,const loc_t * loc)386 static void def_error_fn(const char *msg, const loc_t *loc)
387 {
388 if (message_style == MESSAGE_COMPACT)
389 fmt_loc(stderr, loc);
390 errorf("%s", msg);
391 if (message_style == MESSAGE_FULL)
392 fmt_loc(stderr, loc);
393 }
394
prepare_msg(const char * fmt,va_list ap,bool force_plain)395 static char *prepare_msg(const char *fmt, va_list ap, bool force_plain)
396 {
397 char *strp LOCAL = xvasprintf(fmt, ap);
398 return filter_color(strp, force_plain);
399 }
400
msg_at(print_fn_t fn,const loc_t * loc,const char * fmt,va_list ap)401 static void msg_at(print_fn_t fn, const loc_t *loc, const char *fmt, va_list ap)
402 {
403 char *strp = prepare_msg(fmt, ap, false);
404 if (message_style == MESSAGE_COMPACT)
405 fmt_loc(stderr, loc);
406 (*fn)("%s", strp);
407 if (message_style == MESSAGE_FULL)
408 fmt_loc(stderr, loc);
409 free(strp);
410 }
411
color_vfprintf(FILE * f,const char * fmt,va_list ap)412 static int color_vfprintf(FILE *f, const char *fmt, va_list ap)
413 {
414 char *strp LOCAL = prepare_msg(fmt, ap, false);
415
416 bool escape = false;
417 int len = 0;
418 for (const char *p = strp; *p != '\0'; p++) {
419 if (*p == '\033')
420 escape = true;
421 if (escape)
422 escape = (*p != 'm');
423 else
424 len += 1;
425 }
426
427 fputs(strp, f);
428 return len;
429 }
430
color_fprintf(FILE * f,const char * fmt,...)431 static int color_fprintf(FILE *f, const char *fmt, ...)
432 {
433 va_list ap;
434 va_start(ap, fmt);
435 const int len = color_vfprintf(f, fmt, ap);
436 va_end(ap);
437 return len;
438 }
439
color_printf(const char * fmt,...)440 int color_printf(const char *fmt, ...)
441 {
442 va_list ap;
443 va_start(ap, fmt);
444 int rc = color_vprintf(fmt, ap);
445 va_end(ap);
446 return rc;
447 }
448
color_vprintf(const char * fmt,va_list ap)449 int color_vprintf(const char *fmt, va_list ap)
450 {
451 char *strp LOCAL = prepare_msg(fmt, ap, false);
452
453 bool escape = false;
454 int len = 0;
455 for (const char *p = strp; *p != '\0'; p++) {
456 if (*p == '\033')
457 escape = true;
458 if (escape)
459 escape = (*p != 'm');
460 else
461 len += 1;
462 }
463
464 printf("%s", strp);
465 return len;
466 }
467
error_at(const loc_t * loc,const char * fmt,...)468 void error_at(const loc_t *loc, const char *fmt, ...)
469 {
470 va_list ap;
471 va_start(ap, fmt);
472
473 char *strp LOCAL = prepare_msg(fmt, ap, error_force_plain);
474 error_fn(strp, loc != NULL ? loc : &LOC_INVALID);
475 show_hint();
476
477 va_end(ap);
478 }
479
catch_in_unit_test(print_fn_t fn,const loc_t * loc,const char * fmt,va_list ap)480 static void catch_in_unit_test(print_fn_t fn, const loc_t *loc,
481 const char *fmt, va_list ap)
482 {
483 if (opt_get_int("unit-test")) {
484 char *strp LOCAL = prepare_msg(fmt, ap, error_force_plain);
485 error_fn(strp, loc != NULL ? loc : &LOC_INVALID);
486 }
487 else
488 msg_at(fn, loc, fmt, ap);
489 }
490
default_hint_fn(void * arg)491 static void default_hint_fn(void *arg)
492 {
493 hint_t *h = arg;
494 note_at(&(h->loc), "%s", h->str);
495 }
496
pop_hint(void)497 static void pop_hint(void)
498 {
499 hint_t *tmp = hints->next;
500 free(hints->str);
501 free(hints);
502 hints = tmp;
503 }
504
show_hint(void)505 static void show_hint(void)
506 {
507 static bool inside = false;
508
509 if (inside)
510 return;
511
512 inside = true;
513
514 while (hints != NULL) {
515 (*hints->func)(hints->context);
516 pop_hint();
517 }
518
519 inside = false;
520 }
521
set_hint_fn(hint_fn_t fn,void * context)522 void set_hint_fn(hint_fn_t fn, void *context)
523 {
524 hint_t *h = xmalloc(sizeof(hint_t));
525 h->func = fn;
526 h->str = NULL;
527 h->context = context;
528 h->next = hints;
529 h->loc = LOC_INVALID;
530
531 hints = h;
532 }
533
clear_hint(void)534 void clear_hint(void)
535 {
536 while (hints != NULL)
537 pop_hint();
538 }
539
hint_at(const loc_t * loc,const char * fmt,...)540 void hint_at(const loc_t *loc, const char *fmt, ...)
541 {
542 va_list ap;
543 va_start(ap, fmt);
544
545 hint_t *h = xmalloc(sizeof(hint_t));
546 h->func = default_hint_fn;
547 h->str = prepare_msg(fmt, ap, error_force_plain);
548 h->context = h;
549 h->loc = *loc;
550 h->next = hints;
551
552 va_end(ap);
553
554 hints = h;
555 }
556
warn_at(const loc_t * loc,const char * fmt,...)557 void warn_at(const loc_t *loc, const char *fmt, ...)
558 {
559 va_list ap;
560 va_start(ap, fmt);
561 catch_in_unit_test(warnf, loc, fmt, ap);
562 show_hint();
563 va_end(ap);
564 }
565
note_at(const loc_t * loc,const char * fmt,...)566 void note_at(const loc_t *loc, const char *fmt, ...)
567 {
568 va_list ap;
569 va_start(ap, fmt);
570 catch_in_unit_test(notef, loc, fmt, ap);
571 show_hint();
572 va_end(ap);
573 }
574
fatal_at(const loc_t * loc,const char * fmt,...)575 void fatal_at(const loc_t *loc, const char *fmt, ...)
576 {
577 va_list ap;
578 va_start(ap, fmt);
579 catch_in_unit_test(fatalf, loc, fmt, ap);
580 show_hint();
581 va_end(ap);
582
583 if (fatal_fn != NULL)
584 (*fatal_fn)();
585
586 exit(EXIT_FAILURE);
587 }
588
set_error_fn(error_fn_t fn,bool want_color)589 error_fn_t set_error_fn(error_fn_t fn, bool want_color)
590 {
591 error_fn_t old = error_fn;
592 error_fn = fn ?: def_error_fn;
593 error_force_plain = !want_color;
594 return old;
595 }
596
set_fatal_fn(fatal_fn_t fn)597 void set_fatal_fn(fatal_fn_t fn)
598 {
599 fatal_fn = fn;
600 }
601
fatal(const char * fmt,...)602 void fatal(const char *fmt, ...)
603 {
604 va_list ap;
605 va_start(ap, fmt);
606 fmt_color(ANSI_FG_RED, "Fatal", fmt, ap);
607 show_hint();
608 va_end(ap);
609
610 if (fatal_fn != NULL)
611 (*fatal_fn)();
612
613 exit(EXIT_FAILURE);
614 }
615
fatal_trace(const char * fmt,...)616 void fatal_trace(const char *fmt, ...)
617 {
618 va_list ap;
619 va_start(ap, fmt);
620 fmt_color(ANSI_FG_RED, "Fatal", fmt, ap);
621 va_end(ap);
622
623 #ifndef NO_STACK_TRACE
624 show_stacktrace();
625 #endif // !NO_STACK_TRACE
626
627 exit(EXIT_FAILURE);
628 }
629
fatal_errno(const char * fmt,...)630 void fatal_errno(const char *fmt, ...)
631 {
632 va_list ap;
633 va_start(ap, fmt);
634
635 set_attr(ANSI_FG_RED);
636 fprintf(stderr, "** Fatal: ");
637 set_attr(ANSI_RESET);
638 vfprintf(stderr, fmt, ap);
639
640 #ifdef __MINGW32__
641 LPSTR mbuf = NULL;
642 FormatMessage(
643 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
644 | FORMAT_MESSAGE_IGNORE_INSERTS,
645 NULL,
646 GetLastError(),
647 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
648 (LPSTR)&mbuf, 0, NULL);
649
650 fprintf(stderr, ": %s", mbuf);
651 fflush(stderr);
652
653 LocalFree(mbuf);
654 #else
655 fprintf(stderr, ": %s\n", strerror(errno));
656 #endif
657
658 va_end(ap);
659
660 exit(EXIT_FAILURE);
661 }
662
fmt_loc(FILE * f,const struct loc * loc)663 void fmt_loc(FILE *f, const struct loc *loc)
664 {
665 if (loc == NULL || loc->first_line == LINE_INVALID || loc->file == NULL)
666 return;
667
668 if (message_style == MESSAGE_COMPACT) {
669 fprintf(f, "%s:%d:%d: ", istr(loc->file), loc->first_line,
670 loc->first_column + 1);
671 return;
672 }
673
674 fprintf(f, "\tFile %s, Line %u\n", istr(loc->file), loc->first_line);
675
676 if (loc->linebuf == NULL)
677 return;
678
679 const char *lb = loc->linebuf;
680 char buf[80];
681 size_t i = 0;
682 while (i < sizeof(buf) - 1 && *lb != '\0' && *lb != '\n') {
683 if (*lb == '\t')
684 buf[i++] = ' ';
685 else
686 buf[i++] = *lb;
687 ++lb;
688 }
689 buf[i] = '\0';
690
691 // Print ... if error location spans multiple lines
692 bool many_lines = (loc->first_line != loc->last_line)
693 || (i == sizeof(buf) - 1 && i <= loc->last_column);
694 int last_col = many_lines ? strlen(buf) + 3 : loc->last_column;
695
696 set_attr(ANSI_FG_CYAN);
697 fprintf(f, " %s%s\n", buf, many_lines ? " ..." : "");
698 if (last_col >= loc->first_column) {
699 for (unsigned j = 0; j < loc->first_column + 4; j++)
700 fprintf(f, " ");
701 set_attr(ANSI_FG_GREEN);
702 for (unsigned j = 0; j < last_col - loc->first_column + 1; j++)
703 fprintf(f, "^");
704 set_attr(ANSI_RESET);
705 fprintf(f, "\n");
706 }
707
708 fflush(f);
709 }
710
711 #ifndef NO_STACK_TRACE
712
check_guard_page(uintptr_t addr)713 static bool check_guard_page(uintptr_t addr)
714 {
715 for (guard_t *it = guards; it != NULL; it = it->next) {
716 if ((addr >= it->base) && (addr < it->limit)) {
717 fatal_trace("accessed %d bytes beyond $cyan$%s$$ region",
718 (int)(addr - it->base), it->tag);
719 }
720 }
721
722 return false;
723 }
724
725 #if defined HAVE_LIBDW
die_has_pc(Dwarf_Die * die,Dwarf_Addr pc)726 static bool die_has_pc(Dwarf_Die* die, Dwarf_Addr pc)
727 {
728 Dwarf_Addr low, high;
729
730 if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) {
731 if (dwarf_lowpc(die, &low) != 0)
732 return false;
733 if (dwarf_highpc(die, &high) != 0) {
734 Dwarf_Attribute attr_mem;
735 Dwarf_Attribute* attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem);
736 Dwarf_Word value;
737 if (dwarf_formudata(attr, &value) != 0)
738 return false;
739 high = low + value;
740 }
741 return pc >= low && pc < high;
742 }
743
744 Dwarf_Addr base;
745 ptrdiff_t offset = 0;
746 while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) {
747 if (pc >= low && pc < high)
748 return true;
749 }
750
751 return false;
752 }
753
libdw_trace_iter(struct _Unwind_Context * ctx,void * param)754 static _Unwind_Reason_Code libdw_trace_iter(struct _Unwind_Context* ctx,
755 void *param)
756 {
757 static Dwfl *handle = NULL;
758 static Dwfl_Module *home = NULL;
759
760 if (handle == NULL) {
761 static Dwfl_Callbacks callbacks = {
762 .find_elf = dwfl_linux_proc_find_elf,
763 .find_debuginfo = dwfl_standard_find_debuginfo,
764 .debuginfo_path = NULL
765 };
766
767 if ((handle = dwfl_begin(&callbacks)) == NULL) {
768 warnf("failed to initialise dwfl");
769 return _URC_NORMAL_STOP;
770 }
771
772 dwfl_report_begin(handle);
773 if (dwfl_linux_proc_report(handle, getpid()) < 0) {
774 warnf("dwfl_linux_proc_report failed");
775 return _URC_NORMAL_STOP;
776 }
777 dwfl_report_end(handle, NULL, NULL);
778
779 home = dwfl_addrmodule(handle, (uintptr_t)libdw_trace_iter);
780 }
781
782 int *skip = param;
783 if (skip != NULL && *skip > 0) {
784 (*skip)--;
785 return _URC_NO_REASON;
786 }
787
788 int ip_before_instruction = 0;
789 uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
790
791 if (ip == 0)
792 return _URC_NO_REASON;
793 else if (!ip_before_instruction)
794 ip -= 1;
795
796 Dwfl_Module *mod = dwfl_addrmodule(handle, ip);
797
798 const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0);
799 const char *sym_name = dwfl_module_addrname(mod, ip);
800
801 Dwarf_Addr mod_bias = 0;
802 Dwarf_Die *die = dwfl_module_addrdie(mod, ip, &mod_bias);
803
804 if (die == NULL) {
805 // Hack to support Clang taken from backward-cpp
806 while ((die = dwfl_module_nextcu(mod, die, &mod_bias))) {
807 Dwarf_Die child;
808 if (dwarf_child(die, &child) != 0)
809 continue;
810
811 Dwarf_Die* iter = &child;
812 do {
813 switch (dwarf_tag(iter)) {
814 case DW_TAG_subprogram:
815 case DW_TAG_inlined_subroutine:
816 if (die_has_pc(iter, ip))
817 goto found_die_with_ip;
818 }
819 } while (dwarf_siblingof(iter, iter) == 0);
820 }
821 found_die_with_ip:
822 ;
823 }
824
825 Dwarf_Line* srcloc = dwarf_getsrc_die(die, ip - mod_bias);
826 const char* srcfile = dwarf_linesrc(srcloc, 0, 0);
827
828 int line = 0, col = 0;
829 dwarf_lineno(srcloc, &line);
830 dwarf_linecol(srcloc, &col);
831
832 color_printf("[$green$%p$$] ", (void *)ip);
833 if (mod != home)
834 color_printf("($red$%s$$) ", module_name);
835 if (srcfile != NULL)
836 color_printf("%s:%d ", srcfile, line);
837 if (sym_name != NULL)
838 color_printf("$yellow$%s$$", sym_name);
839 printf("\n");
840
841 FILE *f = fopen(srcfile, "r");
842 if (f != NULL) {
843 char buf[TRACE_MAX_LINE];
844 for (int i = 0; i < line + 1 && fgets(buf, sizeof(buf), f); i++) {
845 if (i < line - 2)
846 continue;
847
848 const size_t len = strlen(buf);
849 if (len <= 1)
850 continue;
851 else if (buf[len - 1] == '\n')
852 buf[len - 1] = '\0';
853
854 if (i == line - 1)
855 color_printf("$cyan$$bold$-->$$ $cyan$%s$$\n", buf);
856 else
857 color_printf(" $cyan$%s$$\n", buf);
858 }
859 fclose(f);
860 }
861
862 if (sym_name != NULL && strcmp(sym_name, "main") == 0)
863 return _URC_NORMAL_STOP;
864 else
865 return _URC_NO_REASON;
866 }
867 #elif defined HAVE_EXECINFO_H
868
print_trace(char ** messages,int trace_size)869 static void print_trace(char **messages, int trace_size)
870 {
871 fputs("\n-------- STACK TRACE --------\n", stderr);
872
873 for (int i = 0; i < trace_size; i++)
874 fprintf(stderr, "%s\n", messages[i]);
875
876 fputs("-----------------------------\n", stderr);
877
878 #ifdef __linux
879 color_fprintf(stderr, "\n$cyan$Hint: you can get better stack traces by "
880 "installing the libdw-dev package and reconfiguring$$\n");
881 #endif // __linux
882 }
883 #endif // HAVE_EXECINFO_H
884
885 #ifdef __MINGW32__
886
887 #ifdef __WIN64
win64_stacktrace(PCONTEXT context)888 static void win64_stacktrace(PCONTEXT context)
889 {
890 STACKFRAME64 stk;
891 memset(&stk, 0, sizeof(stk));
892
893 stk.AddrPC.Offset = context->Rip;
894 stk.AddrPC.Mode = AddrModeFlat;
895 stk.AddrStack.Offset = context->Rsp;
896 stk.AddrStack.Mode = AddrModeFlat;
897 stk.AddrFrame.Offset = context->Rbp;
898 stk.AddrFrame.Mode = AddrModeFlat;
899
900 fputs("\n-------- STACK TRACE --------\n", stderr);
901
902 HANDLE hProcess = GetCurrentProcess();
903
904 SymInitialize(hProcess, NULL, TRUE);
905
906 for (ULONG frame = 0; frame < 25; frame++) {
907 if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
908 hProcess,
909 GetCurrentThread(),
910 &stk,
911 context,
912 NULL,
913 SymFunctionTableAccess,
914 SymGetModuleBase,
915 NULL))
916 break;
917
918 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
919 PSYMBOL_INFO psym = (PSYMBOL_INFO)buffer;
920 psym->SizeOfStruct = sizeof(SYMBOL_INFO);
921 psym->MaxNameLen = MAX_SYM_NAME;
922
923 DWORD64 disp;
924 if (SymFromAddr(hProcess, stk.AddrPC.Offset, &disp, psym)) {
925 fprintf(stderr, "%p %s+0x%x\n", (void *)(uintptr_t)stk.AddrPC.Offset,
926 psym->Name, (int)disp);
927 }
928 else
929 fprintf(stderr, "%p ???\n", (void *)(uintptr_t)stk.AddrPC.Offset);
930 }
931
932 fputs("-----------------------------\n", stderr);
933 fflush(stderr);
934
935 SymCleanup(hProcess);
936 }
937 #endif // __WIN64
938
939 WINAPI
win32_exception_handler(EXCEPTION_POINTERS * ExceptionInfo)940 static LONG win32_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
941 {
942 DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
943 PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
944
945 #ifdef __WIN64
946 DWORD64 ip = ExceptionInfo->ContextRecord->Rip;
947 #else
948 DWORD ip = ExceptionInfo->ContextRecord->Eip;
949 #endif
950
951 const char *what = "???";
952 switch (code) {
953 case EXCEPTION_ACCESS_VIOLATION:
954 what = "EXCEPTION_ACCESS_VIOLATION";
955 addr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
956 break;
957 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
958 what = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
959 break;
960 case EXCEPTION_BREAKPOINT:
961 what = "EXCEPTION_BREAKPOINT";
962 break;
963 case EXCEPTION_DATATYPE_MISALIGNMENT:
964 what = "EXCEPTION_DATATYPE_MISALIGNMENT";
965 break;
966 case EXCEPTION_ILLEGAL_INSTRUCTION:
967 what = "EXCEPTION_ILLEGAL_INSTRUCTION";
968 break;
969 case EXCEPTION_IN_PAGE_ERROR:
970 what = "EXCEPTION_IN_PAGE_ERROR";
971 break;
972 case EXCEPTION_INT_DIVIDE_BY_ZERO:
973 what = "EXCEPTION_INT_DIVIDE_BY_ZERO";
974 break;
975 case EXCEPTION_INT_OVERFLOW:
976 what = "EXCEPTION_INT_OVERFLOW";
977 break;
978 case EXCEPTION_PRIV_INSTRUCTION:
979 what = "EXCEPTION_PRIV_INSTRUCTION";
980 break;
981 case EXCEPTION_STACK_OVERFLOW:
982 what = "EXCEPTION_STACK_OVERFLOW";
983 break;
984 }
985
986 if (code == EXCEPTION_ACCESS_VIOLATION)
987 check_guard_page((uintptr_t)addr);
988
989 color_fprintf(stderr, "\n$red$$bold$*** Caught exception %x (%s)",
990 code, what);
991
992 switch (code) {
993 case EXCEPTION_ACCESS_VIOLATION:
994 case EXCEPTION_ILLEGAL_INSTRUCTION:
995 fprintf(stderr, " [address=%p, ip=%p]", (void *)addr, (void*)ip);
996 break;
997 }
998
999 color_fprintf(stderr, " ***$$\n");
1000 fflush(stderr);
1001
1002 #ifdef __WIN64
1003 if (code != EXCEPTION_STACK_OVERFLOW )
1004 win64_stacktrace(ExceptionInfo->ContextRecord);
1005 #endif
1006
1007 return EXCEPTION_EXECUTE_HANDLER;
1008 }
1009
1010 #endif // __MINGW32__
1011
show_stacktrace(void)1012 void show_stacktrace(void)
1013 {
1014 #if defined HAVE_LIBDW
1015 int skip = 1;
1016 _Unwind_Backtrace(libdw_trace_iter, &skip);
1017 #elif defined HAVE_EXECINFO_H
1018 void *trace[N_TRACE_DEPTH];
1019 char **messages = NULL;
1020 int trace_size = 0;
1021
1022 trace_size = backtrace(trace, N_TRACE_DEPTH);
1023 messages = backtrace_symbols(trace, trace_size);
1024
1025 print_trace(messages, trace_size);
1026
1027 free(messages);
1028 #elif defined __WIN64
1029 CONTEXT context;
1030 RtlCaptureContext(&context);
1031
1032 win64_stacktrace(&context);
1033 #endif
1034 }
1035
1036 #ifndef __MINGW32__
signame(int sig)1037 static const char *signame(int sig)
1038 {
1039 switch (sig) {
1040 case SIGSEGV: return "SIGSEGV";
1041 case SIGABRT: return "SIGABRT";
1042 case SIGILL: return "SIGILL";
1043 case SIGFPE: return "SIGFPE";
1044 case SIGUSR1: return "SIGUSR1";
1045 case SIGBUS: return "SIGBUS";
1046 default: return "???";
1047 }
1048 }
1049
bt_sighandler(int sig,siginfo_t * info,void * secret)1050 static void bt_sighandler(int sig, siginfo_t *info, void *secret)
1051 {
1052 ucontext_t *uc = (ucontext_t*)secret;
1053 uintptr_t ip = uc->PC_FROM_UCONTEXT;
1054
1055 if (sig == SIGSEGV)
1056 check_guard_page((uintptr_t)info->si_addr);
1057
1058 color_fprintf(stderr, "\n$red$$bold$*** Caught signal %d (%s)",
1059 sig, signame(sig));
1060
1061 switch (sig) {
1062 case SIGSEGV:
1063 case SIGILL:
1064 case SIGFPE:
1065 case SIGBUS:
1066 fprintf(stderr, " [address=%p, ip=%p]", info->si_addr, (void*)ip);
1067 break;
1068 }
1069
1070 color_fprintf(stderr, " ***$$\n");
1071
1072 #if defined HAVE_LIBDW
1073 fprintf(stderr, "\n");
1074 int skip = 2;
1075 _Unwind_Backtrace(libdw_trace_iter, &skip);
1076 #elif defined HAVE_EXECINFO_H
1077 void *trace[N_TRACE_DEPTH];
1078 int trace_size = 0;
1079 char **messages = NULL;
1080
1081 trace_size = backtrace(trace, N_TRACE_DEPTH);
1082
1083 // Overwrite sigaction with caller's address
1084 trace[1] = (void*)ip;
1085
1086 messages = backtrace_symbols(trace, trace_size);
1087
1088 // Skip first stack frame (points here)
1089 print_trace(messages + 1, trace_size - 1);
1090
1091 free(messages);
1092 #endif
1093
1094 if (sig != SIGUSR1)
1095 exit(2);
1096 }
1097 #endif // !__MINGW32__
1098
1099 #endif // NO_STACK_TRACE
1100
1101 #if defined __linux__
scan_file_for_token(const char * file,const char * token)1102 static bool scan_file_for_token(const char *file, const char *token)
1103 {
1104 bool found = false;
1105 FILE *f = fopen(file, "r");
1106 if (f != NULL) {
1107 char buf[1024];
1108 while (!found && fgets(buf, sizeof(buf), f)) {
1109 if (strstr(buf, token))
1110 found = true;
1111 }
1112 fclose(f);
1113 }
1114
1115 return found;
1116 }
1117 #endif
1118
is_debugger_running(void)1119 bool is_debugger_running(void)
1120 {
1121 static int cached = -1;
1122 if (cached != -1)
1123 return cached;
1124
1125 #if defined __APPLE__
1126
1127 struct kinfo_proc info;
1128 info.kp_proc.p_flag = 0;
1129
1130 int mib[4];
1131 mib[0] = CTL_KERN;
1132 mib[1] = KERN_PROC;
1133 mib[2] = KERN_PROC_PID;
1134 mib[3] = getpid();
1135
1136 size_t size = sizeof(info);
1137 int rc = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
1138 if (rc != 0)
1139 fatal_errno("sysctl");
1140
1141 return (cached = ((info.kp_proc.p_flag & P_TRACED) != 0));
1142
1143 #elif defined __linux
1144
1145 // Hack to detect if Valgrind is running
1146 if (scan_file_for_token("/proc/self/maps", "vgpreload"))
1147 return (cached = true);
1148
1149 // Ptrace technique below doesn't work on WSL
1150 if (scan_file_for_token("/proc/version", "Microsoft"))
1151 return (cached = false);
1152
1153 #ifdef PR_SET_PTRACER
1154 // For Linux 3.4 and later allow tracing from any proccess
1155 // Failure is harmless as this may not be implemented even in a >3.4 kernel
1156 (void)prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
1157 #endif // PR_SET_PTRACER
1158
1159 pid_t pid = fork();
1160
1161 if (pid == -1)
1162 fatal_errno("fork");
1163 else if (pid == 0) {
1164 int ppid = getppid();
1165
1166 // Try to trace the parent: if we can then GDB is not running
1167 if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0) {
1168 // Wait for the parent to stop and continue it
1169 waitpid(ppid, NULL, 0);
1170 ptrace(PTRACE_CONT, NULL, NULL);
1171
1172 // Detach
1173 ptrace(PTRACE_DETACH, ppid, NULL, NULL);
1174
1175 // Able to trace so debugger not present
1176 exit(0);
1177 }
1178 else {
1179 // Trace failed so debugger is present
1180 exit(1);
1181 }
1182 }
1183 else {
1184 int status;
1185 waitpid(pid, &status, 0);
1186 return (cached = WEXITSTATUS(status));
1187 }
1188
1189 #else
1190
1191 // Not able to detect debugger on this platform
1192 return (cached = false);
1193
1194 #endif
1195 }
1196
1197 #ifdef __linux
gdb_sighandler(int sig,siginfo_t * info)1198 static void gdb_sighandler(int sig, siginfo_t *info)
1199 {
1200 char exe[256];
1201 if (readlink("/proc/self/exe", exe, sizeof(exe)) < 0)
1202 fatal_errno("readlink");
1203
1204 pid_t pp = getpid();
1205
1206 pid_t p = fork();
1207 if (p == 0) {
1208 char *pid = xasprintf("%d", pp);
1209 execl("/usr/bin/gdb", "gdb", "-ex", "cont", exe, pid, NULL);
1210 free(pid);
1211 fatal_errno("execl");
1212 }
1213 else if (p < 0)
1214 fatal_errno("fork");
1215 else {
1216 // Allow a little time for GDB to start before dropping
1217 // into the default signal handler
1218 sleep(1);
1219 signal(sig, SIG_DFL);
1220 }
1221 }
1222 #endif // __linux
1223
register_trace_signal_handlers(void)1224 void register_trace_signal_handlers(void)
1225 {
1226 #if defined __MINGW32__
1227
1228 SetUnhandledExceptionFilter(win32_exception_handler);
1229
1230 #elif !defined NO_STACK_TRACE
1231 if (is_debugger_running())
1232 return;
1233
1234 struct sigaction sa;
1235 sa.sa_sigaction = (void*)bt_sighandler;
1236 sigemptyset(&sa.sa_mask);
1237 sa.sa_flags = SA_RESTART | SA_SIGINFO;
1238
1239 sigaction(SIGSEGV, &sa, NULL);
1240 sigaction(SIGUSR1, &sa, NULL);
1241 sigaction(SIGFPE, &sa, NULL);
1242 sigaction(SIGBUS, &sa, NULL);
1243 sigaction(SIGILL, &sa, NULL);
1244 sigaction(SIGABRT, &sa, NULL);
1245 #endif // NO_STACK_TRACE
1246 }
1247
register_gdb_signal_handlers(void)1248 void register_gdb_signal_handlers(void)
1249 {
1250 #ifdef __linux
1251 if (is_debugger_running())
1252 return;
1253
1254 struct sigaction sa;
1255 sa.sa_sigaction = (void*)gdb_sighandler;
1256 sigemptyset(&sa.sa_mask);
1257 sa.sa_flags = SA_RESTART | SA_SIGINFO;
1258
1259 sigaction(SIGSEGV, &sa, NULL);
1260 sigaction(SIGUSR1, &sa, NULL);
1261 sigaction(SIGFPE, &sa, NULL);
1262 sigaction(SIGBUS, &sa, NULL);
1263 sigaction(SIGILL, &sa, NULL);
1264 sigaction(SIGABRT, &sa, NULL);
1265 #else // __linux
1266 register_trace_signal_handlers();
1267 #endif // __linux
1268 }
1269
term_init(void)1270 void term_init(void)
1271 {
1272 const char *nvc_no_color = getenv("NVC_NO_COLOR");
1273 const char *term = getenv("TERM") ?: "";
1274
1275 static const char *term_blacklist[] = {
1276 "dumb"
1277 };
1278
1279 bool is_tty = isatty(STDERR_FILENO) && isatty(STDOUT_FILENO);
1280
1281 #ifdef __MINGW32__
1282 if (!is_tty) {
1283 // Handle running under MinTty
1284 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
1285 const size_t size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
1286 FILE_NAME_INFO *nameinfo = malloc(size);
1287 if (!GetFileInformationByHandleEx(hStdOut, FileNameInfo, nameinfo, size))
1288 fatal_errno("GetFileInformationByHandle");
1289
1290 if ((wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0
1291 || wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0)
1292 && wcsstr(nameinfo->FileName, L"pty") != NULL)
1293 is_tty = true;
1294
1295 free(nameinfo);
1296 }
1297 #endif
1298
1299 want_color = is_tty && (nvc_no_color == NULL);
1300
1301 if (want_color && (term != NULL)) {
1302 for (size_t i = 0; i < ARRAY_LEN(term_blacklist); i++) {
1303 if (strcmp(term, term_blacklist[i]) == 0) {
1304 want_color = false;
1305 break;
1306 }
1307 }
1308 }
1309
1310 #ifdef __MINGW32__
1311 HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
1312 DWORD mode;
1313 if (GetConsoleMode(hConsole, &mode)) {
1314 mode |= 0x04; // ENABLE_VIRTUAL_TERMINAL_PROCESSING
1315 if (!SetConsoleMode(hConsole, mode))
1316 want_color = false;
1317 }
1318 #endif
1319 }
1320
opt_set_generic(const char * name,option_kind_t kind,optval_t value)1321 static void opt_set_generic(const char *name, option_kind_t kind,
1322 optval_t value)
1323 {
1324 ident_t name_i = ident_new(name);
1325 struct option *it;
1326 for (it = options; (it != NULL) && (it->key != name_i); it = it->next)
1327 ;
1328
1329 if (it != NULL) {
1330 if (it->kind == OPTION_STRING)
1331 free(it->value.s);
1332 it->value = value;
1333 }
1334 else {
1335 it = xmalloc(sizeof(struct option));
1336 it->key = ident_new(name);
1337 it->value = value;
1338 it->next = options;
1339 it->kind = kind;
1340
1341 options = it;
1342 }
1343 }
1344
opt_get_generic(const char * name,option_kind_t kind)1345 static optval_t opt_get_generic(const char *name, option_kind_t kind)
1346 {
1347 ident_t name_i = ident_new(name);
1348 struct option *it;
1349 for (it = options; (it != NULL) && (it->key != name_i); it = it->next)
1350 ;
1351
1352 if (it != NULL) {
1353 if (it->kind == kind)
1354 return it->value;
1355 else
1356 fatal_trace("wrong option kind for %s", name);
1357 }
1358 else
1359 fatal_trace("invalid option %s", name);
1360 }
1361
opt_set_int(const char * name,int val)1362 void opt_set_int(const char *name, int val)
1363 {
1364 opt_set_generic(name, OPTION_INT, (optval_t)val);
1365 }
1366
opt_get_int(const char * name)1367 int opt_get_int(const char *name)
1368 {
1369 return opt_get_generic(name, OPTION_INT).i;
1370 }
1371
opt_set_str(const char * name,const char * val)1372 void opt_set_str(const char *name, const char *val)
1373 {
1374 opt_set_generic(name, OPTION_STRING, (optval_t)(val ? strdup(val) : NULL));
1375 }
1376
opt_get_str(const char * name)1377 const char *opt_get_str(const char *name)
1378 {
1379 return opt_get_generic(name, OPTION_STRING).s;
1380 }
1381
get_fmt_buf(size_t len)1382 char *get_fmt_buf(size_t len)
1383 {
1384 // This is a bit of a kludge but keeping a sufficient number
1385 // of static buffers allows us to use format functions multiple
1386 // times in printf
1387 static char *buf_set[MAX_FMT_BUFS];
1388 static size_t buflen[MAX_FMT_BUFS];
1389 static int next_buf = 0;
1390
1391 char **bufp = &buf_set[next_buf];
1392 size_t *blenp = &buflen[next_buf];
1393 next_buf = (next_buf + 1) % MAX_FMT_BUFS;
1394
1395 if (*bufp == NULL) {
1396 *bufp = xmalloc(len);
1397 *blenp = len;
1398 }
1399
1400 while (len > *blenp) {
1401 *blenp *= 2;
1402 *bufp = xrealloc(*bufp, *blenp);
1403 }
1404
1405 return *bufp;
1406 }
1407
next_power_of_2(int n)1408 int next_power_of_2(int n)
1409 {
1410 n--;
1411 n |= n >> 1;
1412 n |= n >> 2;
1413 n |= n >> 4;
1414 n |= n >> 8;
1415 n |= n >> 16;
1416 n++;
1417 return n;
1418 }
1419
ilog2(int64_t n)1420 int ilog2(int64_t n)
1421 {
1422 if (n <= 1)
1423 return 1;
1424 else {
1425 int r = 0;
1426 int64_t c = 1;
1427 while (c < n) {
1428 r += 1;
1429 c *= 2;
1430 }
1431 return r;
1432 }
1433 }
1434
ipow(int64_t x,int64_t y)1435 int64_t ipow(int64_t x, int64_t y)
1436 {
1437 int64_t r = 1;
1438 while (y) {
1439 if (y & 1)
1440 r *= x;
1441 y >>= 1;
1442 x *= x;
1443 }
1444 return r;
1445 }
1446
mmap_guarded(size_t sz,const char * tag)1447 void *mmap_guarded(size_t sz, const char *tag)
1448 {
1449 #ifndef __MINGW32__
1450 const long pagesz = sysconf(_SC_PAGESIZE);
1451 #else
1452 const long pagesz = 4096;
1453 #endif
1454 const size_t pagemsk = pagesz - 1;
1455 if (sz & pagemsk)
1456 sz = (sz & ~pagemsk) + pagesz;
1457
1458 #if (defined __APPLE__ || defined __OpenBSD__)
1459 const int flags = MAP_SHARED | MAP_ANON;
1460 #elif !(defined __MINGW32__)
1461 const int flags = MAP_SHARED | MAP_ANONYMOUS;
1462 #endif
1463
1464 #ifndef __MINGW32__
1465 void *ptr = mmap(NULL, sz + pagesz, PROT_READ | PROT_WRITE, flags, -1, 0);
1466 if (ptr == MAP_FAILED)
1467 fatal_errno("mmap");
1468 #else
1469 HANDLE handle = CreateFileMapping(NULL, NULL, PAGE_READWRITE,
1470 0, sz + pagesz, NULL);
1471 if (!handle)
1472 fatal_errno("CreateFileMapping");
1473
1474 void *ptr = MapViewOfFileEx(handle, FILE_MAP_ALL_ACCESS, 0,
1475 0, (SIZE_T) (sz + pagesz), (LPVOID) NULL);
1476 CloseHandle(handle);
1477 if (ptr == NULL)
1478 fatal_errno("MapViewOfFileEx");
1479 #endif
1480 uint8_t *guard_ptr = (uint8_t *)ptr + sz;
1481 #ifndef __MINGW32__
1482 if (mprotect(guard_ptr, pagesz, PROT_NONE) < 0)
1483 fatal_errno("mprotect");
1484 #else
1485 DWORD old_prot;
1486 if (!VirtualProtect(guard_ptr, pagesz, PAGE_NOACCESS, &old_prot))
1487 fatal_errno("VirtualProtect");
1488 #endif
1489 guard_t *guard = xmalloc(sizeof(guard_t));
1490 guard->next = guards;
1491 guard->tag = tag;
1492 guard->base = (uintptr_t)guard_ptr;
1493 guard->limit = guard->base + pagesz;
1494
1495 guards = guard;
1496
1497 return ptr;
1498 }
1499
checked_sprintf(char * buf,int len,const char * fmt,...)1500 int checked_sprintf(char *buf, int len, const char *fmt, ...)
1501 {
1502 assert(len > 0);
1503
1504 va_list ap;
1505 va_start(ap, fmt);
1506
1507 const int nbytes = vsnprintf(buf, len, fmt, ap);
1508 if (nbytes >= len)
1509 fatal_trace("checked_sprintf requires %d bytes but have %d", nbytes, len);
1510
1511 va_end(ap);
1512
1513 return nbytes;
1514 }
1515
tb_new(void)1516 text_buf_t *tb_new(void)
1517 {
1518 text_buf_t *tb = xmalloc(sizeof(text_buf_t));
1519 tb->alloc = 256;
1520 tb->len = 0;
1521 tb->buf = xmalloc(tb->alloc);
1522
1523 tb->buf[0] = '\0';
1524
1525 return tb;
1526 }
1527
tb_free(text_buf_t * tb)1528 void tb_free(text_buf_t *tb)
1529 {
1530 free(tb->buf);
1531 free(tb);
1532 }
1533
_tb_cleanup(text_buf_t ** tb)1534 void _tb_cleanup(text_buf_t **tb)
1535 {
1536 tb_free(*tb);
1537 }
1538
tb_printf(text_buf_t * tb,const char * fmt,...)1539 void tb_printf(text_buf_t *tb, const char *fmt, ...)
1540 {
1541 int nchars, avail;
1542 for (;;) {
1543 va_list ap;
1544 va_start(ap, fmt);
1545
1546 avail = tb->alloc - tb->len;
1547 nchars = vsnprintf(tb->buf + tb->len, avail, fmt, ap);
1548
1549 va_end(ap);
1550
1551 if (nchars < avail)
1552 break;
1553
1554 tb->alloc *= 2;
1555 tb->buf = xrealloc(tb->buf, tb->alloc);
1556 }
1557
1558 tb->len += nchars;
1559 }
1560
tb_append(text_buf_t * tb,char ch)1561 void tb_append(text_buf_t *tb, char ch)
1562 {
1563 if (tb->len + 1 >= tb->alloc) {
1564 tb->alloc *= 2;
1565 tb->buf = xrealloc(tb->buf, tb->alloc);
1566 }
1567
1568 tb->buf[(tb->len)++] = ch;
1569 tb->buf[tb->len] = '\0';
1570 }
1571
tb_claim(text_buf_t * tb)1572 char *tb_claim(text_buf_t *tb)
1573 {
1574 char *buf = tb->buf;
1575 tb->buf = NULL;
1576 return buf;
1577 }
1578
tb_get(text_buf_t * tb)1579 const char *tb_get(text_buf_t *tb)
1580 {
1581 return tb->buf;
1582 }
1583
tb_rewind(text_buf_t * tb)1584 void tb_rewind(text_buf_t *tb)
1585 {
1586 tb->len = 0;
1587 tb->buf[0] = '\0';
1588 }
1589
tb_backup(text_buf_t * tb,unsigned n)1590 void tb_backup(text_buf_t *tb, unsigned n)
1591 {
1592 tb->len = n > tb->len ? 0 : tb->len - n;
1593 tb->buf[tb->len] = '\0';
1594 }
1595
_local_free(void * ptr)1596 void _local_free(void *ptr)
1597 {
1598 free(*(void **)ptr);
1599 }
1600
set_message_style(message_style_t style)1601 void set_message_style(message_style_t style)
1602 {
1603 message_style = style;
1604
1605 if (style == MESSAGE_COMPACT)
1606 want_color = false;
1607 }
1608
1609 #ifndef __MINGW32__
tv2ms(struct timeval * tv)1610 static unsigned tv2ms(struct timeval *tv)
1611 {
1612 return (tv->tv_sec * 1000) + (tv->tv_usec / 1000);
1613 }
1614 #endif
1615
nvc_rusage(nvc_rusage_t * ru)1616 void nvc_rusage(nvc_rusage_t *ru)
1617 {
1618 #ifndef __MINGW32__
1619 static struct rusage last;
1620
1621 struct rusage sys;
1622 if (getrusage(RUSAGE_SELF, &sys) < 0)
1623 fatal_errno("getrusage");
1624
1625 const unsigned utime = tv2ms(&(sys.ru_utime)) - tv2ms(&(last.ru_utime));
1626 const unsigned stime = tv2ms(&(sys.ru_stime)) - tv2ms(&(last.ru_stime));
1627
1628 ru->ms = utime + stime;
1629
1630 #ifdef __APPLE__
1631 const int rss_units = 1024;
1632 #else
1633 const int rss_units = 1;
1634 #endif
1635
1636 ru->rss = sys.ru_maxrss / rss_units;
1637
1638 last = sys;
1639 #else
1640 static long long last;
1641 ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1642 HANDLE hProcess = GetCurrentProcess();
1643
1644 FILETIME ftCreation, ftExit, ftKernel, ftUser;
1645 if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1646 fatal_errno("GetProcessTimes");
1647
1648 lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1649 lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1650 lv_Tuser.LowPart = ftUser.dwLowDateTime;
1651 lv_Tuser.HighPart = ftUser.dwHighDateTime;
1652
1653 ru->ms = (lv_Tkernel.QuadPart + lv_Tuser.QuadPart) / 10000 - last;
1654 last = ru->ms;
1655
1656 PROCESS_MEMORY_COUNTERS counters;
1657 if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1658 fatal_errno("GetProcessMemoryInfo");
1659
1660 ru->rss = counters.PeakWorkingSetSize / 1024;
1661 #endif
1662 }
1663
run_program(const char * const * args,size_t n_args)1664 void run_program(const char *const *args, size_t n_args)
1665 {
1666 const bool quiet = (getenv("NVC_LINK_QUIET") != NULL);
1667
1668 if (!quiet) {
1669 for (size_t i = 0; i < n_args; i++)
1670 printf("%s%c", args[i], (i + 1 == n_args ? '\n' : ' '));
1671 fflush(stdout);
1672 }
1673
1674 #if defined __CYGWIN__ || defined __MINGW32__
1675 int status = spawnv(_P_WAIT, args[0], (char *const *)args);
1676 if (status != 0)
1677 fatal("%s failed with status %d", args[0], status);
1678 #else // __CYGWIN__
1679 pid_t pid = fork();
1680 if (pid == 0) {
1681 execv(args[0], (char *const *)args);
1682 fatal_errno("execv");
1683 }
1684 else if (pid > 0) {
1685 int status;
1686 if (waitpid(pid, &status, 0) != pid)
1687 fatal_errno("waitpid");
1688
1689 if (WEXITSTATUS(status) != 0)
1690 fatal("%s failed with status %d", args[0], WEXITSTATUS(status));
1691 }
1692 else
1693 fatal_errno("fork");
1694 #endif // __CYGWIN__
1695 }
1696
file_read_lock(int fd)1697 void file_read_lock(int fd)
1698 {
1699 #ifdef __MINGW32__
1700 HANDLE hf = (HANDLE)_get_osfhandle(fd);
1701
1702 LARGE_INTEGER li;
1703 li.QuadPart = _filelengthi64(fd);
1704
1705 OVERLAPPED ovlp;
1706 memset(&ovlp, 0, sizeof ovlp);
1707
1708 if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1709 fatal_errno("LockFileEx");
1710 #else
1711 if (flock(fd, LOCK_SH) < 0)
1712 fatal_errno("flock");
1713 #endif
1714 }
1715
file_write_lock(int fd)1716 void file_write_lock(int fd)
1717 {
1718 #ifdef __MINGW32__
1719 HANDLE hf = (HANDLE)_get_osfhandle(fd);
1720
1721 LARGE_INTEGER li;
1722 li.QuadPart = _filelengthi64(fd);
1723
1724 OVERLAPPED ovlp;
1725 memset(&ovlp, 0, sizeof ovlp);
1726
1727 if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1728 li.LowPart, li.HighPart, &ovlp))
1729 fatal_errno("LockFileEx");
1730 #else
1731 if (flock(fd, LOCK_EX) < 0)
1732 fatal_errno("flock");
1733 #endif
1734 }
1735
file_unlock(int fd)1736 void file_unlock(int fd)
1737 {
1738 #ifdef __MINGW32__
1739 HANDLE hf = (HANDLE)_get_osfhandle(fd);
1740
1741 LARGE_INTEGER li;
1742 li.QuadPart = _filelengthi64 (fd);
1743
1744 UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1745 #else
1746 if (flock(fd, LOCK_UN) < 0)
1747 fatal_errno("flock");
1748 #endif
1749 }
1750
map_file(int fd,size_t size)1751 void *map_file(int fd, size_t size)
1752 {
1753 #ifdef __MINGW32__
1754 HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1755 PAGE_READONLY, 0, size, NULL);
1756 if (!handle)
1757 fatal_errno("CreateFileMapping");
1758
1759 void *ptr = MapViewOfFileEx(handle, FILE_MAP_COPY, 0,
1760 0, (SIZE_T) size, (LPVOID) NULL);
1761 CloseHandle(handle);
1762 if (ptr == NULL)
1763 fatal_errno("MapViewOfFileEx");
1764 #else
1765 void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1766 if (ptr == MAP_FAILED)
1767 fatal_errno("mmap");
1768 #endif
1769 return ptr;
1770 }
1771
unmap_file(void * ptr,size_t size)1772 void unmap_file(void *ptr, size_t size)
1773 {
1774 #ifdef __MINGW32__
1775 if (!UnmapViewOfFile((LPCVOID) ptr))
1776 fatal_errno("UnmapViewOfFile");
1777 #else
1778 munmap(ptr, size);
1779 #endif
1780 }
1781
make_dir(const char * path)1782 void make_dir(const char *path)
1783 {
1784 #ifdef __MINGW32__
1785 if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1786 fatal_errno("mkdir: %s", path);
1787 #else
1788 if (mkdir(path, 0777) != 0 && errno != EEXIST)
1789 fatal_errno("mkdir: %s", path);
1790 #endif
1791 }
1792
get_timestamp_us()1793 uint64_t get_timestamp_us()
1794 {
1795 #if defined __MACH__ && !defined CLOCK_MONOTONIC
1796 clock_serv_t cclock;
1797 mach_timespec_t mts;
1798 host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
1799 clock_get_time(cclock, &mts);
1800 mach_port_deallocate(mach_task_self(), cclock);
1801 return (mts.tv_nsec / 1000) + (mts.tv_sec * 1000 * 1000);
1802 #elif defined _WIN32
1803 return 0; // TODO
1804 #else
1805 struct timespec ts;
1806 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
1807 fatal_errno("clock_gettime");
1808 return (ts.tv_nsec / 1000) + (ts.tv_sec * 1000 * 1000);
1809 #endif
1810 }
1811
safe_symbol(const char * text)1812 const char *safe_symbol(const char *text)
1813 {
1814 // Return a string that is safe to use as a symbol name on this platform
1815 #if defined _WIN32 || defined __CYGWIN__
1816 if (strpbrk(text, "()\"[]*+=") == NULL)
1817 return text;
1818
1819 text_buf_t *tb = tb_new();
1820
1821 for (const char *p = text; *p != '\0' && p - text < 240; p++) {
1822 switch (*p) {
1823 case '(': tb_printf(tb, "_lp_"); break;
1824 case ')': tb_printf(tb, "_rp_"); break;
1825 case '"': tb_printf(tb, "_q_"); break;
1826 case '[': tb_printf(tb, "_ls_"); break;
1827 case ']': tb_printf(tb, "_rs_"); break;
1828 case '*': tb_printf(tb, "_mult_"); break;
1829 case '+': tb_printf(tb, "_plus_"); break;
1830 case '=': tb_printf(tb, "_eq_"); break;
1831 default:
1832 tb_append(tb, *p);
1833 }
1834 }
1835
1836 return tb_get(tb);
1837 #else
1838 return text;
1839 #endif
1840 }
1841