1 /*
2  * Copyright (c) 2001-2019 Stephen Williams (steve@icarus.com)
3  *
4  *    This source code is free software; you can redistribute it
5  *    and/or modify it in source code form under the terms of the GNU
6  *    General Public License as published by the Free Software
7  *    Foundation; either version 2 of the License, or (at your option)
8  *    any later version.
9  *
10  *    This program is distributed in the hope that it will be useful,
11  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *    GNU General Public License for more details.
14  *
15  *    You should have received a copy of the GNU General Public License
16  *    along with this program; if not, write to the Free Software
17  *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 # include  "config.h"
21 # include  "vpi_priv.h"
22 # include  "ivl_dlfcn.h"
23 # include  "vvp_cleanup.h"
24 # include  <cstdio>
25 # include  <cstring>
26 # include  <sys/types.h>
27 # include  <sys/stat.h>
28 # include  "ivl_alloc.h"
29 
30 static ivl_dll_t*dll_list = 0;
31 static unsigned dll_list_cnt = 0;
32 
33 #if defined(__MINGW32__) || defined (__CYGWIN__)
34 typedef PLI_UINT32 (*vpip_set_callback_t)(vpip_routines_s*, PLI_UINT32);
35 #endif
36 typedef void (*vlog_startup_routines_t)(void);
37 
38 # define VPIP_MODULE_PATH_MAX 64
39 
40 static const char* vpip_module_path[VPIP_MODULE_PATH_MAX] = {0};
41 
42 static unsigned vpip_module_path_cnt = 0;
43 
44 static bool disable_default_paths = false;
45 
vpip_clear_module_paths()46 void vpip_clear_module_paths()
47 {
48       vpip_module_path_cnt = 0;
49       vpip_module_path[0] = 0;
50       disable_default_paths = true;
51 }
52 
vpip_add_module_path(const char * path)53 void vpip_add_module_path(const char*path)
54 {
55       if (vpip_module_path_cnt >= VPIP_MODULE_PATH_MAX) {
56 	    fprintf(stderr, "Too many module paths specified\n");
57 	    exit(1);
58       }
59       vpip_module_path[vpip_module_path_cnt++] = path;
60 }
61 
vpip_add_env_and_default_module_paths()62 void vpip_add_env_and_default_module_paths()
63 {
64       if (disable_default_paths)
65 	    return;
66 
67       if (char *var = ::getenv("IVERILOG_VPI_MODULE_PATH")) {
68             char *ptr = var;
69             char *end = var+strlen(var);
70             int len = 0;
71             while (ptr <= end) {
72                   if (*ptr == 0 || *ptr == ':' || *ptr == ';') {
73                         if (len > 0) {
74                               vpip_add_module_path(strndup(var, len));
75                         }
76                         len = 0;
77                         var = ptr+1;
78                   } else {
79                         len++;
80                   }
81                   ptr++;
82             }
83       }
84 
85 #ifdef __MINGW32__
86 	/* Calculate the module path from the path to the command.
87 	   This is necessary because of the installation process on
88 	   Windows. Mostly, it is those darn drive letters, but oh
89 	   well. We know the command path is formed like this:
90 
91 		D:\iverilog\bin\iverilog.exe
92 
93 	   The IVL_ROOT in a Windows installation is the path:
94 
95 		D:\iverilog\lib\ivl$(suffix)
96 
97 	   so we chop the file name and the last directory by
98 	   turning the last two \ characters to null. Then we append
99 	   the lib\ivl$(suffix) to finish. */
100       char *s;
101       char basepath[4096], tmp[4096];
102       GetModuleFileName(NULL, tmp, sizeof tmp);
103 	/* Convert to a short name to remove any embedded spaces. */
104       GetShortPathName(tmp, basepath, sizeof basepath);
105       s = strrchr(basepath, '\\');
106       if (s) *s = 0;
107       else {
108 	    fprintf(stderr, "%s: Missing first \\ in exe path!\n", tmp);
109 	    exit(1);
110       }
111       s = strrchr(basepath, '\\');
112       if (s) *s = 0;
113       else {
114 	    fprintf(stderr, "%s: Missing second \\ in exe path!\n", tmp);
115 	    exit(1);
116       }
117       strcat(s, "\\lib\\ivl" IVL_SUFFIX);
118       vpip_add_module_path(strdup(basepath));
119 #else
120 #ifdef MODULE_DIR1
121       vpip_add_module_path(MODULE_DIR1);
122 #endif
123 #endif
124 #ifdef MODULE_DIR2
125       vpip_add_module_path(MODULE_DIR2);
126 #endif
127 }
128 
load_module_delete(void)129 void load_module_delete(void)
130 {
131       for (unsigned idx = 0; idx < dll_list_cnt; idx += 1) {
132 	    ivl_dlclose(dll_list[idx]);
133       }
134       free(dll_list);
135       dll_list = 0;
136       dll_list_cnt = 0;
137 }
138 
vpip_load_module(const char * name)139 void vpip_load_module(const char*name)
140 {
141       struct stat sb;
142       int rc;
143       bool export_flag = false;
144       char buf[4096];
145 
146 #ifdef __MINGW32__
147       const char sep = '\\';
148 #else
149       const char sep = '/';
150 #endif
151 
152       ivl_dll_t dll = 0;
153       buf[0] = 0;                     /* terminate the string */
154       if (strchr(name, sep)) {
155 	      /* If the name has at least one directory character in
156 		 it, then assume it is a complete name, maybe including any
157 		 possible .vpi suffix. */
158 	    export_flag = false;
159 	    rc = stat(name, &sb);
160 
161 	    if (rc != 0) {            /* did we find a file? */
162 	          /* no, try with a .vpi suffix too */
163 		  export_flag = false;
164 		  snprintf(buf, sizeof(buf), "%s.vpi", name);
165 		  rc = stat(buf, &sb);
166 
167 		    /* Try also with the .vpl suffix. */
168 		  if (rc != 0) {
169 			export_flag = true;
170 			snprintf(buf, sizeof(buf), "%s.vpl", name);
171 			rc = stat(buf, &sb);
172 		  }
173 
174 		  if (rc != 0) {
175 			fprintf(stderr, "%s: Unable to find module file `%s' "
176 				"or `%s.vpi'.\n", name,name,buf);
177 			return;
178 		  }
179 	    } else {
180 	      strcpy(buf,name);   /* yes copy the name into the buffer */
181 	    }
182 
183       } else {
184 	    rc = -1;
185 	    for (unsigned idx = 0
186 		       ; (rc != 0) && (idx < vpip_module_path_cnt)
187 		       ;  idx += 1) {
188 		  export_flag = false;
189 		  snprintf(buf, sizeof(buf), "%s%c%s.vpi",
190 			   vpip_module_path[idx], sep, name);
191 		  rc = stat(buf,&sb);
192 
193 		  if (rc != 0) {
194 			export_flag = true;
195 			snprintf(buf, sizeof(buf), "%s%c%s.vpl",
196 				 vpip_module_path[idx], sep, name);
197 			rc = stat(buf,&sb);
198 		  }
199 	    }
200 
201 	    if (rc != 0) {
202 		  fprintf(stderr, "%s: Unable to find a "
203 			  "`%s.vpi' module on the search path.\n",
204 			  name, name);
205 		  return;
206 	    }
207 
208       }
209 
210       /* must have found some file that could possibly be a vpi module
211        * try to open it as a shared object.
212        */
213       dll = ivl_dlopen(buf, export_flag);
214       if(dll==0) {
215 	/* hmm, this failed, let the user know what has really gone wrong */
216 	fprintf(stderr,"%s:`%s' failed to open using dlopen() because:\n"
217 		"    %s.\n",name,buf,dlerror());
218 
219 	return;
220       }
221 
222 #if defined(__MINGW32__) || defined (__CYGWIN__)
223       void*function = ivl_dlsym(dll, "vpip_set_callback");
224       if (function == 0) {
225 	    fprintf(stderr, "%s: no vpip_set_callback()\n", name);
226 	    ivl_dlclose(dll);
227 	    return;
228       }
229       vpip_set_callback_t set_callback = (vpip_set_callback_t)function;
230       if (!set_callback(&vpi_routines, vpip_routines_version)) {
231 	    fprintf(stderr, "Failed to link VPI module %s. Try rebuilding it with iverilog-vpi.\n", name);
232 	    ivl_dlclose(dll);
233 	    return;
234       }
235 #endif
236 
237       void*table = ivl_dlsym(dll, LU "vlog_startup_routines" TU);
238       if (table == 0) {
239 	    fprintf(stderr, "%s: no vlog_startup_routines\n", name);
240 	    ivl_dlclose(dll);
241 	    return;
242       }
243 
244 	/* Add the dll to the list so it can be closed when we are done. */
245       dll_list_cnt += 1;
246       dll_list = (ivl_dll_t*)realloc(dll_list, dll_list_cnt*sizeof(ivl_dll_t));
247       dll_list[dll_list_cnt-1] = dll;
248 
249       vpi_mode_flag = VPI_MODE_REGISTER;
250       vlog_startup_routines_t*routines = (vlog_startup_routines_t*)table;
251       for (unsigned tmp = 0 ;  routines[tmp] ;  tmp += 1)
252 	    (routines[tmp])();
253       vpi_mode_flag = VPI_MODE_NONE;
254 }
255