Resolving symbols colision at build time - c

I have some symbols collision in a C program, some previous search leads to this objcopy the problem is my workflow is a golang cgo one so I don't deal myself with the .o and .a (I know I could be the goal of my lib is to be used by other people so I can't have a custom golang workflow.).
More info of what I need:
I have a bunch of functions doing various things in my go lib, this code is autogenerated and can't be predicted, they sometimes collide (have the same name) with other function later in the build pipeline, so I would like all the C function in my go lib to be renamed, this can either happend at build time using the standart cgo process (basicaly build each file first with gcc and then link them all up) or after the autogeneration of the code (I guess I could run a preprocessor renaming all the functions and there calls in the source but I weren't able to find one).
What I've tried already :
#pragma extern_prefix
This and this seems very promising but whatever I try I can't get it to works :
// test.c
#include <stdio.h>
#pragma extern_prefix "TestPrefix"
int test() {
printf("Hello, World!\n");
return 0;
}
#pragma extern_prefix ""
int main() {
return test();
}
Shell output :
$ gcc test.c -o test && ./test && nm -an test | grep test
Hello, World!
0000000000000000 a test.c
0000000000001149 T test
Unlike what I expected the test symbol isn't prefixed like I expect (with my understanding the symbol should be TestPrefixtest).

Related

"Compiling" go project as C shared object library on AIX 7.2 results in Executable that doesn't run

