Difference of usage between shared and static library - c++

I have a very simple sample of code :
#include<iostream>
#include "libMPSSE_spi.h"
int main() {
uint32 channels = 0;
std::cout << "erreur = " << SPI_GetNumChannels(&channels) << std::endl;
std::cout << "Number of available SPI channels = " << (int)channels << std::endl;
return 0;
}
That works fine when I link the static libMPSSE, but has an issue with the shared library.
My two commands to build the file are :
g++ test.cpp -o test.o -I../../libs/MPSSE -I../../libs/FTDI/linux -L../../libs/MPSSE/linux/x64 -lMPSSE
g++ test.cpp -o test.o -I../../libs/MPSSE -I../../libs/FTDI/linux ../../libs/MPSSE/linux/x64/libMPSSE.a -ldl
Both compilation works, but the execution output is not the same. With static linking the library works fine, and with dynamic linking it returns "other error".
Note : I have built both library myself with the provided makefile (object are built before the same way for both libraries):
libMPSSE: $(OBJECTS)
$(CC) -o libMPSSE.so -shared $(OBJECTS) -L /MinGW/lib -ldl
$(AR) rcs libMPSSE.a $(OBJECTS)
What could explain the different behavior of both libraries?
Note : in the example they provide with their library, they use the shared library with dlopen but if I have the library and header at link time, I shouldnt have to do that?
Note2 : they both use libftd2xx.so and I use this command to run the executable (dynamic and static)
LD_LIBRARY_PATH=~/WS/Qt/main/LC4/main/libs/FTDI/linux/x64:~/WS/Qt/main/LC4/main/libs/MPSSE/linux/x64 ./test.o
LD_LIBRARY_PATH=~/WS/Qt/main/LC4/main/libs/FTDI/linux/x64 ./test.o
Edit : code that work with shared library (from their example)
#include <iostream>
#include "libMPSSE_spi.h"
#include <dlfcn.h>
int main() {
typedef FT_STATUS (*pfunc_SPI_GetNumChannels)(uint32 *numChannels);
pfunc_SPI_GetNumChannels p_SPI_GetNumChannels;
void *h_libMPSSE;
h_libMPSSE = dlopen("../../libs/MPSSE/linux/x64/libMPSSE.so",RTLD_LAZY);
if(!h_libMPSSE)
{
std::cout <<"Failed loading libMPSSE.so. Please check if the file exists in ";
std::cout << "the shared library folder(/usr/lib or /usr/lib64)" << std::endl;
return 1;
}
p_SPI_GetNumChannels = (pfunc_SPI_GetNumChannels)dlsym(h_libMPSSE, "SPI_GetNumChannels");
uint32 channels = 0;
std::cout << "erreur = " << p_SPI_GetNumChannels(&channels) << std::endl;
std::cout << "Number of available SPI channels = " << (int)channels << std::endl;
return 0;
}

As your program is loaded and initialized the operating system searches for the required shared libraries in the appropriate directories (e.g. /usr/lib or %WINDIR%\system32)*.
If that library is not present in those directories the application will not run, as the dependency isn't met. Your example probably advises you to use dlopen to load the shared library from the same directory your application is in - you might no be able to copy your shared library into the default paths without administrator rights.
* usually you can add more paths to be searched but those are the default ones

Related

Why does an std::any_cast of a passed std::any inside a dlopen'd function raise an error

