Tcl interpreter undefined reference error while compiling with gcc - c

I am new to Tcl scripting and would like to use C to embed Tcl codes.
This is the code that I have copied from a website to test the Tcl-C working.
test.c
#include <stdio.h>
#include <tcl.h>
void main ()
{
Tcl_Interp *myinterp;
char *action = "set a [expr 5 * 8]; puts $a";
int status;
printf ("Your Program will run ... \n");
myinterp = Tcl_CreateInterp();
status = Tcl_Eval(myinterp,action);
printf ("Your Program has completed\n");
getch();
}
I am using MinGW to compile this file.
I have copied the contents of the C:\Tcl\include folder into the C:\MinGW\include folder as well.
My gcc command for compiling :
gcc -o test.exe test.c
The error message shown :
C:\Users\user\AppData\Local\Temp\ccEHJKCb.o:tcl_connection_test.c:(.text+0x23): undefined reference to `_imp__Tcl_CreateInterp'
C:\Users\user\AppData\Local\Temp\ccEHJKCb.o:tcl_connection_test.c:(.text+0x3d): undefined reference to `_imp__Tcl_Eval'
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: C:\Users\user\AppData\Local\Temp\ccEHJKCb.o: bad reloc address 0x20 in section `.eh_frame'
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: final link failed: Invalid operation
collect2.exe: error: ld returned 1 exit status
I don't seem to have any libtcl file in the Tcl folder.
The Tcl version is ActiveTcl 8.5.15.0.297577.
Any help would be really appreciated.

Your example how to embed Tcl is outdated, and you are missing certain things in your link line (-ltcl85 for example). If you simply add -ltcl85 to your link line it should start to work.
It does not work in your case, because you installed the x64 (64-Bit version) of ActiveTcl, which provides x64 dlls, not 32-Bit ones. But the standard mingw gcc only works with 32-Bit libraries.
So to get this to work:
Download the 32-Bit ActiveTcl distribution
Compile your code with gcc -o test.exe test.c -Lc:/tcl/lib -Ic:/tcl/include -ltcl86
Adjust your path so the c:\tcl\bin\tcl86.dll is found in PATH, make also sure Tcl finds its libdir (set TCL_LIBRARY=c:\tcl\lib\tcl8.6)
run your program
But for more complex examples, you still need to initialise the library and a do some boilerplate code, so please call Tcl_FindExecutable(argv[0]); before the call to Tcl_CreateInterp() otherwise a few commands (e.g. clock might just not work as expected).
Have a look at http://www.tcl.tk/cgi-bin/tct/tip/66.html for some more details. Also have a look at the Tcl source distribution and the source for the tclsh shell.

You're very close to getting it right.
The Tcler's Wiki has a few examples, some of which are very confusing to be frank, but this one from this page is the best I've spotted recently. (The comments are mine.)
#include <stdlib.h>
#include <tcl.h>
int main (int argc, char *argv[]) {
Tcl_Interp *interp;
const char *script = "proc p1 a { puts $a }";
// Initialize the Tcl library; ***STRONGLY RECOMMENDED***
Tcl_FindExecutable(argv[0]);
// Create the interpreter, the execution context
interp = Tcl_CreateInterp();
// Initialise the interpreter
if (TCL_OK != Tcl_Init(interp)) {
fprintf(stderr, "Tcl_Init error: %s\n", Tcl_GetStringResult(interp));
exit(EXIT_FAILURE);
}
// Define a procedure
Tcl_Eval(interp, script);
fprintf(stderr, "res 1: %s\n", Tcl_GetStringResult(interp));
// Check if the procedure exists
Tcl_Eval(interp, "puts [info commands p*]");
fprintf(stderr, "res 2: %s\n", Tcl_GetStringResult(interp));
// Call the procedure
Tcl_Eval(interp, "p1 abc");
fprintf(stderr, "res 3: %s\n", Tcl_GetStringResult(interp));
// We could use Tcl_DeleteInterpreter to clean up here, but why bother?
return EXIT_SUCCESS;
}
What else were you missing? Simple. You forgot to tell the C compiler to use the Tcl library when building the executable; the compiler (or, more strictly, the linker) is in places a stupid piece of code. The exact option to use to get the linker to add the library in will depend on your system configuration, but is probably going to be -ltcl, -ltcl8.5 or -ltcl8.6; which it is depends on the filename and all sorts of things that we can't check exactly without being on your system. The names do fit a simple pattern though.
It's also possible that you might need to pass the -L option in to tell the linker about additional library locations. (There's an equivalent -I for telling the compiler where to find include files, so you don't have to copy everything into one gigantic unmanageable directory.)
The order of arguments can matter. Libraries should be listed after the source file:
gcc -o test.exe test.c -L/mingw/path/to/library/directory -ltcl86
(If you're using old, unsupported versions of Tcl — why would you do that?! — then the code above won't work because Tcl_Eval then took a writable string. But that was fixed many years ago and upgrading to a current version is the fix.)

Related

TCL: call TCL package while embedded in C

From this question I learnt that we can embed TCL in C as easy as the folloiwng
#include <stdio.h>
#include <tcl.h>
void main ()
{
Tcl_Interp *myinterp;
char *action = "set a [expr 5 * 8]; puts $a";
int status;
printf ("Your Program will run ... \n");
myinterp = Tcl_CreateInterp();
status = Tcl_Eval(myinterp,action);
printf ("Your Program has completed\n");
getch();
}
And to compile it, we need to define path to the tcl libraries:
gcc -o test.exe test.c -Ic:/tcl/include /mingw64/bin/tcl86.dll
My question is: If my tcl script is calling another package (for example: package require Img), How to include this package (for example "Img") in the created test.exe.
I am using mingw64 on windows to compile my C code, but when running the resulted test.exe, it gives me TCL error that {can't find package Img while executing "package require Img"}
BTW, I have Img is installed in and when I run my TCL script using tclsh, I have no errors.
You should extend the list in the global auto_path variable with the path to the location (i.e., the directory) of the extra libraries you want to be able to access.
Tcl_SetVar(interp, "::auto_path", "/path/to/directory", TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
Do this after you create the interpreter but before you evaluate any scripts in it. This is safe against characters like spaces in the pathname. On Windows, you can use \ as a separator if you prefer. If you have multiple locations, put several calls to Tcl_SetVar() in. (How you work out the correct directory or directories is up to you; the value gets copied immediately.)

Compile and Link to .com file with Turbo C

I'm trying to compile and link a simple program to a DOS .com file using Turbo C compiler and linker. By that I try the simplest C-program I can think of.
void main()
{}
Are there command line arguments to link to com files in the Turbo C Linker?
The Error Message I get from the Linker is the following:
"Fatal: Cannot generate COM file: invalid entry point address"
I know that com files need entry point to be at 100h. Does Turbo C have an option to set this address?
It has been a long time since I have genuinely tried to use Turbo-C for this kind of thing. If you are compiling and linking on the command line separately with TCC.EXE and TLINK.EXE then this may work for you.
To compile and link to a COM file you can do this for each one of your C source files creating an OBJ file for each:
tcc -IF:\TURBOC3\INCLUDE -c -mt file1.c
tcc -IF:\TURBOC3\INCLUDE -c -mt file2.c
tcc -IF:\TURBOC3\INCLUDE -c -mt file3.c
tlink -t -LF:\TURBOC3\LIB c0t.obj file1.obj file2.obj file3.obj,myprog.com,myprog.map,cs.lib
Each C file is compiled individually using -mt (tiny memory model) to a corresponding OBJ file. The -I option specifies the path of the INCLUDE directory in your environment (change accordingly). The -c option tell TCC to compile to a OBJ file only.
When linking -t tells the linker to generate a COM program (and not an EXE), -LF:\TURBOC3\LIB is the path to the library directory in your environment (change accordingly). C0T.OBJ is the C runtime file for the tiny memory model. This includes the main entry point that you are missing. You then list all the other OBJ files separated by a space. After the first comma is the output file name. If using -t option name the program with a COM extension. After the second comma is the MAP file name (you can leave the file name blank if you don't want a MAP file). After the third comma is the list of libraries separated by spaces. With the tiny model you want to use the small model libraries. The C library for the small memory model is called CS.LIB .
As an example if we have a single source file called TEST.C that looks like:
#include<stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
}
If we want to compile and link this the commands would be:
tcc -IF:\TURBOC3\INCLUDE -c -mt test.c
tlink -t -LF:\TURBOC3\LIB c0t.obj test.obj,test.com,test.map,cs.lib
You will have to use the paths for your own environment. These commands should produce a program called TEST.COM. When run it should print:
Hello, world!
You can generate COM file while still using IDE to generate EXE. Following worked on TC 2.01. Change memory model to Tiny in the options, then compile the program and generate EXE file, then go to command prompt, and run EXE2BIN PROG.EXE PROG.COM. Replace PROG with your program name.
Your problem is about "entry point"
some compiler or linker can recognize void main() like entry point omiting a return value but no all of them.
You shoud use int main() entry point instead for better control of app and compiler can recognize main function as entry point
example:
int main() {
/* some compiler return 0 when you don't for main,
they can ask for return value */
}
from geekforgeeks:
A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to “the system” that invokes it. On systems that doesn’t provide such a facility the return value is ignored, but that doesn’t make “void main()” legal C++ or legal C. Even if your compiler accepts “void main()” avoid it, or risk being considered ignorant by C and C++ programmers.
In C++, main() need not contain an explicit return statement. In that case, the value returned is 0, meaning successful execution.
source: https://www.geeksforgeeks.org/fine-write-void-main-cc/

Compiling C code without having it saved in a file

Inspired by this PCG challange: https://codegolf.stackexchange.com/q/61836/31033
I asked my self, if one would try to leave as few trace as possible when compiling such kind of tool (no matter of a browser or something else), is there some way (aimed for gcc/clang as this probably are the preinstalled commandline compillers in such a working enviroment) to hand over source code to the compiler as command line argument or equal mechanism, without need for the source code beeing saved as *.c file, as the user would usually do?
(ofcourse the compiler will produce temp files while compiling, but those probably won't get scanned.)
At least gcc can as it is able to read source from the standard input. You can also use Unix here string bash construction :
gcc -xc - << "int main() { exit(0); }"
or here file sh construction :
gcc -xc - <<MARK
int main() {
exit(0);
}
MARK
----EDIT----
You can also imagine using cryptography to encode your source, uncipher the content on the fly and inject the result to the standard input of gcc, something like:
uncipher myfile.protected | gcc -xc -

Using Go on existing C project

I have a program entirely written in C that uses multiple object (.o) files in it. These files are all packed inside an archive file (.a) which, in turn, is used at compile-time of the program's main (.c) file.
I want to write a new file for this project in Go. My idea is to write this .go file and then create an object (.o) file from it. Afterwards, I want to put this object file inside the already mentioned archive (.a) file.
This basically means that I want to call Go functions from a C program. I've read this question, and while it showed me that what I want is possible via GCCGO, it's not 100% clear as to how to do it.
Even with the most basic of tests, I get errors during the linking phase. More specifically, here's one of such basic example:
printString.go
package main
import
(
"fmt"
)
func PrintString(buff string) int {
fmt.Printf(buff)
return 1
}
c_caller.c
#define _GNU_SOURCE
#include <stdio.h>
extern int PrintString(char*) __asm__ ("print.main.PrintString");
int main() {
char *string_to_pass= NULL;
asprintf(&string_to_pass, "This is a test.");
int result= PrintString(string_to_pass);
if(result) {printf("Everything went as expected!\n");}
else {printf("Uh oh, something went wrong!\n");}
return result;
}
Compiling
In order to compile the Go file, I used this command:
gccgo -c printString.go -o printString.o -fgo-prefix=print -Wall -Werror -march=native
In order to compile the entire thing, I used this command:
gccgo -o main c_caller.c printString.o -Wall -Werror -march=native
The return message I'm getting is:
/usr/lib64/libgo.so.4.0.0: undefined reference to `main.main'
/usr/lib64/libgo.so.4.0.0: undefined reference to `__go_init_main'
collect2: error: ld returned 1 exit status
Which means that GCCGO's expecting a main function in the Go file instead of the C one.
Using the --static-libgo, -static and -Wl,-R,/path/to/libgo.so's_folder options on the second command yield a different result:
/usr/bin/ld: cannot find -lgo
collect2: error: ld returned 1 exit status
Which makes no sense, since I have the LD_LIBRARY_PATH environment variable properly pointing to libgo.so's folder.
I realize that I'm probably doing something wrong here, but I just can't see what that is. There's next to no examples of GCCGO and its interaction with C out there, and the only reference I could find was this page, which I personally feel like it's not enough.
I ask kindly for some advice on this matter and thank you for your time. :)
This may not be what you want, but in Go 1.5, that's coming this August, you'll be able to build C-compatible libraries with the go tool. So with this in your _main.c
#include <stdio.h>
int main()
{
char *string_to_pass = NULL;
if (asprintf(&string_to_pass, "This is a test.") < 0) {
printf("asprintf fail");
return -1;
}
PrintString(string_to_pass);
return 0;
}
and this in your main.go
package main
import "C"
import "fmt"
//export PrintString
func PrintString(cs *C.char) {
s := C.GoString(cs)
fmt.Println(s)
}
func main() {}
You can do, for static library:
go build -buildmode c-archive -o mygopkg.a
gcc -o main _main.c mygopkg.a -lpthread
For shared library:
go build -buildmode c-shared -o mygopkg.so
LD_RUN_PATH=$(pwd) gcc -o main _main.c mygopkg.so -lpthread
(LD_RUN_PATH is here to make the linker look for the shared library in the same directory you're building.)
See the Go execution modes design document for more info.
There currently isn't a supported way to do what you want. Go always needs the support of its runtime, and the entry point for that is always main. AFAIK, gccgo also makes these same assumptions, and doesn't provide a way to easily link into from other programs.
If you want to do this in a supported manner, you will have to wait until go1.5+ where work is being done to compile shared libraries from Go code.
If you really want to hack on this now, you can look into how the Android port works using the default gc toolchain with -linkmode external, which renames main in the object file and calls it externally.

Combine C and TCL using Swig

I have been following a tutorial to combine C with TCL using Swig. The tutorial seemed to be properly working but at the end I ran into an error that I cannot solve. The situation is as follows:
The tutorial I was following is:
http://www.swig.org/tutorial.html.
I have a file named test.c:
char *HelloWorld()
{
return "hello world";
}
and another named test.i:
%module test
%{
/* Put header files here or function declarations like below */
extern char *HelloWorld();
%}
extern char *HelloWorld();
I then used the following command line arguments to ready the correct files:
gcc -c test.c -o test.o
swig -tcl test.i
gcc -c test_wrap.c -o test_wrap.o
gcc -dynamiclib -framework Tcl test.o test_wrap.o -o test.so
And finally I tried to load it using:
tclsh
% load test.so test
This is the point where I received the following error:
dlsym(0x100600090, Test_Unload): symbol not founddlsym(0x100600090, Test_SafeUnload): symbol not found
As far as I know I did not stray from the tutorial. Can anyone tell me how it is that I got this error and more importantly how to get rid of it?
Thanks in advance!
Are those error messages stopping the load from working? They shouldn't; they're reporting that the low-level API for supporting unloading of the extension isn't present, but that's OK (lots of extensions can't be unloaded; it's tricky to write code that supports it).
You don't mention exactly which version of Tcl you are using — but it must be at least 8.5 for those symbols to be even searched for in the first place — so it is a little hard to guess what the exact underlying issue is. (The message should simply not be reported.) I advise filing a bug report on this; make sure you include all exact versions in your report.
It's a long time since I used SWIG, so I'm not sure whether it gives you sufficient control over the code it generates for you to be able to apply this fix. Glossing over that detail, I can reproduce (and fix) the issue with the following:
In 'ext.c':
#include <tcl.h>
int DLLEXPORT Ext_Init(Tcl_Interp *interp) {
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
if (Tcl_PkgProvide(interp, "Ext", "0.0") == TCL_ERROR) {
return TCL_ERROR;
}
return TCL_OK;
}
Build, run tclsh, load extension:
$ gcc -dynamiclib -framework Tcl ext.c -o ext.so
$ tclsh8.5
% load ./ext.so
dlsym(0x400000, Ext_SafeInit): symbol not found
dlsym(0x400000, Ext_Unload): symbol not found
dlsym(0x400000, Ext_SafeUnload): symbol not found
Something internal to the library loading code is putting that error message into the interpreters result. To stop the message ever surfacing, set or reset the result so that the _Init() function ends with one or other of:
// Set the result to a message of your choosing
Tcl_SetObjResult(interp, Tcl_NewStringObj("ok", -1));
// Or clear out the result altogether
Tcl_ResetResult(interp);
return TCL_OK;
}
The init block feature of swig might insert code in the right place to achieve the same thing:
%init %{
Tcl_ResetResult(interp);
%}

Resources