Detect if I'm building Executable or Dynamic Library? - c++

I have two versions of my app, one works as a dynamic library loaded from a host app and another one works as a standalone executable app.
In my code, there are parts where I need to use different code depending on which version I'm building.
Is there a way to detect if I'm building a dynamic library or a standalone executable app so I can use the same source files for the two versions?
I would appreciate a cross-platform solution.
ADDED:
My dynamic lib or executable code:
#include <iostream>
int main(int argc, const char * argv[])
{
#ifdef IS_DYNAMICLIB
std::cout << "Compiled as a dynamic library!\n";
#else
std::cout << "Compiled as an executable!\n";
#endif
return 0;
}
In the terminal on macOS and I build it using the command:
g++ -dynamiclib -undefined suppress -flat_namespace main.cpp -o Test.dylib
Now, I have Test.dylib
Host app code:
#include <iostream>
#include <dlfcn.h>
int main(int argc, const char * argv[])
{
void* handle;
typedef void (*func_t)();
handle = dlopen("/Path/to/Test.dylib", RTLD_LAZY);
if (!handle) {
printf("failed to open the library\n");
return 0;
}
func_t mainFunc = (func_t) dlsym(handle, "main");
if (!mainFunc) {
printf("failed to find main method\n");
dlclose(handle);
return 0;
}
mainFunc();
return 0;
}
When I build and run this, I get:
Compiled as an executable!
Program ended with exit code: 0
But I would like it to print:
Compiled as a dynamic library!
Program ended with exit code: 0
How can I do this?

Related

linux + visual studio 2013 + visualgdb, undefined reference to 'dlopen'

#include <iostream>
#include <dlfcn.h>
using namespace std;
bool LoadEESQuote()
{
void* m_handle;
m_handle = dlopen("libEESQuoteApi.so", RTLD_LAZY);
return true;
}
int main(int argc, char *argv[])
{
LoadEESQuote();
return 0;
}
it is said
need -ldl to compile
, so I set:
project properties-Configuration Properties-NMake-Additional Options: -lrt -ldl
but I still get error.
Please help me
Thanks to n.m.
VisualGDB Project Properties- Makefile settings -Additional linker inputs: -ldl
and it is worked out

How to correctly link C++ objects using GLFW functions in Linux?

I've created a very simple program containing multiple files and I'm now trying to link the GLFW library to it. I've only added GLFW functions to the file main.cpp and only include the library there, and only compiling and executing this file using the command g++ main.cpp -lglfw goes fine.
Before I added this library, compiling and linking the entire program also went fine, but even though there are no GLFW functions used in the other files when I want to link everything together (g++ -Wall -g -std=c++11 -lglfw main.o hello_world.o console.o) I suddenly get the error 'undefined reference to' every GLFW function I used. (I got no error while compiling main.cpp: g++ -Wall -g -std=c++11 -lglfw -c main.cpp)
This is the file main.cpp:
#include "basis.h"
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
// Open-GL
#include <GLFW/glfw3.h>
/// Setup:
/// sudo apt-get update
/// sudo apt-get install libglfw3
/// sudo apt-get install libglfw3-dev
/// Compile:
/// g++ main.cpp -lglfw
using namespace std;
void errorCallback(int error, const char* description) {
fprintf(stderr, "Error: %s\n", description);
}
void test() {
GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
if (!window) {
cout << "Window creation failed!" << endl;
}
runConsole();
}
int main(int argc, char* argv[]) {
glfwSetErrorCallback(errorCallback);
if (!glfwInit()) {
cout << "GLFW initialization failed!" << endl;
exit(EXIT_FAILURE);
}
test();
glfwTerminate();
return 0;
}
Furthermore, I do not know where the library is located on my machine. Also, the Linux machine is Virtual Box, and the source code is in a shared folder located on my host windows system, but this his never created problems with libraries before as I compile them on Linux.
Oops, while re-reading the question I thought of a new solution, and it worked :) The correct linker command is: g++ -Wall -g -std=c++11 main.o hello_world.o console.o -lglfw

File too big compiling on Cygwin G++

