Tcl on VxWorks

Introduction

FPX: I spent some time in early 2008 porting Tcl to VxWorks[1 ]. The result is a patch that I posted to the Tcl feature tracker here: http://sourceforge.net/tracker/index.php?func=detail&aid=1955146&group_id=10894&atid=360894 .

This file is far from being a readily mergeable patch -- see some of the ugly technical details below -- but it should get someone started who is desperate to run Tcl on VxWorks.

These instructions will result in a Tcl DKM (downloadable kernel module) that can be run and interacted with on the console. Running as a VxWorks RTP (real-time process) mode is not supported, although that should be a comparatively small step.


Building Tcl for VxWorks


These instructions are for a cross-build from Microsoft Windows to, for reference, the "SIMNTgnu" target (which is the Windows-based VxWorks simulator that comes with Workbench) from the Cygwin command line. For other targets, update the tools and their parameters accordingly (e.g., use the ccppc compiler instead of ccpentium for PowerPC targets).

Apply VxWorks Patch

Download the Tcl 8.5.2 source code.

Download the patch: http://sourceforge.net/tracker/download.php?group_id=10894&atid=360894&file_id=276425&aid=1955146 (this is a plain-text file).

Apply the patch to the Tcl 8.5.2 source code using the "patch" program.

Configure for SIMNTgnu

Some of these settings must be adjusted based on your target.

  export WIND_BASE="c:\\WindRiver\\vxworks-6.3"
  export CC="ccpentium"
  export CPPFLAGS="-D_WRS_KERNEL -DCPU=SIMNT -DTOOL_FAMILY=gnu -DTOOL=gnu -Ic:/WindRiver/vxworks-6.3/target/h -Ic:/WindRiver/vxworks-6.3/target/h/wrn/coreip -DUSE_FIONBIO"
  export CFLAGS="-mtune=i486 -march=i486 -ansi"
  export LDFLAGS="-r -nostdlib"
  export EXTRA_TCLSH_LIBS="-Wl,-X -T `cygpath --mixed ${WIND_BASE}/target/h/tool/gnu/ldscripts/link.OUT`"
  export DL_OBJS="tclLoadVxWorks.o"
  export SHLIB_SUFFIX=".out"
  export SHLIB_LD="ccpentium -r -nostdlib"
  export SHLIB_LD_LIBS="-Wl,-X -T `cygpath --mixed ${WIND_BASE}/target/h/tool/gnu/ldscripts/link.OUT`"
  export AR="arpentium"
  export ac_cv_c_bigendian=no

Generic Configuration

These settings are to convince the configure script of the existence of various VxWorks functionality, rather than determining the host's functionality.

 export ac_cv_func_memcmp_clean=yes
 export ac_cv_func_strtoll=no
 export ac_cv_func_strtoull=no
 export ac_cv_func_strncasecmp=no
 export ac_cv_func_realpath=no
 export ac_cv_func_uname=no
 export ac_cv_func_gethostbyname_r=no
 export ac_cv_func_gethostbyaddr_r=no
 export tcl_cv_strstr_unbroken=ok
 export tcl_cv_strtoul_unbroken=ok 
 export tcl_cv_strtod_unbroken=ok 
 export tcl_cv_strtod_buggy=ok
 export tcl_cv_timezone_long=no
 export tcl_cv_timezone_time=no
 export tcl_cv_putenv_copy=yes
 export ac_cv_lib_pthread_pthread_mutex_init=yes
 export no_pipe=yes

Run Configure

Now run the autoconf script:

 ./configure --enable-threads --disable-shared --enable-symbols --host=vxworksdkm

Fix Makefiles for Cygwin

When building on Windows using Cygwin, the Makefile needs to be hacked so that make sees cygwin-style paths while the compiler sees Windows-style paths:

 sed -e 's/\$(CC)\(.*\)\(\$[A-Za-z0-9_./()]*\)$/$(CC)\1\`cygpath --windows \2\`/' < Makefile > Makefile.1
 sed -e 's/-I\(\${[A-Z_]*}\)/-I\`cygpath --windows \1\`/g' < Makefile.1 > Makefile.2
 sed -e 's%-L/cygdrive/\(.\)\(/[A-Za-z0-9._/]*\)%-L\1:\2%g' < Makefile.2 > Makefile.3
 sed -e 's%\t\t\(\$(GENERIC_DIR)/tclPkgConfig.c\)%\t\t\`cygpath --windows \1\`%' < Makefile.3 > Makefile.4
 sed -e 's%\t\t\(\$(UNIX_DIR)/tclUnixInit.c\)%\t\t\`cygpath --windows \1\`%' < Makefile.4 > Makefile.5

This shouldn't be necessary for a cross-build from Unix.

Build

After all of the above, Tcl should build fine. On Windows, use the hacked Makefile.5 (see above).

This should result in the loadable module tclsh.out.


Using Tcl


In the VxWorks C interpreter, load the Tcl DKM built above -- assumed to be in the /tcl directory, adjust as necessary -- using

 ld < /tcl/tclsh.out

