shared library error about "undefined symbol" when using `dlopen` - c++

First thing first, here's my minimum reproducible program:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.17)
project(untitled4)
set(CMAKE_CXX_STANDARD 17)
add_library(lib1 SHARED lib1.cpp)
add_executable(untitled4 main.cpp)
target_link_libraries(untitled4 PRIVATE dl)
main.cpp:
#include "iostream"
#include <dlfcn.h>
int Test() {
return 123456;
}
int main() {
auto* handler = dlopen("/home/liu/source/untitled4/cmake-build-debug/liblib1.so", RTLD_LAZY|RTLD_GLOBAL);
if (!handler) {
std::cerr << dlerror();
exit(1);
}
}
and lib1.cpp:
#include "iostream"
extern int Test();
class Foo {
public:
Foo() {
std::cout << Test() << std::endl;
}
};
Foo foo;
now let me explain:
As you can see I defined a function called Test in main.cpp, and I want to the shared library liblib1.so call it when it is loaded.
But when I run the main() function, there's an error log said:
/home/liu/source/untitled4/cmake-build-debug/liblib1.so: undefined symbol: _Z4Testv
I check the symbol by using nm untitled4 |grep Test and the symbol seems exist:
0000000000000b87 t _GLOBAL__sub_I__Z4Testv
0000000000000aea T _Z4Testv
So what did I do wrong? How to fix this?
An important thing to be notice is that in the real case, the build of lib1 and the build of main.cpp are totally separated, the two build don't know each other. But I can make them into one build (very difficult) if this can fix the problem(if there's no other way).
P.S. I tried using extern "C" to wrap around the Test() in both files, but not working, seems not the C/C++ function naming problem.

With the addition of the linker option -rdynamic your code does not fail with "undefined symbol".
You can set that with set_target_properties(untitled4 PROPERTIES ENABLE_EXPORTS 1) or target_link_options(untitled4 BEFORE PRIVATE "-rdynamic").
Example:
cmake_minimum_required(VERSION 3.17)
project(untitled4)
set(CMAKE_CXX_STANDARD 17)
add_library(lib1 SHARED lib1.cpp)
add_executable(untitled4 main.cpp)
set_target_properties(untitled4 PROPERTIES ENABLE_EXPORTS 1)
target_link_libraries(untitled4 PRIVATE dl)

Related

Issue linking c++ project together with CMake [duplicate]

