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 #include "util.h"
19 #include "rt.h"
20 #include "lib.h"
21 #include "tree.h"
22 #include "common.h"
23 #include "array.h"
24 
25 #include <assert.h>
26 #include <limits.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <math.h>
35 
36 #ifdef HAVE_EXECINFO_H
37 #include <execinfo.h>
38 #endif
39 
40 #ifdef __MINGW32__
41 #define WIN32_LEAN_AND_MEAN
42 #include <windows.h>
43 #else
44 #include <dlfcn.h>
45 #endif
46 
47 #define TRACE_MAX 10
48 
49 #ifdef __MINGW32__
50 #ifdef _WIN64
51 extern void ___chkstk_ms(void);
52 #else
53 #undef _alloca
54 extern void _alloca(void);
55 #endif
56 
57 static HMODULE *search_modules;
58 static size_t nmodules = 0, max_modules = 0;
59 
60 #endif
61 
jit_find_symbol(const char * name,bool required)62 void *jit_find_symbol(const char *name, bool required)
63 {
64 #if (defined __MINGW32__ || defined __CYGWIN__) && !defined _WIN64
65    if (*name == '_')
66       name++;   // Remove leading underscore on 32-bit Windows
67 #endif
68 
69    name = safe_symbol(name);
70 
71 #ifdef __MINGW32__
72 
73 #ifdef _WIN64
74    if (strcmp(name, "___chkstk_ms") == 0)
75       return (void *)(uintptr_t)___chkstk_ms;
76 #else
77    if (strcmp(name, "_alloca") == 0)
78       return (void *)(uintptr_t)_alloca;
79 #endif
80 
81    if (strcmp(name, "exp2") == 0)
82       return (void *)(uintptr_t)exp2;
83 
84    for (size_t i = 0; i < nmodules; i++) {
85       void *ptr = (void *)(uintptr_t)GetProcAddress(search_modules[i], name);
86       if (ptr != NULL)
87          return ptr;
88    }
89 
90    if (required)
91       fatal("cannot find symbol %s", name);
92 
93    return NULL;
94 
95 #else  // __MINGW32__
96 
97    dlerror();   // Clear any previous error
98 
99    void *sym = dlsym(NULL, name);
100    const char *error = dlerror();
101    if (error != NULL) {
102       sym = dlsym(RTLD_DEFAULT, name);
103       error = dlerror();
104       if ((error != NULL) && required)
105          fatal("%s: %s", name, error);
106    }
107    return sym;
108 #endif
109 }
110 
jit_load_module(ident_t name)111 static void jit_load_module(ident_t name)
112 {
113    lib_t lib = lib_find(ident_until(name, '.'), true);
114 
115    tree_kind_t kind = lib_index_kind(lib, name);
116    if (kind == T_LAST_TREE_KIND)
117       fatal("Cannot find %s in library %s", istr(name), istr(lib_name(lib)));
118 
119    if (kind == T_ENTITY || kind == T_ARCH)
120       return;
121 
122    const bool optional = (kind == T_PACKAGE || kind == T_PACK_BODY);
123 
124    char *so_fname LOCAL = xasprintf("_%s." DLL_EXT, istr(name));
125 
126    char so_path[PATH_MAX];
127    lib_realpath(lib, so_fname, so_path, sizeof(so_path));
128 
129    if (access(so_path, F_OK) != 0 && optional)
130       return;
131 
132    if (opt_get_int("rt_trace_en"))
133       fprintf(stderr, "TRACE (init): load %s from %s\n", istr(name), so_path);
134 
135 #ifdef __MINGW32__
136    HMODULE hModule = LoadLibrary(so_path);
137    if (hModule == NULL)
138       fatal("failed to load %s", so_path);
139 
140    ARRAY_APPEND(search_modules, hModule, nmodules, max_modules);
141 #else
142    if (dlopen(so_path, RTLD_LAZY | RTLD_GLOBAL) == NULL)
143       fatal("%s: %s", so_path, dlerror());
144 #endif
145 }
146 
jit_init(tree_t top)147 void jit_init(tree_t top)
148 {
149 #ifdef __MINGW32__
150    max_modules = 16;
151    nmodules = 0;
152    search_modules = xmalloc(sizeof(HMODULE) * max_modules);
153    ARRAY_APPEND(search_modules, GetModuleHandle(NULL), nmodules, max_modules);
154    ARRAY_APPEND(search_modules, GetModuleHandle("MSVCRT.DLL"),
155                 nmodules, max_modules);
156 #endif
157 
158    const int ncontext = tree_contexts(top);
159    for (int i = 0; i < ncontext; i++) {
160       tree_t c = tree_context(top, i);
161       if (tree_kind(c) == T_USE)
162          jit_load_module(tree_ident(c));
163    }
164 
165    jit_load_module(tree_ident(top));
166 }
167 
jit_shutdown(void)168 void jit_shutdown(void)
169 {
170 
171 }
172 
jit_trace(jit_trace_t ** trace,size_t * count)173 void jit_trace(jit_trace_t **trace, size_t *count)
174 {
175 #if defined HAVE_EXECINFO_H && defined __linux__
176 
177    void *frames[TRACE_MAX];
178    char **messages = NULL;
179    int trace_size = 0;
180 
181    trace_size = backtrace(frames, TRACE_MAX);
182    messages = backtrace_symbols(frames, trace_size);
183 
184    *count = 0;
185    *trace = xcalloc(sizeof(jit_trace_t) * trace_size);
186 
187    for (int i = 0; i < trace_size; i++) {
188       // This hack only works for native compiled code
189       char *begin = strchr(messages[i], '(');
190       char *end = strchr(messages[i], '+');
191 
192       if (begin == NULL || end == NULL)
193          continue;
194 
195       *end = '\0';
196 
197       bool maybe_vhdl = false;
198       for (const char *p = begin + 1; *p != '\0'; p++) {
199          if (isupper((int)*p) || isdigit((int)*p) || *p == '_') {
200             maybe_vhdl = true;
201             continue;
202          }
203          else if (*p == '.') {
204             maybe_vhdl = p > begin + 1;
205             break;
206          }
207          else {
208             maybe_vhdl = false;
209             break;
210          }
211       }
212 
213       if (!maybe_vhdl)
214          continue;
215 
216       ident_t mangled = ident_new(begin + 1);
217       ident_t lib_name = ident_until(mangled, '.');
218 
219       lib_t lib = lib_find(lib_name, false);
220       if (lib == NULL)
221          continue;
222 
223       ident_t decl_name = ident_until(mangled, '$');
224 
225       ident_t unit_name = ident_runtil(decl_name, '.');
226       tree_t unit = lib_get(lib, unit_name);
227       if (unit == NULL)
228          continue;
229 
230       if (tree_kind(unit) == T_PACKAGE) {
231          unit = lib_get(lib, ident_prefix(unit_name, ident_new("body"), '-'));
232          if (unit == NULL)
233             continue;
234       }
235 
236       tree_t best = NULL;
237       const int ndecls = tree_decls(unit);
238       for (int i = 0; i < ndecls; i++) {
239          tree_t d = tree_decl(unit, i);
240          if (tree_attr_str(d, mangled_i) == mangled)
241             best = d;
242          else if (tree_ident(d) == decl_name && best == NULL)
243             best = d;
244       }
245 
246       if (best == NULL)
247          continue;
248 
249       (*trace)[*count].loc = *tree_loc(best);
250       (*trace)[*count].tree = best;
251       (*count)++;
252    }
253 
254    free(messages);
255 
256 #else
257    *count = 0;
258    *trace = NULL;
259 #endif
260 }
261