Before starting Tcl, the pre-init script must be set to configure Tcl's library path, e.g., by running the following command in the VxWorks C interpreter:

 TclSetPreInitScript ("set ::tclDefaultLibrary /tcl/library");

Note: if this step omitted, then Tcl will fail, complaining that it can not find the init.tcl file. Then it will crash.

The Tcl module provides two entry points that can be called from the C interpreter:

tcl
Enters an interactive Tcl shell session. Line editing and history follows the usual VxWorks rules. Use shellConfigSet INTERPRETER=C to return to the C interpreter. Tcl interpreter state is preserved when switching in and out of the interactive session. Ctrl-C can be used to restart the interpreter (at which point state is lost).
tclsh file
This executes the contents of file in a new Tcl interpreter (no state is shared with other sessions).

Tcl Extensions


The following VxWorks-specific commands are available:

shellConfigSet

Usage:

 shellConfigSet config

This calls shellConfigSet (CURRENT_SHELL_SESSION, config).

The primary purpose of this command is to switch back from an interactive Tcl session to the C interpreter using shellConfigSet INTERPRETER=C. See the shellLib documentation for more information.

shellEvaluate

Usage:

 shellEvaluate cmd ?interpreter?

This evaluates cmd in interpreter and returns the result. If omitted, interpreter defaults to C.

See the documentation for shellInterpEvaluate() for more information.

Only integer-type return values are supported. While shellInterpEvaluate() appears to return the polymorphic type SHELL_EVAL_VALUE, it is not documented, and I have not seen its type field be anything else than SHELL_INT.

shellExec

Usage:

 shellExec cmd ?interpreter?

This evaluates cmd in interpreter and returns the output, similar to capturing a process' standard output. If omitted, interpreter defaults to C.

The shell echoes commands as they are interpreted, so cmd is included in the output. The C shell also prints the value that each individual command evaluates to. So some post-processing of the returned value is necessary.

A new interpreter session is created, which has its working directory initialized to some default, rather than inheriting Tcl's working directory. cmd should use absolute file names or change to the desired directory before accessing any files.

This command may hang if cmd attempts to read from the console.

exit

The exit command is replaced to delete the current interpreter rather than exiting from the process -- the latter would make Tcl unusable. If this happens at an inopportune time, the error "eval called on a deleted interpreter" may occur. This is harmless, but Tcl should be modified to avoid this error.

If exit is used in an interactive session, the current Tcl interpreter is deleted, and the session is returned to the C interpreter. When tcl is called again, a new Tcl interpreter session is started.


BSP Features


For reference, I needed to enable the following features in the kernel configurator beyond the "default":

  • INCLUDE_GETADDRINFO
  • INCLUDE_GETIFADDRS
  • INCLUDE_GETNAMEINFO
  • INCLUDE_GETNAMEINFO_SYSCTL
  • INCLUDE_GETSERVBYNAME
  • INCLUDE_STRICMP

Tcl Features


Loadable Tcl modules must be built as DKMs, and should be stub-enabled.

Sorry, no threads yet.

No pipes or exec.


The Gory Details


Here are some implementation details of the VxWorks port:

  • For better or worse, the patch modifies Tcl's unix port rather than creating a separate vxworks port. That kept the patch smaller, but makes it impossible to merge.
  • The patch does not make use of some the POSIX APIs that were added in more recent versions of VxWorks, but instead uses the legacy APIs.
  • When TclpObjAccess() is called with an R_OK (is the file readable?) parameter, it is replaced with a check for F_OK (does the file exist?). VxWorks' access(...,R_OK) erroneously returned false for files on NFS-mounted volumes even though they were readable. This means that file readable may now return a false positive in some cases.
  • The probably greatest hack is the use of the event-based rather than the threaded notifier. The major reason is that VxWorks does not have anonymous pipes (the pipe() system call) that the threaded notifier uses. VxWorks does have named pipes which could be used instead, but I did not get around to implementing that. So far, the event-based notifier seems to hold up well even when multiple threads are running. However, given that I don't understand all the details, I can't guarantee that this won't cause issues in more complex applications.
  • In VxWorks DKM mode, everything runs as tasks (threads) in the kernel "process". Calling exit() is therefore not possible. Tcl_Exit() calls Tcl_Finalize(), which renders the Tcl state unusable for other threads. Tcl_SetExitProc() was not an option since it is expected to not return. Therefore the patch overload's Tcl's exit command to delete the current interpreter instead. That seems to work for the most part, except for a few places where Tcl kept calling Tcl_Eval() without checking for interpreter deletion first -- so I added the appropriate checks in the places that I came across.
  • VxWorks has no timezone, user id, group id, or /etc/passwd, so those features are commented out. Note that many VxWorks systems do not have real-time clocks, so unless the time is explicitly set (e.g., using Network Time Protocol - NTP), the clock starts at zero.
  • I shied away from implementing threads so far, because they depend on pthread_exit(). Its equivalent in the VxWorks legacy API, taskDelete(), does not clean up resources that the thread was using, and might thus cause memory leaks and other issues.