I am doing some C code on a Solaris SPARC system and am scratching my head at an "inconsistency" I have found in my integration testing.
I am doing some tests into an SAP system and am using the SAP RFCSDK, but SAP is not my question here at all.....I am wondering if I am missing some compile options etc
Let me try explain:
Client code -> Shared lib(custom) -> Shared Lib (SAP) -> SAP === works 100%
Client code -> Shared lib(custom PROXY)-> Shared lib(custom) -> Shared Lib (SAP) -> SAP ==INVALID handle
So without going into too much detail have you ever heard of any "handle" becoming invalid just because another shared lib was "inbetween" the client and "destination shared lib"?
The PROXY shared lib that is causing the issue with the INVALID HANDLE does nothing special, just a "pass through"
#include <stdio.h>
#include <stdlib.h>
#include "saprfc_receiver_proxy.h"
#include "saprfc_receiver.h"
int saprfc_receiver_proxy_initialize(char *sap_hostname, char *sap_client,
int sap_sysnbr, char *sap_language, char *sap_user, char *sap_password,
int sap_rfctrace){
return saprfc_receiver_initialize(sap_hostname, sap_client, sap_sysnbr,
sap_language, sap_user, sap_password, sap_rfctrace);
}
int saprfc_receiver_proxy_beginTransaction(char *tid){
return saprfc_receiver_beginTransaction(tid);
}
int saprfc_receiver_proxy_commitTransaction(char *tid){
return saprfc_receiver_commitTransaction(tid);
}
int saprfc_receiver_proxy_rollbackTransaction(char *tid){
return saprfc_receiver_rollbackTransaction(tid);
}
int saprfc_receiver_proxy_writeMessage(char *tid, char *buffer){
return saprfc_receiver_writeMessage(tid, buffer);
}
int saprfc_receiver_proxy_openConnection(){
return saprfc_receiver_openConnection();
}
int saprfc_receiver_proxy_closeConnection(){
return saprfc_receiver_closeConnection();
}
The compilation is very straightforward, here is the shared lib compile that calls the SAP shared libs directly:
/usr/sfw/bin/gcc -m64 -R/usr/sfw/lib/64 -c -fPIC -I../include \
-I../rfcsdk/include saprfc_receiver.c -o saprfc_receiver.o
/usr/sfw/bin/gcc -m64 -R/usr/sfw/lib/64 -shared -L../rfcsdk/lib/ \
-o libsaprfc_receiver.so saprfc_receiver.o -lrfccm -lrfc
And here is the proxy shared lib compile:
/usr/sfw/bin/gcc -m64 -R/usr/sfw/lib/64 -c -fPIC -I../include \
saprfc_receiver_proxy.c -o saprfc_receiver_proxy.o
/usr/sfw/bin/gcc -m64 -R/usr/sfw/lib/64 -shared -L../lib \
-o libsaprfc_receiver_proxy.so saprfc_receiver_proxy.o -lsaprfc_receiver
So is there any "golden rule" when using C to make sure that "shared libs in between" do not influence the state at all?
Sorry if my explaining is bad, I will try edit this post later with more detail....
The bottom line is that if the client calls the shared lib (custom) directly then it works, the moment I add a SIMPLE proxy in between the SAP handle goes bad.
Related
I am forced to link two version of the same third party dynamic library (Linux .so, C language) into the same executable to support old and new functionality in the same process. Having two executables or remote services are undesirable.
I made the assumption that this must be a doable task. I tried to experiment with the naive approach of creating 2 proxy dynamic libraries each linked against one of the real libraries and have function renamed.
Unfortunately, this attempt failed – both new functions call the same target function.
I still want to believe that the problem is in the lack of my knowledge as there are plenty of compiler and linker ( gcc and ld) options.
I will appreciate any help. I also look forward to using dlopen/dlsym, but first want to check if the original approach can work.
Here is the sample code
/* ./old/b.c */
#include <stdio.h>
int b (int i)
{
printf("module OLD %d\n",i);
return 0;
}
/* ./old/Makefile */
libold.so: b.c
gcc -c -g b.c
gcc -shared b.o -o $#
/* ./new/b.c */
#include <stdio.h>
int b (int i)
{
printf("module new %d\n",i);
return 0;
}
/* ./new/Makefile */
libnew.so: b.c
gcc -c -g b.c
gcc -shared b.o -o $#
/* ./a1.c */
#include <stdio.h>
int b(int);
void call_new(void)
{
printf("will call new 1\n");
b(1);
printf("called new 1\n");
}
/* ./a2.c */
#include <stdio.h>
int b(int);
void call_old(void)
{
printf("will call old 2\n");
b(2);
printf("called old 2\n");
}
/* ./main.c */
#include <stdio.h>
int call_new(void);
int call_old(void);
int main()
{
call_new();
call_old();
return 0;
}
/* ./Makefile */
.PHONY: DEPSNEW DEPSOLD clean
main: liba1.so liba2.so main.c
gcc -c main.c
gcc -o main main.o -rdynamic -Wl,-rpath=new -Wl,-rpath=old -L . -la1 -la2
DEPSNEW:
make -C new
DEPSOLD:
make -C old
liba1.so: DEPSNEW a1.c
gcc -c -fpic a1.c
gcc -shared a1.o -L new -lnew -o liba1.so
liba2.so: DEPSOLD a2.c
gcc -c -fpic a2.c
gcc -shared a2.o -L old -lold -o liba2.so
clean:
find -name "*.so" -o -name "*.o" -o -name main | xargs -r rm
/* ./run.sh */
#/bin/sh
LD_LIBRARY_PATH=new:old:. main
The result is not that I want - function from "new" library is called twice
will call new 1
module new 1
called new 1
will call old 2
module new 2
called old 2
In this case, you can not control the automatic loading of the dynamic library in order to assure which library will be loaded for the depending libraries. What you can do, is to use one of the libraries (the new one) for the dynamic linker and to link the second library manually as follows:
Add function to dynamically load and link the function from the library.
a2.c
#include <stdio.h>
#include <dlfcn.h>
static int (*old_b)(int);
void init_old(void) {
void* lib=dlopen("./old/libold.so", RTLD_LOCAL | RTLD_LAZY);
old_b=dlsym(lib,"b");
}
void call_old(void)
{
printf("will call old 2\n");
old_b(2);
printf("called old 2\n");
}
call the initialization function
main.c
#include <stdio.h>
void init_old(void);
int call_new(void);
int call_old(void);
int main()
{
init_old();
call_new();
call_old();
return 0;
}
Modify the linker options to add the dynamic loading library -ldl
liba2.so: DEPSOLD a2.c
gcc -c -fpic a2.c
gcc -shared a2.o -L old -lold -ldl -o liba2.so
After this modification
~$ ./run.sh
will call new 1
module new 1
called new 1
will call old 2
module OLD 2
called old 2
I have an application that runs fine on Ubuntu but immediately exit at startup, WITHOUT ANY ERRORS, on Windows.
It seems that main() function is not entered.
Application has been compiled without errors and it uses SDL_image.h. When (in the same application) the code that uses SDL Image was not present the application was running fine.
In the .exe directory there are all the needed dlls, SDL2.dll, libjpeg-9.dll, libpng16-16.dll, libtiff-5.dll, libwebp-7.dll, SDL2_image.dll and zlib1.dll.
IMG_Init() is called before any other image functions in this way:
(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG)
and, as I said before, tha application runs without problems on Ubuntu.
This is a reproducible example.
This code works:
makefile
CC = gcc -fno-pie -no-pie -m32
CFLAGS = -IC:/sdl2/include -O0 -ggdb
LIBS = -LC:/sdl2/lib/x86 -lSDL2main -lSDL2
all:
$(CC) $(CFLAGS) main.c $(LIBS) -o application.exe
main.c
#include <stdio.h>
#include "SDL.h"
int main(int argc, char **argv)
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
SDL_Log("SDL fails to initialize video subsystem!\n%s", SDL_GetError());
return -1;
}
else
printf("SDL Video was initialized fine!\n");
return 0;
}
This code, where I use SDL Image library, doesn't do nothing, no errors, immediately exits:
makefile
CC = gcc -fno-pie -no-pie -m32
CFLAGS = -IC:/sdl2/include -IC:/sdl2_image/include -O0 -ggdb
LIBS = -LC:/sdl2/lib/x86 -LC:/sdl2_image/lib/x86 -lSDL2main -lSDL2 -lSDL2_image
all:
$(CC) $(CFLAGS) main.c $(LIBS) -o application.exe
main.c
#include <stdio.h>
#include "SDL.h"
#include "SDL_image.h"
int main(int argc, char **argv)
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
SDL_Log("SDL fails to initialize video subsystem!\n%s", SDL_GetError());
return -1;
}
else
printf("SDL Video was initialized fine!\n");
if((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG)
{
SDL_Log("SDL fails to initialize image handler!\n%s", IMG_GetError());
return -1;
}
else
printf("SDL Image was initialized fine!\n");
return 0;
}
As I said before, it seems that the problem is the windows library (or the environment) and this is why I didn't add a reproducible code before
After 2 days I found a solution.
First of all (but it isn't my problem) the SDL Image library is not so "smart" so it is important that SDL2_image.dll library and the used ones from libjpeg-9.dll, libpng16-16.dll, libtiff-5.dll, libwebp-7.dll and zlib1.dll are present. You'll not receive any error if one of needed library is missing.
I had a problem in my environment, on this machine I use Visual Studio and Eclipse. I made a mistake and my makefile pointed to Visual Studio SDL libraries and not to MinGW ones.
I don't know why but the first reproducible example works fine also with this mistake (and without -lmingw32), so it pointed me out of the correct way.
I did this change on makefile and now it works:
CFLAGS = -IC:/sdl2_mingw/i686-w64-mingw32/include/SDL2 -IC:/sdl2_image_mingw/i686-w64-mingw32/include/SDL2 -O0 -ggdb
LIBS = -lmingw32 -LC:/sdl2_mingw/i686-w64-mingw32/lib -LC:/sdl2_image_mingw/i686-w64-mingw32/lib -lSDL2main -lSDL2 -lSDL2_image
as a continuation to this post C pluginsystem: symbol lookup error, I am still writing my plugin system and encounter new bugs.
To recap what the plugins are, the program consists of a network application interfaced by a shell, messages has a type and therefore can be use to create applications on the network. For exemple, a possible application would be a chat or a transfert application.
So shell commands can send message of a particular application on the network, when a message is received, if it corresponds to a particular application then an action function is executed with the message content as argument, it could be the application.
A plugin is a shared library with an init function that register it's commands and actions. A command could just be a simple command that doesn't interact with the network, and that's why I achieved at the moment.
The plugin system consists in modules:
plugin_system.c
list.c used by the first module to store plugins
The network part consists in:
protocol.c main part of the protocol
message.c main part for message treatment
application.c main part used to program applications
common.c file with ccommon functions
network.c useful network functions
The modules in protocole are all interdependent, I have split files for conveniency.
All modules are compiled with -fPIC option.
To compile a plugin called plug.c wich doesn't interact with the network, I use:
gcc -Wall -O2 -std=gnu99 -D DEBUG -g -fPIC -c -o plug.o plug.c
gcc -Wall -O2 -std=gnu99 -D DEBUG -g -o plug.so plug.o plugin_system.o list.o -shared
And it works perfectly, the library is loaded with dlopen with no problem, the init function loaded with dlsym and executed correctly so the plugin is registered, I then executed the command and I can see that it work.
Now I wan't to add supports for network communications for the plugins, so I have modified the same test plugin that I used which has just a command to print a message. I have had a call to sendappmessage_all a function that send a message to everyone over the network, defined in message.c.
I can compile the new plugin without adding the network module objects, it compile, the plugin loads correctly, but when it call sendappmessage_all obviously it fails with the message
symbol lookup error: ./plugins/zyva.so: undefined symbol: sendappmessage_all
So to make it work, I should like the plugin with network modules so that's what I have done with
gcc -Wall -O2 -std=gnu99 -D DEBUG -g -o plug.so plug.o plugin_system.o list.o protocol.o message.o thread.o common.o application.o network.o -shared
It compile but when I try to load the plugin, dlopen return NULL.
I have also tried to add just one module, at worst it would only result in an undefined symbol error, but I dlopen still return NULL.
I know it's a lot of informations and on the otherside you probably wan't to see the code but I tried to be the clearer in the most succint way I could be because is way more complex and bigger than the post.
Thank you for your understanding.
The problem is that when you compile the plugin system (i.e. functions called by plugins), and link it to the final executable, the linker does not export the symbols used by the plugins in the dynamic symbol table.
There are two options:
Use -rdynamic when linking the final executable, adding all symbols to the dynamic symbol table.
Use -Wl,-dynamic-list,plugin-system.list when linking the final executable, adding symbols listed in file plugin-system.list to the dynamic symbol table.
The file format is simple:
{
sendappmessage_all;
plugin_*;
};
In other words, you can list either each symbol name (function or data structure), or a glob pattern that matches the desired symbol names. Remember the semicolon after each symbol, and after the closing brace, or you'll get a "syntax error in dynamic list" error at link time.
Note that just marking a function "used" via __attribute__((used)) is not sufficient to make the linker include it in the dynamic symbol table (with GCC 4.8.4 and GNU ld 2.24, at least).
Since the OP thinks what I wrote above is incorrect, here is a fully verifiable proof of the above.
First, a simple main.c that loads plugin files named on the command line, and executes their const char *register_plugin(void); function. Because the function name is shared across all plugins, we need to link them locally (RTLD_LOCAL).
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <stdio.h>
static const char *load_plugin(const char *pathname)
{
const char *errmsg;
void *handle; /* We deliberately leak the handle */
const char * (*initfunc)(void);
if (!pathname || !*pathname)
return "No path specified";
dlerror();
handle = dlopen(pathname, RTLD_NOW | RTLD_LOCAL);
errmsg = dlerror();
if (errmsg)
return errmsg;
initfunc = dlsym(handle, "register_plugin");
errmsg = dlerror();
if (errmsg)
return errmsg;
return initfunc();
}
int main(int argc, char *argv[])
{
const char *errmsg;
int arg;
if (argc < 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s plugin [ plugin ... ]\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
for (arg = 1; arg < argc; arg++) {
errmsg = load_plugin(argv[arg]);
if (errmsg) {
fflush(stdout);
fprintf(stderr, "%s: %s.\n", argv[arg], errmsg);
return EXIT_FAILURE;
}
}
fflush(stdout);
fprintf(stderr, "All plugins loaded successfully.\n");
return EXIT_SUCCESS;
}
The plugins will have access via certain functions (and/or variables), declared in plugin_system.h:
#ifndef PLUGIN_SYSTEM_H
#define PLUGIN_SYSTEM_H
extern void plugin_message(const char *);
#endif /* PLUGIN_SYSTEM_H */
They are implemented in plugin_system.c:
#include <stdio.h>
void plugin_message(const char *msg)
{
fputs(msg, stderr);
}
and listed as dynamic symbols in plugin_system.list:
{
plugin_message;
};
We'll also need a plugin, plugin_foo.c:
#include <stdlib.h>
#include "plugin_system.h"
const char *register_plugin(void) __attribute__((used));
const char *register_plugin(void)
{
plugin_message("Plugin 'foo' is here.\n");
return NULL;
}
and just to remove any confusion about what effect there is having each plugin a registration function by the same name, another plugin named plugin_bar.c:
#include <stdlib.h>
#include "plugin_system.h"
const char *register_plugin(void) __attribute__((used));
const char *register_plugin(void)
{
plugin_message("Plugin 'bar' is here.\n");
return NULL;
}
To make all of this easy to compile, we'll need a Makefile:
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS := -ldl -Wl,-dynamic-list,plugin_system.list
PLUGIN_CFLAGS := $(CFLAGS)
PLUGIN_LDFLAGS := -fPIC
PLUGINS := plugin_foo.so plugin_bar.so
PROGS := example
.phony: all clean progs plugins
all: clean progs plugins
clean:
rm -f *.o $(PLUGINS) $(PROGS)
%.so: %.c
$(CC) $(PLUGIN_CFLAGS) $^ $(PLUGIN_LDFLAGS) -shared -Wl,-soname,$# -o $#
%.o: %.c
$(CC) $(CFLAGS) -c $^
plugins: $(PLUGINS)
progs: $(PROGS)
example: main.o plugin_system.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $#
Note that Makefiles require intendation by tabs, not spaces; listing the file here always converts them to spaces. So, if you paste the above to a file, you'll need to fix the indentation, via e.g.
sed -e 's|^ *|\t|' -i Makefile
It is safe to run that more than once; the worst it can do, is mess up your "human-readable" layout.
Compile the above using e.g.
make
and run it via e.g.
./example ./plugin_bar.so ./plugin_foo.so
which shall output
Plugin 'bar' is here.
Plugin 'foo' is here.
All plugins loaded successfully.
to standard error.
Personally, I prefer to register my plugins via a structure, with a version number, and at least one function pointer (to the initialization function). This lets me load all plugins before initializing them, and resolve e.g. interplugin conflicts or dependencies. (In other words, I use a structure with a fixed name, rather than a function with a fixed name, to identify plugins.)
Now, as to __attribute__((used)). If you modify plugin_system.c into
#include <stdio.h>
void plugin_message(const char *msg) __attribute__((used));
void plugin_message(const char *msg)
{
fputs(msg, stderr);
}
and modify the Makefile to have LDFLAGS := -ldl only, the example program and plugins will compile just fine, but running it will yield
./plugin_bar.so: ./plugin_bar.so: undefined symbol: plugin_message.
In other words, if the API exported to plugins is compiled in a separate compilation unit, you will need to use either -rdynamic or -Wl,-dynamic-list,plugin-system.list to ensure the functions are included in the dynamic symbol table in the final executable; the used attribute does not suffice.
If you want all and only non-static functions and symbols in plugin_system.o included in dynamic symbol table in the final binary, you can e.g. modify the end of the Makefile into
example: main.o plugin_system.o
#rm -f plugin_system.list
./list_globals.sh plugin_system.o > plugin_system.list
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $#
with list_globals.sh:
#!/bin/sh
[ $# -ge 1 ] || exit 0
export LANG=C LC_ALL=C
IFS=:
IFS="$(printf '\t ')"
printf '{\n'
readelf -s "$#" | while read Num Value Size Type Bind Vis Ndx Name Dummy ; do
[ -n "$Name" ] || continue
if [ "$Bind:$Type" = "GLOBAL:FUNC" ]; then
printf ' %s;\n' "$Name"
elif [ "$Bind:$Type:$Ndx" = "GLOBAL:OBJECT:COM" ]; then
printf ' %s;\n' "$Name"
fi
done
printf '};\n'
Remember to make the script executable, chmod u+x list_globals.sh.
I want to access the global variable of executable in shared library? I have tried to compile using option -export-dynamic but no luck.
I have tried with extern key word. this also not working.
Any help or suggestion would be appreciable.
Environment c - Linux
executable:-
tst.c
int tstVar = 5;
void main(){
funInso();
printf("tstVar %d", tstVar);
}
lib:-
tstLib.c
extern int tstVar;
void funInso(){
tstVar = 50;
}
Since my code is very big, I just gave the sample which I have used in my program.
It should work. BTW, your tst.cis lacking a #include <stdio.h>. And its main should return an ìnt and end with e.g. return 0;.
With
/* file tst.c */
#include <stdio.h>
int tstVar = 5;
extern void funInso(void);
int main(){
funInso();
printf("tstVar %d\n", tstVar);
return 0;
}
and
/* file tstlib.c */
extern int tstVar;
void funInso(){
tstVar = 50;
}
I compiled with gcc -Wall -c tst.c the first file, I compiled with gcc -Wall -c tstlib.c the second file. I made it a library with
ar r libtst.a tstlib.o
ranlib libtst.a
Then I linked the first file to the library with gcc -Wall tst.o -L. -ltst -o tst
The common practice is to have with your library a header file tstlib.h which would contain e.g.
#ifndef TSTLIB_H_
#define TSTLIB_H_
/* a useful explanation about tstVar. */
extern int tstVar;
/* the role of funInso. */
extern void funInso(void);
#endif /*TSTLIB_H */
and have both tst.c and tstlib.c contain an #include "tstlib.h"
If the library is shared, you should
compile the library file in position independent code mode
gcc -Wall -fpic -c tstlib.c -o tstlib.pic.o
link the library with -shared
gcc -shared tstlib.pic.o -o libtst.so
Note that you can link a shared object with other libraries. You could have appended -lgdbm to that command, if your tstlib.c is e.g. calling gdbm_open hence including <gdbm.h>. This is one of the many features shared libraries give you that static libraries don't.
link the executable with -rdynamic
gcc -rdynamic tst.o -L. -ltst -o tst
Please take time to read the Program Library Howto
your tstVar variable could be defined in the lib. and you can share this variable via functions:
setFunction: to edit this variable
void setFunction (int v)
{
tstVar = v;
}
getFunction: to return the variable
int getFunction ()
{
return tstVar
}
In libname.h:
int add_libname(int, int);
In libname.c:
#include "libname.h"
int add_libname(int a, int b)
{
return a+b;
}
I can build the shared library this way:
gcc -shared -fPIC libname.c -o libname.so
But I can't use it in another programe test.c:
#include <stdio.h>
#include "libname.h"
int main(int argc, char* argv[])
{
printf("%d\n", add_libname(1,5));
}
Reporting undefined reference to add_libname when I try to build it..
What's wrong here?
Because add_libname takes (int, int) you're giving it (1+5 = 6) or just (int)
I think you meant
add_libname(1, 5);
Also to compile it correctly you must use gcc like so
gcc -o myapp test.c -L. -lname
the lib part of libname is ignored as it is implicit
To create a shared library use these
gcc -fPIC -c libname.c
it gives warning: position independent code and libname.o file is generated.
and now type these command,
gcc -shared libname.so libname.o
libname.so ( the shared library is created with .so extension). To use the shared library
gcc -I/give the path of libname.h sourcefile.c /give the path of your .so file
example if your c file is file.c and the header file libname.h is in c:\folder1\project and your libname.so (shared library) is in c:\folder\project2
then
gcc -I/cygdrive/c/folder1/project file.c /cygdrive/c/folder/project/libname.so
this is the gcc command to be used while using the shared library.
Thank you.