This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 6 months ago.
Been having a little issue figuring out how to link things together with CMake and I've been stuck for three days so far. No matter what I've tried I've been having an issue in having the function returnThisClass() being undefined no matter what I've tried.
On the minimum reproducible example in headertest.cpp, there is an undefined reference to the creation of the pointer, the same error is not present on my program despite everything appearing to be a direct copy.
My file structure is
main.cpp
src/
headertest.cpp
otherheader.cpp
lib/
headertest.h
otherheader.h
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(stackoverflowExample1 VERSION 0.1.0)
include(CTest)
enable_testing()
add_library(aTest STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/otherheader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/headertest.cpp)
target_include_directories(aTest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/)
set(
SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/headertest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/otherheader.cpp
)
add_executable(stackoverflowExample1 ${SOURCE_FILES})
target_link_libraries(stackoverflowExample1 PRIVATE aTest)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
main.cpp
#include "lib/otherheader.h"
int main() {
doSomethingHere();
}
headertest.h
namespace theTest {
class foo1 {
public:
foo1();
};
foo1 * returnThisClass();
}
otherheader.h
void doSomethingHere();
headertest.cpp
#include "../lib/headertest.h"
theTest::foo1 * returnThisClass(){
theTest::foo1 * aFoo1 = new theTest::foo1();
return aFoo1;
}
otherheader.cpp
#include "../lib/otherheader.h"
#include "../lib/headertest.h"
void doSomethingHere(){
theTest::foo1 *foo1Ptr = theTest::returnThisClass();
}
Change
theTest::foo1 * returnThisClass(){
to
theTest::foo1 * theTest::foo1::returnThisClass(){
No idea about the CMake part, but the above is the first step.

CMake not linking static libraries properly when using LLVM on Windows

I am using Windows 10 and CMake to build my project (using MinGW Makefiles as the generator), while choosing Clang/Clang++ (LLVM) as my compiler due to my Language Server Protocol also being for Clang.
However, when trying to link a static library I had created to my main executable, none of the definitions in the translation units were able to be resolved.
For a simple recreation:
File system
+ root/
+ main.cpp
+ foobar.cpp
+ foobar.hpp
+ CMakeLists.txt
// main.cpp
#include <iostream>
#include "foobar.hpp"
int main( int argc, char** argv ) {
foo();
std::cin.get();
return 0;
}
// foobar.cpp
#include "foobar.hpp"
void foo() {
std::cout << "bar" << std::endl;
}
// foobar.hpp
#pragma once
#ifndef FOOBAR_HEADER_H
#define FOOBAR_HEADER_H
#include <iostream>
void foo();
#endif
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(linker_fail
VERSION 1.0.0
LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(foobar
foobar.cpp
foobar.hpp)
add_executable(${PROJECT_NAME}
main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC
foobar)
Then for building:
root> cmake -S . -B bin -G "MinGW Makefiles"
root> cmake --build bin
lld-link: error: undefined symbol: void __cdecl foo(void)
Does anyone know how to fix this? Should I have passed some arguments to the compiler/linker? Or does Clang just not allow static linking?

"Undefined reference to " after linking with CMake

I want to build a simple application based on a .cpp and a .h file.
I couldn't make my project work so i started from a basic example but didn't succeed as i'm just starting creating project in Linux. From what i've seen, my CMakeLists should be like this :
My CMakeLists.txt :
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 11)
project(test C CXX)
add_executable(${PROJECT_NAME} main_new.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR})
My main_new.cpp :
#include <read.h>
int main(int argc, char* argv[])
{
int a, b, c, d, e = 0;
std::cout << "hello im example " << std::endl;
read_int(&a, &b, &c, &d, &e);*
return 0;
}
My read.h :
#ifdef __cplusplus
extern "C" {
#endif
int read_int(int *vectorA, int *vectorB, int *matrixA, int *matrixB, int *flags);
#ifdef __cplusplus
}
#endif
My .cpp and .h file are in the same folder. cmake . is giving me no error, but after using make i get
main_new.cpp:(.text+0x68) : undefined reference to « read_int »
I'm using the makefile created by the cmake command. Should i create a custom makefile ?
Edit : Added question :
I also have to implement .so files, but doing target_link_libraries(test ${CMAKE_SOURCE_DIR}/libA.so ${CMAKE_SOURCE_DIR}/libB.so) in my CMakeLists.txt file doesn't work. How can i link these libraries to my executable ?

Dynamically linking a shared library from a pybind11-wrapped code

I am trying to add python bindings to a medium-sized C++ scientific code (some tens of thousands LOCs). I have managed to make it work without too many issues, but I have now incurred in an issue which I am incapable of solving myself. The code is organized as follows:
All the classes and data structures are compiled in a library libcommon.a
Executables are created by linking this library
pybind11 is used to create a core.so python module
The bindings for the "main" parts work fine. Indeed, simulations launched from the standalone code or from python give the exact same results.
However, the code also supports a plugin-like system which can load shared libraries at runtime. These shared libraries contain classes that inherit from interfaces defined in the main code. It turns out that if I try to link these shared libraries from python I get the infamous "undefined symbol" errors. I have checked that these symbols are in the core.so module (using nm -D). In fact, simulations that perform the dynamic linking with the standalone code works perfectly (within the same folder and with the same input). Somehow, the shared lib cannot find the right symbols when called through python, but it has no issues when loaded by the standalone code. I am using CMake to build the system.
What follows is a MCE. Copy each file in a folder, copy (or link) the pybind11 folder in the same place and use the following commands:
mkdir build
cd build
cmake ..
make
which will generate a standalone binary and a python module. The standalone executable will produce the correct output. By contrast, using the following commands in python3 (that, at least in my head, should be equivalent) yields an error:
import core
b = core.load_plugin()
main.cpp
#include "Base.h"
#include "plugin_loader.h"
#include <iostream>
int main() {
Base *d = load_plugin();
if(d == NULL) {
std::cerr << "No lib found" << std::endl;
return 1;
}
d->foo();
return 0;
}
Base.h
#ifndef BASE
#define BASE
struct Base {
Base();
virtual ~Base();
virtual void foo();
};
#endif
Base.cpp
#include "Base.h"
#include <iostream>
Base::Base() {}
Base::~Base() {}
void Base::foo() {
std::cout << "Hey, it's Base!" << std::endl;
}
plugin_loader.h
#ifndef LOADER
#define LOADER
#include "Base.h"
Base *load_plugin();
#endif
plugin_loader.cpp
#include "plugin_loader.h"
#include <dlfcn.h>
#include <iostream>
typedef Base* make_base();
Base *load_plugin() {
void *handle = dlopen("./Derived.so", RTLD_LAZY | RTLD_GLOBAL);
const char *dl_error = dlerror();
if(dl_error != nullptr) {
std::cerr << "Caught an error while opening shared library: " << dl_error << std::endl;
return NULL;
}
make_base *entry = (make_base *) dlsym(handle, "make");
return (Base *) entry();
}
Derived.h
#include "Base.h"
struct Derived : public Base {
Derived();
virtual ~Derived();
void foo() override;
};
extern "C" Base *make() {
return new Derived();
}
Derived.cpp
#include "Derived.h"
#include <iostream>
Derived::Derived() {}
Derived::~Derived() {}
void Derived::foo() {
std::cout << "Hey, it's Derived!" << std::endl;
}
bindings.cpp
#include <pybind11/pybind11.h>
#include "Base.h"
#include "plugin_loader.h"
PYBIND11_MODULE(core, m) {
pybind11::class_<Base, std::shared_ptr<Base>> base(m, "Base");
base.def(pybind11::init<>());
base.def("foo", &Base::foo);
m.def("load_plugin", &load_plugin);
}
CMakeLists.txt
PROJECT(foobar)
# compile the library
ADD_LIBRARY(common SHARED Base.cpp plugin_loader.cpp)
TARGET_LINK_LIBRARIES(common ${CMAKE_DL_LIBS})
SET_TARGET_PROPERTIES(common PROPERTIES POSITION_INDEPENDENT_CODE ON)
# compile the standalone code
ADD_EXECUTABLE(standalone main.cpp)
TARGET_LINK_LIBRARIES(standalone common)
# compile the "plugin"
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
ADD_LIBRARY(Derived SHARED Derived.cpp)
# compile the bindings
ADD_SUBDIRECTORY(pybind11)
INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/pybind11/include )
FIND_PACKAGE( PythonLibs 3 REQUIRED )
INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} )
ADD_LIBRARY(_oxpy_lib STATIC bindings.cpp)
TARGET_LINK_LIBRARIES(_oxpy_lib ${PYTHON_LIBRARIES} common)
SET_TARGET_PROPERTIES(_oxpy_lib PROPERTIES POSITION_INDEPENDENT_CODE ON)
pybind11_add_module(core SHARED bindings.cpp)
TARGET_LINK_LIBRARIES(core PRIVATE _oxpy_lib)
You are right, symbols from imported library are not visible because core loaded without RTLD_GLOBAL flag set. You can fix that with a couple of extra lines on python side:
import sys, os
sys.setdlopenflags(os.RTLD_GLOBAL | os.RTLD_LAZY)
import core
b = core.load_plugin()
From sys.setdlopenflags() doc:
To share symbols across extension modules, call as sys.setdlopenflags(os.RTLD_GLOBAL). Symbolic names for the flag values can be found in the os module (RTLD_xxx constants, e.g. os.RTLD_LAZY).