I am toying around with c++17 and plugins, and I have run into an error that I cannot get around. In the following MWE I can call a local function that takes a std::any, and everything works as expected when I try to read the contents. When I load this exact same function through a plugin (dlopen), it correctly sees the type on the any, but it cannot std::any_cast the contents.
Any help would be greatly appreciated in figuring out what is causing this error.
Here is my environment, MWE, and resulting error.
>> g++ --version
g++ (GCC) 7.1.1 20170526 (Red Hat 7.1.1-2)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>> scons --version
SCons by Steven Knight et al.:
script: v2.5.1.rel_2.5.1:3735:9dc6cee5c168[MODIFIED], 2016/11/03 14:02:02, by bdbaddog on mongodog
engine: v2.5.1.rel_2.5.1:3735:9dc6cee5c168[MODIFIED], 2016/11/03 14:02:02, by bdbaddog on mongodog
engine path: ['/usr/lib/scons/SCons']
Copyright (c) 2001 - 2016 The SCons Foundation
>> tree
.
├── SConstruct
└── src
├── main.cpp
├── plugin.cpp
└── SConscript
1 directory, 4 files
>> cat SConstruct
SConscript('src/SConscript', variant_dir='build', duplicate=0)
>> cat src/SConscript
env = Environment()
env.Append(CXXFLAGS=['-std=c++17'])
plugin = env.SharedLibrary('plugin', 'plugin.cpp')
Install('../lib', plugin)
driver_env = env.Clone()
driver_env.Append(LIBS=['dl', 'stdc++fs'])
driver = driver_env.Program('driver', 'main.cpp')
Install('../bin', driver)
>> cat src/plugin.cpp
#include <any>
#include <iostream>
using namespace std;
extern "C" {
int plugin(any& context) {
cout << " Inside Plugin" << endl;
cout << " Has Value? " << context.has_value() << endl;
cout << " Type Name: " << context.type().name() << endl;
cout << " Value: " << any_cast<double>(context) << endl;
}
}
>> cat src/main.cpp
#include <functional>
#include <iostream>
#include <stdexcept>
#include <any>
#include <experimental/filesystem>
#include <dlfcn.h>
using namespace std;
using namespace std::experimental;
function< void(any&) > loadplugin(string filename) {
function< void(any&) > plugin;
filesystem::path library_path(filename);
filesystem::path library_abspath = canonical(library_path);
void * libraryHandle = dlopen(library_abspath.c_str(), RTLD_NOW);
if (!libraryHandle) {
throw runtime_error("ERROR: Could not load the library");
}
plugin = (int(*) (any&))dlsym(libraryHandle, "plugin");
if (!plugin) {
throw runtime_error("ERROR: Could not load the plugin");
}
return plugin;
}
int local(any& context) {
cout << " Inside Local" << endl;
cout << " Has Value? " << context.has_value() << endl;
cout << " Type Name: " << context.type().name() << endl;
cout << " Value: " << any_cast<double>(context) << endl;
}
int main(int argc, char* argv[]) {
cout << " Resolving Paths..." << endl;
filesystem::path full_path = filesystem::system_complete( argv[0] ).parent_path();
filesystem::path plugin_path(full_path/".."/"lib"/"libplugin.so");
cout << " Creating Context..." << endl;
any context(.1);
cout << " Loading Plugin..." << endl;
function<void(any&) > plugin = loadplugin(plugin_path.string());
cout << " Calling Local..." << endl;
local(context);
cout << " Calling Plugin..." << endl;
plugin(context);
}
>> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/main.o -c -std=c++17 src/main.cpp
g++ -o build/driver build/main.o -ldl -lstdc++fs
Install file: "build/driver" as "bin/driver"
g++ -o build/plugin.os -c -std=c++17 -fPIC src/plugin.cpp
g++ -o build/libplugin.so -shared build/plugin.os
Install file: "build/libplugin.so" as "lib/libplugin.so"
scons: done building targets.
>> tree
.
├── bin
│   └── driver
├── build
│   ├── driver
│   ├── libplugin.so
│   ├── main.o
│   └── plugin.os
├── lib
│   └── libplugin.so
├── SConstruct
└── src
├── main.cpp
├── plugin.cpp
└── SConscript
4 directories, 10 files
>> bin/driver
Resolving Paths...
Creating Context...
Loading Plugin...
Calling Local...
Inside Local
Has Value? 1
Type Name: d
Value: 0.1
Calling Plugin...
Inside Plugin
Has Value? 1
Type Name: d
terminate called after throwing an instance of 'std::bad_any_cast'
what(): bad any_cast
Value: Aborted (core dumped)
libstdc++'s any relies on the address of the same template instantiation being the same inside a program, and that means you need to take precautions if you are using dlopen:
The compiler has to emit [objects with vague linkage, like template
instantiations] in any translation unit that requires their presence,
and then rely on the linking and loading process to make sure that
only one of them is active in the final executable. With static
linking all of these symbols are resolved at link time, but with
dynamic linking, further resolution occurs at load time. You have to
ensure that objects within a shared library are resolved against
objects in the executable and other shared libraries.
For a program which is linked against a shared library, no additional precautions are needed.
You cannot create a shared library with the -Bsymbolic option, as that prevents the resolution described above.
If you use dlopen to explicitly load code from a shared library, you must do several things. First, export global symbols from the
executable by linking it with the -E flag (you will have to specify
this as -Wl,-E if you are invoking the linker in the usual manner
from the compiler driver, g++). You must also make the external
symbols in the loaded library available for subsequent libraries by
providing the RTLD_GLOBAL flag to dlopen. The symbol resolution
can be immediate or lazy.