I'm specifically building a test program to work on Chaiscript, which is how I encountered this issue:
chai.cpp:
#include <cstdio>
#include <iostream>
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_stdlib.hpp>
std::string helloWorld(const std::string &t_name)
{
return "Hello " + t_name + "!";
}
int main(int argc, char** argv, char** env) {
chaiscript::ChaiScript chai;
chai.add(chaiscript::fun(&helloWorld), "helloWorld");
chai.eval("puts(helloWorld(\"Bob\"));");
return 0L;
}
/usr/lib/gcc/i686-pc-cygwin/5.4.0/../../../../i686-pc-cygwin/bin/as: CMakeFiles/chai.dir/src/chai.cpp.o: too many sections (37830)
/tmp/ccqGbeku.s: Assembler messages:
/tmp/ccqGbeku.s: Fatal error: can't write CMakeFiles/chai.dir/src/chai.cpp.o: File too big
/usr/lib/gcc/i686-pc-cygwin/5.4.0/../../../../i686-pc-cygwin/bin/as: CMakeFiles/chai.dir/src/chai.cpp.o: too many sections (37830)
This issue doesn't appear when I build on Mac or Linux.
I discovered a workaround to this issue from the Chaiscript CMakeLists.txt:
if(MINGW OR CYGWIN)
add_definitions(-O3)
endif()
Other searches on the Internet imply this big-object problem is linked the Windows executable format, and is not likely to be addressed in G++. Using MingW32 did not address this error in my case - I'm not going to 64-bit.
Object file has too many sections

Calling function in shared library (Linux) get Segmentation Fault

