This commit is contained in:
Lars-Peter Clausen 2026-05-31 18:33:21 -07:00 committed by GitHub
commit e16b5c0d55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 389 additions and 29 deletions

View File

@ -59,6 +59,8 @@ SUBDIRS += driver-vpi
# rebuild the lexor_keyword.cc file. If we do, then we want to use the
# local version instead of the one is $(srcdir).
vpath lexor_keyword.cc .
vpath %.c $(srcdir)/libmisc
vpath %.c $(srcdir)
vpath %.cc $(srcdir)/libmisc
vpath %.cc $(srcdir)
@ -109,7 +111,8 @@ M = LineInfo.o StringHeap.o
TT = t-dll.o t-dll-api.o t-dll-expr.o t-dll-proc.o t-dll-analog.o
FF = cprop.o exposenodes.o nodangle.o synth.o synth2.o syn-rules.o
O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \
C_OBJ = CmdExec.o
CXX_OBJ = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \
elab_expr.o elaborate_analog.o elab_lval.o elab_net.o \
elab_scope.o elab_sig.o elab_sig_analog.o elab_type.o \
emit.o eval_attrib.o \
@ -128,6 +131,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \
Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PFunction.o \
PGate.o PGenerate.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o PTimingCheck.o \
PTask.o PUdp.o PWire.o Statement.o AStatement.o $M $(FF) $(TT)
O = $(C_OBJ) $(CXX_OBJ)
all: dep config.h _pli_types.h version_tag.h version_base.h ivl@EXEEXT@
$(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true
@ -174,7 +178,7 @@ ifneq (@srcdir@,.)
endif
rm -rf autom4te.cache
cppcheck: $(O:.o=.cc) $(srcdir)/dosify.c
cppcheck: $(C_OBJ:.o=.c) $(CXX_OBJ:.o=.cc) $(srcdir)/dosify.c
cppcheck --enable=all --std=c99 --std=c++11 -f \
--check-level=exhaustive \
--suppressions-list=$(srcdir)/cppcheck-global.sup \
@ -232,6 +236,10 @@ endif
$(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o
mv $*.d dep/$*.d
%.o: %.c config.h | dep
$(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o
mv $*.d dep/$*.d
# Here are some explicit dependencies needed to get things going.
main.o: main.cc version_tag.h

View File

@ -93,6 +93,10 @@ unusedFunction:vpi_modules.cc:90
// vpi_sim_control()
unusedFunction:vpi_modules.cc:110
// CmdExec.c is shared by multiple executables, so each cppcheck target sees
// some exported helpers that are used only by another target.
unusedFunction:libmisc/CmdExec.c
// These are the functions that the compiler exports to the targets.
//ivl_branch_island()
unusedFunction:t-dll-api.cc:39

View File

@ -31,6 +31,7 @@ builddir=@builddir@
top_builddir=@top_builddir@
VPATH = $(srcdir)
vpath %.c $(srcdir)/../libmisc
bindir = $(exec_prefix)/bin
libdir = $(exec_prefix)/lib
@ -50,16 +51,16 @@ MAN = @MAN@
PS2PDF = @PS2PDF@
ifeq (@srcdir@,.)
INCLUDE_PATH = -I. -I..
INCLUDE_PATH = -I. -I.. -I../libmisc
else
INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/..
INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../libmisc
endif
CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@
CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@
LDFLAGS = @LDFLAGS@
O = main.o substit.o cflexor.o cfparse.o
O = main.o substit.o cflexor.o cfparse.o CmdExec.o
all: dep iverilog@EXEEXT@ iverilog.man

View File

@ -11,6 +11,10 @@ memleakOnRealloc
nullPointerArithmeticOutOfMemory
nullPointerOutOfMemory
// CmdExec.c is shared by multiple executables, so this target sees helpers
// that are used only by another target.
unusedFunction:../libmisc/CmdExec.c
// Errors/limitations in the generated yacc and lex files
duplicateBreak:cflexor.lex
constVariablePointer:cfparse.y

View File

@ -97,6 +97,7 @@ extern const char*optarg;
#endif
# include "globals.h"
# include "CmdExec.h"
#include "cfparse_misc.h" /* cfparse() */
#include "ivl_alloc.h"
@ -335,16 +336,16 @@ static int t_version_only(void)
free(source_path);
fflush(0);
snprintf(tmp, sizeof tmp, "%s%civlpp -V", ivlpp_dir, sep);
rc = system(tmp);
snprintf(tmp, sizeof tmp, "\"%s%civlpp\" -V", ivlpp_dir, sep);
rc = ivl_run_cmd(tmp, verbose_flag);
if (rc != 0) {
fprintf(stderr, "Unable to get version from \"%s\"\n", tmp);
}
fflush(0);
snprintf(tmp, sizeof tmp, "%s%civl -V -C\"%s\" -C\"%s\"", ivl_dir, sep,
snprintf(tmp, sizeof tmp, "\"%s%civl\" -V -C\"%s\" -C\"%s\"", ivl_dir, sep,
iconfig_path, iconfig_common_path);
rc = system(tmp);
rc = ivl_run_cmd(tmp, verbose_flag);
if (rc != 0) {
fprintf(stderr, "Unable to get version from \"%s\"\n", tmp);
}
@ -363,7 +364,7 @@ static int t_version_only(void)
static void build_preprocess_command(int e_flag)
{
snprintf(tmp, sizeof tmp, "%s%civlpp%s%s%s -F\"%s\" -f\"%s\" -p\"%s\"%s",
snprintf(tmp, sizeof tmp, "\"%s%civlpp\"%s%s%s -F\"%s\" -f\"%s\" -p\"%s\"%s",
ivlpp_dir, sep,
verbose_flag ? " -v" : "",
e_flag ? "" : " -L",
@ -395,7 +396,7 @@ static int t_preprocess_only(void)
if (verbose_flag)
printf("preprocess: %s\n", cmd);
rc = system(cmd);
rc = ivl_run_cmd(cmd, verbose_flag);
remove(source_path);
free(source_path);
@ -449,7 +450,7 @@ static int t_compile(void)
#endif
/* Build the ivl command. */
snprintf(tmp, sizeof tmp, "%s%civl", ivl_dir, sep);
snprintf(tmp, sizeof tmp, "\"%s%civl\"", ivl_dir, sep);
rc = strlen(tmp);
cmd = realloc(cmd, ncmd+rc+1);
strcpy(cmd+ncmd, tmp);
@ -496,7 +497,7 @@ static int t_compile(void)
printf("translate: %s\n", cmd);
rc = system(cmd);
rc = ivl_run_cmd(cmd, verbose_flag);
if ( ! getenv("IVERILOG_ICONFIG")) {
remove(source_path);
free(source_path);
@ -1484,7 +1485,7 @@ int main(int argc, char **argv)
if (vhdlpp_work == 0)
vhdlpp_work = "ivl_vhdl_work";
fprintf(defines_file, "vhdlpp:%s%cvhdlpp\n", vhdlpp_dir, sep);
fprintf(defines_file, "vhdlpp:\"%s%cvhdlpp\"\n", vhdlpp_dir, sep);
fprintf(defines_file, "vhdlpp-work:%s\n", vhdlpp_work);
for (unsigned idx = 0 ; idx < vhdlpp_libdir_cnt ; idx += 1)
fprintf(defines_file, "vhdlpp-libdir:%s\n", vhdlpp_libdir[idx]);
@ -1547,7 +1548,7 @@ int main(int argc, char **argv)
/* Write the preprocessor command needed to preprocess a
single file. This may be used to preprocess library
files. */
fprintf(iconfig_file, "ivlpp:%s%civlpp %s -L -F\"%s\" -P\"%s\"\n",
fprintf(iconfig_file, "ivlpp:\"%s%civlpp\" %s -L -F\"%s\" -P\"%s\"\n",
ivlpp_dir, sep,
strchr(warning_flags, 'r') ? "-Wredef-all" :
strchr(warning_flags, 'R') ? "-Wredef-chg" : "",

View File

@ -26,6 +26,7 @@ exec_prefix = @exec_prefix@
srcdir = @srcdir@
VPATH = $(srcdir)
vpath %.c $(srcdir)/../libmisc
bindir = @bindir@
libdir = @libdir@
@ -38,16 +39,16 @@ INSTALL_DATA = @INSTALL_DATA@
LEX = @LEX@
ifeq (@srcdir@,.)
INCLUDE_PATH = -I. -I..
INCLUDE_PATH = -I. -I.. -I../libmisc
else
INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/..
INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../libmisc
endif
CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@
CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@
LDFLAGS = @LDFLAGS@
O = main.o lexor.o
O = main.o lexor.o CmdExec.o
all: ivlpp@EXEEXT@

View File

@ -6,6 +6,10 @@ memleakOnRealloc
nullPointerArithmeticOutOfMemory
nullPointerOutOfMemory
// CmdExec.c is shared by multiple executables, so this target sees helpers
// that are used only by another target.
unusedFunction:../libmisc/CmdExec.c
// Errors/limitations in the generated yacc and lex files
ctunullpointerOutOfMemory:lexor.lex
memleakOnRealloc:lexor.lex

View File

@ -27,6 +27,7 @@
# include <ctype.h>
# include <assert.h>
# include "CmdExec.h"
# include "globals.h"
# include "ivl_alloc.h"
@ -2128,12 +2129,12 @@ static void open_input_file(struct include_stack_t*isp)
cmdlen += liblen;
char*cmd = malloc(cmdlen);
snprintf(cmd, cmdlen, "%s -w\"%s\"%s %s", vhdlpp_path, vhdlpp_work, libs, isp->path);
snprintf(cmd, cmdlen, "%s -w\"%s\"%s \"%s\"", vhdlpp_path, vhdlpp_work, libs, isp->path);
if (verbose_flag) fprintf(stderr, "Invoke vhdlpp: %s\n", cmd);
isp->file = popen(cmd, "r");
isp->file_close = pclose;
isp->file = ivl_run_cmd_pipe(cmd);
isp->file_close = ivl_close_cmd_pipe;
free(libs);
free(cmd);

302
libmisc/CmdExec.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Copyright (c) 2026 Lars-Peter Clausen <lars@metafoo.de>
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "CmdExec.h"
# include <stdlib.h>
# include <string.h>
#ifdef __MINGW32__
# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
# undef _WIN32_WINNT
# define _WIN32_WINNT 0x0600
# endif
# if !defined(WINVER) || WINVER < 0x0600
# undef WINVER
# define WINVER 0x0600
# endif
# include <fcntl.h>
# include <io.h>
# include <stdint.h>
# include <windows.h>
static char *get_cmd_exe(void)
{
DWORD n = GetEnvironmentVariableA("COMSPEC", NULL, 0);
char *buf;
if (n > 0) {
buf = (char *)malloc(n);
if (buf == NULL)
return NULL;
DWORD len = GetEnvironmentVariableA("COMSPEC", buf, n);
if (len > 0 && len < n)
return buf;
free(buf);
}
/* Fallback. This avoids searching PATH for cmd.exe. */
{
char sysdir[MAX_PATH];
UINT len = GetSystemDirectoryA(sysdir, sizeof(sysdir));
if (len > 0 && len < sizeof(sysdir)) {
static const char suffix[] = "\\cmd.exe";
size_t need = strlen(sysdir) + sizeof(suffix);
buf = (char *)malloc(need);
if (buf == NULL)
return NULL;
strcpy(buf, sysdir);
strcat(buf, suffix);
return buf;
}
}
return _strdup("cmd.exe");
}
/*
* On Windows `system()` runs the string as cmd.exe /C <command>.
* `popen()` similarly runs the string through CMD.exe and attaches one side of
* a pipe.
*
* cmd.exe has special quote handling for /C. Quote characters are preserved
* only if all of the following are true:
* - there is no /S switch;
* - there are exactly two quote characters;
* - there are no special characters between them, where special is one
* of &<>()@^|;
* - there is whitespace between them;
* - the string between them is the name of an executable file.
*
* For an input like "C:\Program Files\...\ivlpp" -F"...\defs" "input file.v"
* the quotes at the beginning and the end get stripped, which results in
* incorrect behavior.
*
* Below are versions of `system()` and `popen()` that call cmd.exe with /C /S,
* which will preserve the quotes. Select cmd.exe from COMSPEC, with a fallback
* to the system directory, and pass it as the application name so PATH is not
* searched.
*
* Also pass /D to suppress AutoRun commands so a registry setting cannot
* modify this parsing path.
*/
static int start_cmd_process(const char *cmd, HANDLE stdout_handle,
PROCESS_INFORMATION *pi,
DWORD *last_error)
{
STARTUPINFOEX siex;
STARTUPINFO si;
STARTUPINFO *startup_info;
HANDLE child_stdout = NULL;
HANDLE inherit_handles[3];
DWORD creation_flags = 0;
BOOL inherit = FALSE;
SIZE_T attr_size = 0;
unsigned inherit_count = 0;
char *cmd_exe = get_cmd_exe();
size_t cmd2len;
char *cmd2;
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
int attr_list_initialized = 0;
int rc = -1;
if (last_error != NULL)
*last_error = 0;
if (cmd_exe == NULL)
return -1;
cmd2len = strlen(cmd_exe) + strlen(cmd) + 16;
cmd2 = (char *)malloc(cmd2len);
if (cmd2 == NULL)
goto out;
snprintf(cmd2, cmd2len, "\"%s\" /D /S /C \"%s\"", cmd_exe, cmd);
memset(&si, 0x00, sizeof(si));
si.cb = sizeof(si);
startup_info = &si;
if (stdout_handle != NULL) {
HANDLE std_input = GetStdHandle(STD_INPUT_HANDLE);
HANDLE std_error = GetStdHandle(STD_ERROR_HANDLE);
memset(&siex, 0x00, sizeof(siex));
siex.StartupInfo.cb = sizeof(siex);
siex.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
if (!DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(),
&child_stdout, 0, TRUE, DUPLICATE_SAME_ACCESS))
goto out;
/* The normal system()/popen() path already relies on standard
* handles being inheritable. Only the pipe writer is duplicated.
*/
if (std_input != NULL && std_input != INVALID_HANDLE_VALUE)
inherit_handles[inherit_count++] = std_input;
inherit_handles[inherit_count++] = child_stdout;
if (std_error != NULL && std_error != INVALID_HANDLE_VALUE)
inherit_handles[inherit_count++] = std_error;
siex.StartupInfo.hStdInput = std_input;
siex.StartupInfo.hStdOutput = child_stdout;
siex.StartupInfo.hStdError = std_error;
InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size);
attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(attr_size);
if (attr_list == NULL)
goto out;
if (!InitializeProcThreadAttributeList(attr_list, 1, 0, &attr_size))
goto out;
attr_list_initialized = 1;
if (!UpdateProcThreadAttribute(attr_list, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
inherit_handles,
inherit_count * sizeof(inherit_handles[0]),
NULL, NULL))
goto out;
siex.lpAttributeList = attr_list;
startup_info = &siex.StartupInfo;
creation_flags = EXTENDED_STARTUPINFO_PRESENT;
inherit = TRUE;
}
memset(pi, 0x00, sizeof(*pi));
if (!CreateProcess(cmd_exe, cmd2, NULL, NULL, inherit,
creation_flags, NULL, NULL, startup_info, pi)) {
if (last_error != NULL)
*last_error = GetLastError();
goto out;
}
rc = 0;
out:
if (attr_list_initialized)
DeleteProcThreadAttributeList(attr_list);
if (attr_list != NULL)
free(attr_list);
if (child_stdout != NULL)
CloseHandle(child_stdout);
free(cmd2);
free(cmd_exe);
return rc;
}
#endif
int ivl_run_cmd(const char *cmd, int verbose)
{
if (verbose)
fprintf(stderr, "Executing: %s", cmd);
#ifdef __MINGW32__
DWORD exit_code = 1;
DWORD last_error;
PROCESS_INFORMATION pi;
if (start_cmd_process(cmd, NULL, &pi, &last_error) != 0) {
if (last_error != 0)
fprintf(stderr, "CreateProcess failed (%lu).\n", last_error);
return -1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return exit_code;
#else
return system(cmd);
#endif
}
FILE *ivl_run_cmd_pipe(const char *cmd)
{
#ifdef __MINGW32__
SECURITY_ATTRIBUTES sa;
HANDLE read_pipe = NULL;
HANDLE write_pipe = NULL;
PROCESS_INFORMATION pi;
int fd;
FILE *fp;
memset(&sa, 0x00, sizeof(sa));
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
if (!CreatePipe(&read_pipe, &write_pipe, &sa, 0))
return NULL;
if (start_cmd_process(cmd, write_pipe, &pi, NULL) != 0) {
CloseHandle(read_pipe);
CloseHandle(write_pipe);
return NULL;
}
CloseHandle(write_pipe);
CloseHandle(pi.hThread);
/* Match popen("r") text-mode semantics. The lexer expects CRLF from
* Windows command output to be normalized before it sees `line directives.
*/
fd = _open_osfhandle((intptr_t)read_pipe, _O_RDONLY | _O_TEXT);
if (fd < 0) {
CloseHandle(read_pipe);
TerminateProcess(pi.hProcess, 1);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
return NULL;
}
fp = _fdopen(fd, "rt");
if (fp == NULL) {
_close(fd);
TerminateProcess(pi.hProcess, 1);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
return NULL;
}
/* The child owns the pipe writer. We do not need the process handle
* unless we want pclose() style exit status.
*/
CloseHandle(pi.hProcess);
return fp;
#else
return popen(cmd, "r");
#endif
}
int ivl_close_cmd_pipe(FILE *fp)
{
#ifdef __MINGW32__
return fclose(fp);
#else
return pclose(fp);
#endif
}

36
libmisc/CmdExec.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef IVL_CmdExec_H
#define IVL_CmdExec_H
/*
* Copyright (c) 2026 Lars-Peter Clausen <lars@metafoo.de>
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
int ivl_run_cmd(const char *cmd, int verbose);
FILE *ivl_run_cmd_pipe(const char *cmd);
int ivl_close_cmd_pipe(FILE *fp);
#ifdef __cplusplus
}
#endif
#endif /* IVL_CmdExec_H */

View File

@ -44,6 +44,7 @@
# include <cstdlib>
# include <cctype>
# include "CmdExec.h"
# include "ivl_assert.h"
# include "ivl_alloc.h"
@ -3466,17 +3467,14 @@ int pform_parse(const char*path)
if (strcmp(path, "-") == 0) {
vl_input = stdin;
} else if (ivlpp_string) {
char*cmdline = static_cast<char*>(malloc(strlen(ivlpp_string) +
strlen(path) + 4));
strcpy(cmdline, ivlpp_string);
strcat(cmdline, " \"");
strcat(cmdline, path);
strcat(cmdline, "\"");
size_t cmdlen = strlen(ivlpp_string) + strlen(path) + 4;
char*cmdline = static_cast<char*>(malloc(cmdlen));
snprintf(cmdline, cmdlen, "%s \"%s\"", ivlpp_string, path);
if (verbose_flag)
cerr << "Executing: " << cmdline << endl<< flush;
vl_input = popen(cmdline, "r");
vl_input = ivl_run_cmd_pipe(cmdline);
if (vl_input == 0) {
cerr << "Unable to preprocess " << path << "." << endl;
return 1;
@ -3526,7 +3524,7 @@ int pform_parse(const char*path)
if (vl_input != stdin) {
if (ivlpp_string)
pclose(vl_input);
ivl_close_cmd_pipe(vl_input);
else
fclose(vl_input);
}