Undefined reference to (error) in C++ Eclipse but working in Visual Studio 2015

I am trying to integrate AMPL with C/C++ using AMPL-API on Windows-7 in Eclipse Mars 2.0. I created a Makefile project in Eclipse which uses MinGW CC to compile the firstexample code given in their example directory.
firstexample.cpp:
#include <iostream>
#include "ampl/ampl.h"
using namespace std;
int main() {
ampl::AMPL ampl;
// Read the model and data files.
std::string modelDirectory = "models";
ampl.read(modelDirectory + "/diet/diet.mod");
ampl.readData(modelDirectory + "/diet/diet.dat");
// Solve
ampl.solve();
// Get objective entity by AMPL name
ampl::Objective totalcost = ampl.getObjective("total_cost");
// Print it
std::cout << "Objective is: " << totalcost.value() << std::endl;
// Get objective entity by AMPL name
ampl::Objective totalcost = ampl.getObjective("total_cost");
// Print it
std::cout << "Objective is: " << totalcost.value() << std::endl;
// Reassign data - specific instances
ampl::Parameter cost = ampl.getParameter("cost");
cost.setValues(new Tuple[2]{ ampl::Arg("BEEF"), ampl::Arg("HAM")}, new Arg[2]{ 5.01, 4.55 },
2);
std::cout << "Increased costs of beef and ham." << std::endl;
// Resolve and display objective
ampl.solve();
std::cout << "New objective value: " << totalcost.value() << std::endl;
// Reassign data - all instances
ampl::Arg elements[8]{ 3, 5, 5, 6, 1, 2, 5.01, 4.55 };
cost.setValues(elements);
std::cout << "Updated all costs." << std::endl;
// Resolve and display objective
ampl.solve();
std::cout << "New objective value: " << totalcost.value() << std::endl;
// Get the values of the variable Buy in a dataframe object
Variable buy = ampl.getVariable("Buy");
ampl::DataFrame df;
df = buy.getValues();
// Print them
df.print();
ampl::DataFrame df2;
// Get the values of an expression into a DataFrame object
df2 = ampl.getData("{j in FOOD} 100*Buy[j]/Buy[j].ub");
// Print them
df2.print();
}
Following is my Makefile:
CC = g++
CFLAGS = -O2 -g -Wall -fmessage-length=0
INCLUDES = -I "C:\\Local\\AMPL\\AMPL32\\amplapi32\\include"
OBJS = AMPL.o
LFLAGS = -L "C:\\Local\\AMPL\\AMPL32\\amplapi32\\lib"
LIBS = -lampl1.2.2
TARGET = AMPL.exe
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $(TARGET) $(OBJS) $(LFLAGS) $(LIBS)
AMPL.o: AMPL.cpp
$(CC) $(CFLAGS) $(INCLUDES) -c AMPL.cpp
all: $(TARGET)
clean:
rm -f $(OBJS) $(TARGET)
I have added path of required dll files (libampl1.2.2.dll) to the environment variables. I am able to compile and execute code on Visual Studio 2015 with two minor changes:
Without using Makefile (It is a Win32 Console Application)
Adding #include "stdafx.h" in firstexample.cc
However when I execute the same code in Eclipse, it gives me following error:
src\AMPLTesting.o: In function `ZN4ampl8internal11deleteTupleERNS0_5TupleE':
C:/Local/AMPL/AMPL32/amplapi32/include/ampl/ep/tuple_ep.h:24: undefined reference to `_imp___ZN4ampl8internal24AMPL_Variant_DeleteArrayEPKNS0_7VariantE'
src\AMPLTesting.o: In function `ZN4ampl8internal12TupleBuilderC1Ej':
C:/Local/AMPL/AMPL32/amplapi32/include/ampl/ep/tuple_ep.h:35: undefined reference to `_imp___ZN4ampl8internal24AMPL_Variant_CreateArrayEjPNS0_16ErrorInformationE'
collect2.exe: error: ld returned 1 exit status
I am not sure what is the problem? Am I missing some command line option in the Makefile or not adding any specific library? Please help me with this.
The beta version of the C++ API only supports MSVC on Windows at the moment. Support for other compilers will be added in future releases.