I was trying to write a basic example of shared library opening and function calling for practice, but it turns out that I always get "segmentation fault" when the exectuable is actually running. Here are the source code:
main.cpp:
#include<iostream>
#include<dlfcn.h>
using namespace std;
typedef void (*API)(unsigned int);
int main(int argc,char** argv){
void* dl;
API api;
unsigned int tmp;
//...
dl=dlopen("pluginA.so",RTLD_LAZY);
api=(API)dlsym(dl,"API");
cin>>tmp;
(*api)(tmp);
dlclose(dl);
//...
return 0;
}
pluginA.cpp:
#include<iostream>
using namespace std;
extern "C" void API(unsigned int N){switch(N){
case 0:cout<<"1\n"<<flush;break;
case 1:cout<<"2\n"<<flush;break;
case 2:cout<<"4\n"<<flush;break;
case 4:cout<<"16\n"<<flush;break;}}
I compiled the two part with the following command:
g++ -shared -o pluginA.so -fPIC plugin.cpp
g++ main.cpp -ldl
Here is the output
Segmentation fault (core dumped)
BTW, I also tried directly call api(tmp) rather than (*api)(tmp), that also don't work. Since api is a pointer, (*api) makes more sense?
I'm not sure what should I do. There are many totorials about calling function in shared library online, but most of them aren't fully coded, or they actually don't work.
And also I'm not sure what should I do with "attribute((visibility("default")))". Should I even write it down?
EDT1
Thanks for giving me so much advice. I finally find out that actually everything is a typo in compiling command... I mistakenly typed pluginA.so to pluginA.o, and that's the reason why it don't work...
Anyway, here is my revised program, with error handling added, and more "full" system added:
main.cpp:
#include<dirent.h>
#include<dlfcn.h>
#include<iostream>
#include<cstring>
using namespace std;
typedef bool (*DLAPI)(unsigned int);
int main(){
DIR* dldir=opendir("dl");
struct dirent* dldirf;
void* dl[255];
DLAPI dlapi[255];
unsigned char i,dlc=0;
char dldirfname[255]="./dl/";
unsigned int n;
while((dldirf=readdir(dldir))!=NULL){
if(dldirf->d_name[0]=='.')continue;
strcat(dldirfname,dldirf->d_name);
dl[dlc]=dlopen(dldirfname,RTLD_LAZY);
if(!dl[dlc])cout<<dlerror()<<endl;else{
dlapi[dlc]=(DLAPI)dlsym(dl[dlc],"API");
if(!dlapi[dlc])cout<<dlerror()<<endl;else dlc++;}
dldirfname[5]='\0';}
if(dlc==0){
cerr<<"ERROR:NO DL LOADED"<<endl;
return -1;}
while(true){
cin>>n;
for(i=0;i<dlc;i++)if((*dlapi[i])(n))break;
if(i==dlc)cout<<"NOT FOUND"<<endl;}
for(i=0;i<dlc;i++)dlclose(dl[i]);
return 0;}
You should read documentation of dlopen(3) and dlsym and you should always handle failure. So code
dl=dlopen("./pluginA.so",RTLD_LAZY);
if (!dl) { fprintf(stderr, "dlopen failure: %s\n", dlerror());
exit (EXIT_FAILURE); };
api=(API)dlsym(dl,"API");
if (!api) { fprintf(stderr, "dlsym failure: %s\n", dlerror());
exit (EXIT_FAILURE); };
The documentation of dlopen is explaining why you want to pass ./pluginA.so with a ./ prefix
At last, you should always compile with all warnings and debug info, so:
g++ -Wall -Wextra -g -shared -o pluginA.so -fPIC plugin.cpp
g++ -Wall -Wextra -g -rdynamic main.cpp -ldl
(It is useful to link the main program with -rdynamic so that the plugin could access its symbols)
You could want to dlclose(dl) just before the end of main ... (calling or returning from a dlsym-ed function will crash your program if you dlclose too early). You might even avoid the dlclose (i.e. accept some resource leak). By experience you usually can dlopen many hundreds of thousands shared objects (see my manydl.c)
Only once your program is debugged you could add some optimization flag like -O or -O2 (and perhaps remove the debugging flag -g, but I don't recommend that for beginners).
You should perhaps read Drepper's paper: How To Write Shared Libraries.
I correted your code a bit and use the error checking. Try that and get the idea what's going on:
#include<iostream>
#include<dlfcn.h>
using namespace std;
typedef void (*API)(unsigned int);
int main(int argc,char** argv)
{
API api;
unsigned int tmp;
//...
void* handle = dlopen("pluginA.so", RTLD_LAZY);
if (!handle)
{
std::cerr << dlerror() << std::endl;
return 1;
}
dlerror();
api = reinterpret_cast<API>(dlsym(handle, "API"));
if (!api)
{
std::cerr << dlerror() << std::endl;
return 2;
}
cin>>tmp;
(*api)(tmp);
dlclose(handle);
//...
return 0;
}
At last: why it is failed? Use the right path: "./pluginA.so", not "pluginA.so" or put the full path to your plugin.

dlopen() gives unresolved symbol error when .so tries to use a class from the main executable. Why?

I'm on Linux, the question is concerning shared objects of C++ classes.
The problem comes when my shared objects try to use resources linked into the main executable. I have the following codes:
loader.cpp:
#include <dlfcn.h>
#include <iostream>
#include "CommonInfo.h"
using namespace std;
int main(int argc, char** argv) {
for(int i=1; i<argc; ++i) {
string pth = "./";
pth.append(argv[i]);
void* dh = dlopen(pth.c_str(), RTLD_NOW);
if(dh==NULL) {
cerr << dlerror() << endl;
return 1;
}
CommonInfo::GetInfoFunc getInfo = (CommonInfo::GetInfoFunc)(dlsym(dh,"getInfo"));
if(getInfo==NULL) {
cerr << dlerror() << endl;
return 1;
}
CommonInfo* info = getInfo();
cout << "INFO: " << info->getX() << endl;
delete info;
}
return 0;
}
CommonInfo.h:
#include <string>
class CommonInfo {
public:
typedef CommonInfo* (*GetInfoFunc)();
private:
std::string x;
public:
CommonInfo(const std::string& nx);
std::string getX() const;
};
EDIT:
I accidentaly forgot to ctrl-c + ctrl-v the source of CommonInfo.cpp here. Of course, it is there during compilation, so CommonInfo.cpp:
#include "CommonInfo.h"
CommonInfo::CommonInfo(const std::string& nx) : x(nx) {
}
std::string CommonInfo::getX() const {
return x;
}
A Plugin.h header:
#include "CommonInfo.h"
extern "C" CommonInfo* getInfo();
A very simple Plugin.cpp:
#include <iostream>
#include "Plugin.h"
#include "CommonInfo.h"
using namespace std;
CommonInfo* getInfo() {
return new CommonInfo("I'm a cat!");
}
Compiling is done with:
g++ -rdynamic -ldl -Werror CommonInfo.cpp loader.cpp -o loader
g++ -shared -fPIC -Werror Plugin.cpp -o Plugin.so
Running:
./loader Plugin.so
And there goes the error:
./loader: symbol lookup error: ./Plugin.so: undefined symbol: _ZN10CommonInfoC1ERKSs
Indeed, looking inside Plugin.so with nm Plugin.so | grep -i CommonInfo it gives an 'U' for this symbol (unresolved), which is perfectly ok.
Also, looking inside the binary of loader with nm loader.so | grep -i CommonInfo I could find the symbol with 'T', which is also ok.
Question is, shouldn't dlfcn.h unresolve the symbol in question from the main binary? Without this feature it becomes quite hard to use these stuff... Do I have to write a class factory function for CommonInfo, load it with dlfcn from the plugin and call that?
Thanks in advance,
Dennis
I haven't looked closely at your code, but I have in the past found behavior like you describe in the title when I did not link the executable with -E. (Or -Wl,-E when linking with gcc rather than ld.)
Note that not all platforms let the shared libraries take symbols from the calling binary. Linux and the *BSDs allow you to. But if you ever want to port to, say, Windows, you will not be able to use this pattern. I believe there are also some Unix-type OS's that won't let you do this. (It's been a while so I don't remember... Maybe it was Solaris?)