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