C++ / R: RInside in Windows 7 machine

This question is related to: C++ and R: Create a .so or .dll plus i have read the questions and replies of these posts:
Compiling RInside programs on Windows
Problem with compiling RInside examples under Windows
I try to run the code provided as an example in the answer provided
#include <RInside.h> // for the embedded R via RInside
#include <iomanip>
int main(int argc, char *argv[]) {
RInside R(argc, argv); // create an embedded R instance
std::string txt = // load library, run regression, create summary
"suppressMessages(require(stats));"
"swisssum <- summary(lm(Fertility ~ . , data = swiss));"
"print(swisssum)";
R.parseEvalQ(txt); // eval command, no return
// evaluate R expressions, and assign directly into Rcpp types
Rcpp::NumericMatrix M( (SEXP) R.parseEval("swcoef <- coef(swisssum)"));
Rcpp::StringVector cnames( (SEXP) R.parseEval("colnames(swcoef)"));
Rcpp::StringVector rnames( (SEXP) R.parseEval("rownames(swcoef)"));
std::cout << "\n\nAnd now from C++\n\n\t\t\t";
for (int i=0; i<cnames.size(); i++) {
std::cout << std::setw(11) << cnames[i] << "\t";
}
std::cout << std::endl;
for (int i=0; i<rnames.size(); i++) {
std::cout << std::setw(16) << rnames[i] << "\t";
for (int j=0; j<cnames.size(); j++) {
std::cout << std::setw(11) << M(i,j) << "\t";
}
std::cout << std::endl;
}
std::cout << std::endl;
exit(0);
}
The error in the CMD is the following
C:\Users\DON\Desktop>R CMD SHLIB final.cpp
g++ -m64 -I"C:/R/R-3.2.4/include" -DNDEBUG -I"d:/RCompile/r-compiling/local/
local323/include" -O2 -Wall -mtune=core2 -c final.cpp -o final.o
final.cpp:1:74: fatal error: RInside.h: No such file or directory
compilation terminated.
make: *** [final.o] Error 1
Warning message:
comando ejecutado 'make -f "C:/R/R-3.2.4/etc/x64/Makeconf" -f "C:/R/R-3.2.4/shar
e/make/winshlib.mk" SHLIB_LDFLAGS='$(SHLIB_CXXLDFLAGS)' SHLIB_LD='$(SHLIB_CXXLD)
' SHLIB="final.dll" WIN=64 TCLBIN=64 OBJECTS="final.o"' tiene estatus 2
Clearly it cant find the RInside.h header. I have the R installed in a folder without spaces. The PATH in global variables have: C:\R\R-3.2.4\bin; C:\Rtools\bin;C:\Rtools\gcc-4.6.3\bin
I understand that in the CMD i cant introduce comands like
$ export PKG_LIBS=‘Rscript -e "Rcpp:::LdFlags()"‘ # if Rcpp older than 0.11.0
$ export PKG_CXXFLAGS=‘Rscript -e "Rcpp:::CxxFlags()"‘
Which first defines and exports two relevant environment variables which R CMD SHLIB then relies on (as put in the FAQ file)
Any advice on this? I need to do a Makefile for each cpp file that i want to compile?
The error is in your approach. You did
R CMD SHLIB final.cpp
which is nowhere given as the correct approach for working with RInside.
Because we need to tell R about headers and libraries for several components, you are supposed to
cd inst/examples/standard
make # build all
or
make rinside_sample3 # build just this
or, if you're on that OS,
make -f Makefile.win # all
or
make -f Makefile.win rinside_sample3
as the Makefile tells R where do find this. That also answers your second question: One Makefile per directory will do. And look at the Makefile: it sets several include directives; your approach only dealt with Rcpp so of course you get an error about RInside.h not found.
I think you keep asking the same question over and over.

Loading two instances of a shared library without RTLD_PRIVATE or multiple copies