EDIT: For any poor soul that finds this, in search of a solution for the shared library from go conundrum: I was unable to find a solution that uses go and I would suggest, that until google go provides native c-shared support for AIX you should find an alternative for your project.
I did not go forward with gccgo because that felt like an entirely different can of worms that I was unwilling to delve further into. FWIW I myself am going forward switching to pure C implementation, because there I at least have a (somewhat) firm(er) understanding of it all.
Should anyone find a solution I'd love to hear from you and see how you got around this limitation.
Environment:
AIX 7.2
go1.16.12 aix/ppc64
gcc-8
I want to create a C shared object library (in usual unix vernacular a .so file) out of a golang project on AIX 7.2 so that it can be used by a C application.
I can compile it down to a final a.out binary in my example, but it can then not be executed because the shared object is apparently compiled the wrong way.
So far I have achieved the following:
Suppose my example go "library" sharedLibTest.go:
package main
import (
m "fmt"
)
import "C"
func main() {
fmt.Printf("%s\n", "Golang: main was called")
MyPackage_Init()
MyPackage_Create()
}
//export MyPackage_Init
func MyPackage_Init() {
fmt.Printf("%s\n", "Golang: MyPackage_Init was called")
}
//export MyPackage_Create
func MyPackage_Create() {
fmt.Printf("%s\n", "Golang: MyPackage_Create was called")
}
And some C application that calls these functions in main.c:
#include <stdio.h>
#include "sharedLibTest.h"
int main() {
printf("%s\n", "C: main() called");
MyPackage_Init();
MyPackage_Create();
}
m
Now, because AIX feels the need to do things differently the current golang toolchain does not support directly creating a c-shared object with -buildmode=c-shared. Instead I am trying to do the roundabout way by first creating a static lib with -buildmode=c-archive, compiling that into a shared object using gcc-8 and use that in my "target C application".
I can compile sharedLibTest.go this with
go build -v -buildmode=c-archive -mod vendor -o /home/myuser/workspace/go_proj/sharedLibTest/sharedLibTest.a /home/myuser/workspace/go_proj/sharedLibTest/sharedLibTest.go
Because the symbols MyPackage_Init and MyPackage_Create are not exported by default in AIX, I need to manually create an extra symbol file with
$ cat > file.exp << EOF
> MyPackage_Init
> MyPackage_Create
> EOF
Source
(If there are any ideas how i can omit this file.exp step I'd really appreciate it)
Now with that I can compile a shared object out of that by running
gcc -g -O2 -mcpu=power7 -maix64 -shared -lpthread -Wl,-bE:file.exp -o libsharedLibTest.so -Wl,-bnoobjreorder ./sharedLibTest.a
Now because AIX does not look for .so files but only .a files even if they are shared libraries, I rename the resulting libsharedLibTest.so into libsharedLibTest.a with
mv libsharedLibTest.so libsharedLibTest.a
Lastly I want to compile my C applications with
gcc -L/home/myuser/workspace/go_proj/sharedLibTest -g -O2 -mcpu=power7 -maix64 -Wl,-bnoobjreorder -lsharedLibTest -lpthreads main.c
This succeeds and I get my a.out file as a result.
However, when I try to run this with the following, I only get the error below
LD_LIBRARY_PATH=/home/myuser/workspace/go_proj/sharedLibTest ./a.out
$ ./a.out
exec(): 0509-036 Cannot load program ./a.out because of the following errors:
0509-150 Dependent module /home/myuser/workspace/go_proj/sharedLibTest/libsharedLibTest.a(libsharedLibTest.so) could not be loaded.
0509-187 The local-exec model was used for thread-local
storage, but the module is not the main program.
0509-193 Examine the .loader section header with the
'dump -Hv' command.
Some hours of googling so far have revealed that I might be missing the compile option -fPIC to create "emit position-independent code" however adding that flag to any of the above steps in various combinations has all resulted in the same error.
Clearly I need to add some compile option to tell the shared object not to be thread-local, however I am unclear how. Any ideas?
Few points... mv will not make an archieve, ar will. You need to use ar command to create .a file.
Second, use LIBPATH environment variable in place of LD_LIBRARY_PATH. Use of -fPIC option is irrelevant on AIX.

How integrate gnatmake/gnatbind/gnatlink in CMake files for C/Ada code?

I wrote a code in a few languages (C, C++, Fortran77, Fortran90) and I can compile it without any sort of problem by using CMake. It works out perfectly.
Now, I would like to add in the main(), which is written in C, some Ada function and I want to compile it by CMake. Given that I am not able to link my Ada function to the main one by using CMake, I get
main.c:(.text.startup+0x16a): undefined reference to adainit
main.c:(.text.startup+0x179): undefined reference to adafunction
main.c:(.text.startup+0x190): undefined reference to adafinal
I did another simplified test by using the main function (written in C) calling the only Ada function, which I coded, and I compiled it by using
gcc -c main.c
gnatmake -c lib_ada.ali
gnatbind -n lib_ada.ali
gnatlink lib_ada.ali main.o -o exe
and it works out. Do you know how I can integrate this approach in a CMakeList.txt?
Note: I think (maybe I mistake) I cannot use the only gnatlink because I need to link all other functions I already have.
Here is reported a minimal reproducible example.
--- main.c ---
#include <stdio.h>
extern int adainit();
extern int adafinal();
extern int Add(int,int);
int main()
{
adainit();
printf ("Sum of 3 and 4 is: %d\n", Add (3,4));
adafinal();
return 0;
}
--- lib_test.adb ---
package body Lib_Test is
function Ada_Add (A, B : Integer) return Integer is
begin
return A + B;
end Ada_Add;
end Lib_Test;
--- lib_test.ads ---
package Lib_Test is
function Ada_Add (A, B : Integer) return Integer;
pragma Export (C, Ada_Add, "Add");
end Lib_Test;
1° test: if you compile by using the following commands:
gcc -c main.c
gnatmake -c lib_test.adb
gnatbind -n lib_test.ali
gnatlink lib_test.ali main.o -o exe
and run ./exe you get Sum of 3 and 4 is: 7.
2° test: I tried to use the following CMake file (CMakeLists.txt) linking the *.a
cmake_minimum_required(VERSION 2.6)
project(Ada2C)
enable_language(C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -m64")
find_library(TEST_lib lib_test.a PATHS ${CMAKE_CURRENT_SOURCE_DIR})
message(STATUS "Finding library: ${TEST_lib}")
add_executable(TEST_release ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
target_link_libraries(TEST_release ${TEST_lib})
I generate library lib_test.a for the Ada function
gnatmake lib_test.adb
ar rc lib_test.a
I run the cmake and make and I get
main.c:(.text.startup+0x16a): undefined reference to adainit
main.c:(.text.startup+0x179): undefined reference to adafunction
main.c:(.text.startup+0x190): undefined reference to adafinal
More of a comment than an answer, but too long for a comment, so here goes:
Compiling Ada code into your binary means that your binary needs access to the GNAT runtime. This is one thing gnatlink does when you use it to link the final executable. The other thing is the b~<something>.ad{s,b} source gnatbind generates which you need to compile and link against as others mentioned.
The cleanest way to embed Ada in C I've seen so far is to create an encapsulated library. This probably does not make sense if your actual problem is with only one Ada function, but it does with larger chunks of Ada. The encapsulated library will be a shared library that has GNAT's runtime baked in. Being a shared library enables it to implicitly handle initialization during library loading so you don't need adainit() / adafinal() anymore.
The easiest way to create an encapsulated library is to use a ada_code.gpr file:
project ada_code is
for Library_Name use "mylib";
for Library_Dir use "lib";
for Library_Kind use "relocatable";
for Library_Standalone use "encapsulated";
for Library_Auto_Init use "true";
for Library_Interface use ("All", "Packages", "In.Your", "Ada.Code");
for Source_Dirs use ("adasrc");
end ada_code;
In CMake, you can then do:
# tell CMake how to call `gprbuild` on the `.gpr` file.
# you may need to replace `gprbuild` with the absolute path to it
# or write code that finds it on your system.
add_custom_target(compile_mylib
COMMAND gprbuild -P ada_code.gpr)
# copy the library file generated by gprbuild to CMake's build tree
# (you may skip this and just link against the file in the source tree)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mylib.so
DEPENDS compile_mylib
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/lib/mylib.so
${CMAKE_CURRENT_BINARY_DIR}/mylib.so)
# ... snip ...
# link to the copied library
# I am not 100% sure this adds the correct dependency to the custom command.
# You may need to experiment a bit yourself
target_link_libraries(TEST_release ${CMAKE_CURRENT_BINARY_DIR}/mylib.so)
In your C file, you can then delete everything related to adainit() and adafinal().

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/

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