CMAKE LINKING ERROR with a user-defined shared library

I am a newcomer to the world of cmake. This question came up while I was experimenting with some basic cmake configurations in C++. To be precise, following is my directory structure :
/src----
|-> CMakeLists.txt
|-> main.cpp
|-> lib----
|-> libfsystem.so
|->filesystem----
|->CMakeLists.txt
|->listfiles.cpp
|->include-----
|->fsystem.h
Now, the /src/filesystem/CMakeLists.txt file is like this
cmake_minimum_required(VERSION 2.8)
project(fsystem)
set(CMAKE_BUILD_TYPE Release)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/build)
find_package(Boost REQUIRED system filesystem)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(fsystem SHARED listfiles.cpp)
While, the /src/CMakeLists.txt file is like this
cmake_minimum_required(VERSION 2.8)
project(vessel_detect)
add_subdirectory(filesystem)
add_executable(main main.cpp)
set(CMAKE_LIBRARY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
find_library(libpath fsystem)
MESSAGE(${libpath})
target_link_libraries(main ${libpath})
The library libfsystem.so is successfully created.
The library libfsystem.so is also successfully found by /src/CMakeLists.txt
However when the linking of main.cpp is done, then it gives me several undefined reference errors which should not have happened as everything has already been defined. For greater completeness, following is the content of main.cpp file
Main.cpp
#include<iostream>
#include"filesystem/include/fsystem.h"
using namespace std;
int main(void)
{
string path ("~");
vector<string>* output;
output = listfiles(path);
return 0;
}
The contents of listfiles.cpp are
listfiles.cpp
#include"fsystem.h"
using namespace boost::filesystem;
vector<string>* listfiles(int argc, char* argv[])
{
if (argc<2)
{
std::cout<<"No file name provided"<<std::endl;
}
else
{
path p(argv[1]);
vector<string>* output;
if (exists(p))
{
if(is_directory(p))
{
std::cout<<"You specified a directory"<<std::endl;
std::cout<<"Its contents are as follows :-"<<std::endl;
typedef std::vector<path> vec;
vec v;
copy(directory_iterator(p),directory_iterator(),back_inserter(v));
sort(v.begin(),v.end());
for(vec::const_iterator it(v.begin());it!=v.end();++it)
output->push_back(it->filename().string());
// std::cout<<it->filename()<<std::endl;
}
else if (is_regular_file(p))
{
std::cout<<argv[1]<<" "<<file_size(p)<<std::endl;
}
else
{
std::cout<<"The file is neither a directory nor a regular file"<<std::endl;
}
}
else
{
std::cout<<"The speicified path does not exist"<<std::endl;
}
}
}
And finally, the fsystem.h contents are :
fsystem.h
#ifndef _fsystem_h
#define _fsystem_h
#include<iostream>
#include<string>
#include<vector>
#include"boost/filesystem.hpp"
using namespace std;
vector<string>* listfiles(string);
#endif
Could someone provide me a reason for the undefined reference errors I am getting during the linking of main.cpp ? I would also be grateful if you could provide me with a resolution of this issue.
Thanks
Your not linking boost. you need to add target_link_libraries(fsystem ${Boost_LIBRARIES}) to the end of /src/filesystem/CMakeLists.txt and include_directories(${Boost_INCLUDE_DIRS}) between the find_package and add_library.
cmake_minimum_required(VERSION 2.8)
project(fsystem)
set(CMAKE_BUILD_TYPE Release)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/build)
find_package(Boost REQUIRED system filesystem)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${Boost_INCLUDE_DIRS})
add_library(fsystem SHARED listfiles.cpp)
target_link_libraries(fsystem ${Boost_LIBRARIES})
(1) For TARGET_LINK_LIBRARIES you should put the name of the target, thus:
TARGET_LINK_LIBRARIES(main fsystem)
(2) You declare listfiles as vector<string>* listfiles(string) while you define it as vector<string>* listfiles(int,char**)
Additionally you need to link with Boost per the other reply.