In C++ I need to run multiple instances of legacy FORTRAN libraries (over which I have no control) which use COMMON BLOCK variables. I can successfully do this following the prescription of Question 3433522, where I create 2 physical copies of the library, and load these at run-time using dlopen (see example code below).
However, I would like avoid having to create two physical copies of the library, as it can be rather large. As suggested in Question 3433522, using RTLD_PRIVATE would work, but this is not available (as far as I can tell) in libgc with GCC. I've also investigated using RTLD_DEEPBIND rather than RTLD_LOCAL but this makes no difference. Any idea how this could be accomplished using GCC?
My example code is as follows:
Makefile
libgen.so: gen.f
gfortran -Wall -shared -fPIC -o $# $<
libgen%.so: libgen.so
cp $< $#
run: run.cpp libgen1.so libgen2.so
g++ -Wall -o $# $< -L. -ldl
gen.f
C*********************************************************************
C...GENDATA
C...Common block of data.
BLOCK DATA
INTEGER NEVENT
COMMON/GENDATA/NEVENT
DATA NEVENT/0/
END
C*********************************************************************
C...EVENT
C...Simple example routine that produces an "event".
SUBROUTINE GENERATE()
INTEGER NEVENT
COMMON/GENDATA/NEVENT
NEVENT = NEVENT + 1
PRINT *, NEVENT
RETURN
END
run.cpp
#include <iostream>
#include <dlfcn.h>
using namespace std;
//==========================================================================
// Methods to load and close dynamic libraries.
//--------------------------------------------------------------------------
// Load a symbol from a library.
typedef void (*Symbol)();
Symbol libSym(void *&lib, string libName, string symName) {
Symbol sym(0);
const char* error(0);
// Load the library, if not loaded.
if (!lib) {
lib = dlopen(libName.c_str(), RTLD_NOW | RTLD_LOCAL);
error = dlerror();
}
if (error) {
cerr << "Error from libSym: " + string(error) + "\n";
return sym;
}
dlerror();
// Load the symbol.
sym = (Symbol)dlsym(lib, symName.c_str());
error = dlerror();
if (error) cerr << "Error from libSym: " + string(error) + "\n";
dlerror();
return sym;
}
//--------------------------------------------------------------------------
// Close a library.
void libClose(void *&lib) {
if (lib) {dlclose(lib); dlerror();}
}
//==========================================================================
// The main program.
//--------------------------------------------------------------------------
int main() {
// Load libraries.
void *libgen1(0), *libgen2(0);
cout << "Loading library 1.\n";
void (*generate1)();
generate1 = libSym(libgen1, "./libgen1.so", "generate_");
if (!libgen1) return 1;
cout << "Loading library 2.\n";
void (*generate2)();
generate2 = libSym(libgen2, "./libgen2.so", "generate_");
if (!libgen2) return 1;
// Generate events.
cout << "Generating 1.\n";
(*generate1)();
cout << "Generating 1.\n";
(*generate1)();
cout << "Generating 2.\n";
(*generate2)();
// Close the libraries.
libClose(libgen1);
libClose(libgen2);
return 0;
}

Linker error when compiling a program that uses spidermonkey

I've been trying to learn spidermonkey and so have written the following code, adapted from this guide and while the program compiles properly, I get the following error during linking:
/usr/bin/ld: cannot open linker script file symverscript: No such file or directory
I'm using 64-bit Ubuntu 13.10, and here is the code (seems irrelevant to the problem, but can't hurt)
#include <jsapi.h>
#include <iostream>
#include <string>
int main()
{
std::string script = "var x = 10;x*x;";
jsval rval;
JSRuntime* runtime = 0;
JSContext* context = 0;
JSObject* globalob = 0;
if((!(runtime = JS_NewRuntime(1024L*1024L, JS_NO_HELPER_THREADS)))||
(!(context = JS_NewContext(runtime, 8192)))||
(!(globalob = JS_NewObject(context, NULL, NULL, NULL))))
{
return 1;
}
if(!JS_InitStandardClasses(context, globalob))
{
return 1;
}
if(!JS_EvaluateScript(context,globalob,script.data(),script.length(),"script",1,&rval))
{
return 1;
}
std::cout << JSVAL_TO_INT(rval) << "\n";
JS_DestroyContext(context);
JS_DestroyRuntime(runtime);
JS_ShutDown();
return 0;
}
compiled with the command
g++ main.cpp -o out $(js24-config --cflags --libs | tr "\n" " ")
Try to write this command instead,
g++ main.cpp -o main -I/usr/local/include/js/ -L/usr/local/lib/ -lmozjs1.8.5
regarding the path I wrote above, you must write your own path which include the library and JSAPI.h file included in,
And the last term is spidermonkey library, you will find it in lib folder, for me it exists in /usr/local/lib