3

Summarize

I'm integrating a static library based on c/c++ into a shared library based on c (using cmake on linux).

I have errors like this: libmylib.so: undefined reference to `std::ios_base::Init::Init()'

The problem is strongly related with the use of c++ and its linking to the shared library. If I avoid the shared library (even another static library) no error occurs. Because of my project I can not avoid that shared library which uses the static library.

How to generate a properly shared library from an c/c++ static library and new c source code?

PD: I'm sorry for the long question, I'm posting the code in order to give you some context of my problem.

C++ code

cppexample.hpp

typedef struct cpp_api
{
    int (*func_ptr)(const char *, int);
} cpp_api;

#ifdef __cplusplus
extern "C" {
#endif

const cpp_api* getApi();

#ifdef __cplusplus
}
#endif

cppexample.cpp

int apiFunc(const char *strc, int value)
{
  std::string str(strc);
  std::cout << "Api call: " << str << std::endl;
  return value;
}

static const cpp_api libapi =
{
    &apiFunc,
};

extern "C"
{
  const cpp_api* getApi()
  {
    return &libapi;
  }
}

C code

example.h

void doSomething();

example.c

#include "example.h"
#include "cpp_lib/cppexample.hpp"

void doSomething()
{
  const cpp_api *api = getApi();
  int result = api->func_ptr("hello!", 12);
}

mainApi.h

void callDoSomething();

mainApi.c

#include "mainApi.h"
#include "mix_lib/example.h"

void callDoSomething()
{
  doSomething();
}

main.c

#include "mainApi.h"
int main(int argc, const char* argv[])
{
  callDoSomething();
  return 0;
}

Cmake code

Libary gene

cmake_minimum_required(VERSION 3.5)
set(CMAKE_BUILD_TYPE "Release") #Not debug
set(THIS MAINLIB) #project name
project(${THIS} C CXX) #For c and c++
set(CMAKE_C_STANDARD 99) #c99
set(CMAKE_CXX_STANDARD 11) #c++11
set(CMAKE_POSITION_INDEPENDENT_CODE ON) #fPIC

set(MAIN_LIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")

###### MAIN LIB ######
include_directories(${MAIN_LIB_ROOT} ${MAIN_LIB_ROOT}/main) 
list(APPEND MAIN_LIB_SOURCES
            "${MAIN_LIB_ROOT}/main/mainApi.h"
            "${MAIN_LIB_ROOT}/main/mainApi.c")
set(MAIN_LIB_NAME mainlib)
add_library(${MAIN_LIB_NAME} STATIC ${MAIN_LIB_SOURCES})
include("${MAIN_LIB_ROOT}/cpp_lib/cpp_lib.cmake")
include("${MAIN_LIB_ROOT}/mix_lib/mix_lib.cmake")
setup_cpp_lib()
setup_mix_lib()

###### MAIN EXECUTABLE ######
add_executable(${THIS} "${MAIN_LIB_ROOT}/main.c")
target_link_libraries(${THIS} PUBLIC ${MAIN_LIB_NAME})
list(APPEND MAIN_INSTALL_BINS 
            ${THIS})

setup_cpp_lib()

list(APPEND CPP_LIB_SOURCES
            "${MAIN_LIB_ROOT}/cpp_lib/cppexample.hpp"
            "${MAIN_LIB_ROOT}/cpp_lib/cppexample.cpp")

function(setup_cpp_lib)
  add_library(cpplib OBJECT ${CPP_LIB_SOURCES})
  target_sources(${MAIN_LIB_NAME} PRIVATE $<TARGET_OBJECTS:cpplib>)
endfunction()

setup_mix_lib()

list(APPEND MIX_LIB_SOURCES
            "${MAIN_LIB_ROOT}/mix_lib/example.h"
            "${MAIN_LIB_ROOT}/mix_lib/example.c")

function(setup_mix_lib)
  add_library(mixlib OBJECT ${MIX_LIB_SOURCES})
  target_sources(${MAIN_LIB_NAME} PRIVATE $<TARGET_OBJECTS:mixlib>)
endfunction()

Until here the libmainlib.a is created. I copied that library and the mainApi.h and main.c to another project and the error occurs

Cmake with the error

cmake_minimum_required(VERSION 3.5)
set(CMAKE_BUILD_TYPE "Release") #Not debug
set(THIS MYPROJECT) #project name
project(${THIS} C CXX) #For c only
set(CMAKE_C_STANDARD 99) #c99
set(CMAKE_CXX_STANDARD 11) #c++11
set(MAIN_LIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}") #current cmakelists path

include_directories(${MAIN_LIB_ROOT}/includes) #includes for library headers
add_library(mylib SHARED "${MAIN_LIB_ROOT}/main.c")
add_library(mainlib STATIC IMPORTED)
set_property(TARGET mainlib PROPERTY IMPORTED_LOCATION ${MAIN_LIB_ROOT}/lib/libmainlib.a)
target_link_libraries(mylib mainlib)
add_executable(${THIS} "${MAIN_LIB_ROOT}/main.c")
target_link_libraries(${THIS} mylib)

list(APPEND MAIN_INSTALL_BINS ${THIS})
install(TARGETS ${MAIN_INSTALL_BINS} DESTINATION "${CMAKE_INSTALL_PREFIX}")

Errors

libmylib.so: undefined reference to `std::ios_base::Init::Init()'
libmylib.so: undefined reference to `std::string::_Rep::_M_destroy(std::allocator<char> const&)'
libmylib.so: undefined reference to `std::string::_Rep::_S_empty_rep_storage'
libmylib.so: undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)'
collect2: error: ld returned 1 exit status
make[2]: *** [MYPROJECT] Error 1
make[1]: *** [CMakeFiles/MYPROJECT.dir/all] Error 2
make: *** [all] Error 2

===Edit===

I have a static library called libmainlib.a (simulates a complex library that was modified injecting c++ code). That library is used to generate a shared library called libmylib.so (simulates another complex library that I didn't modify but uses the static library).

  • This line target_link_libraries(${THIS} mylib) Were you trying to link to mainlib? – user3389943 Aug 30 '19 at 01:12
  • @user3389943 is the target_link_libraries(mylib mainlib) I'm trying to link mainlib to mylib in order to use mylib in another projects. – Esmeralda Quintero Aug 30 '19 at 01:16
  • is it possible to ask for a recompiled version of the static library? i was in similar situation that i need to make out .so files out of a binary distributed static library . after days of research and trying , the library was only proofed to missing critical compile option for my goal. i end up to go through a longer process to gain read access of the library source code – James Li Aug 30 '19 at 01:27

1 Answers1

5

You got that undefined references because mylib library is linked as C object. But since the static library libmainlib.a is a C++ one, it requires C++ linking. For more info about origin of such undefined references that question: undefined reference to `std::ios_base::Init::Init()'.

Such incorrect linking is because the library mainlib in your second code is STATIC IMPORTED (both keywords are important) and CMake isn't aware of the actual language of that library.

You need to hint CMake that given library is actually a C++ one and requires C++ linking:

set_property(TARGET mainlib PROPERTY IMPORTED_LINK_INTERFACE_LANGUAGES CXX)

Your first code works correctly, because its mainlib library, while it is STATIC, is not IMPORTED one and CMake knows its sources. As a source cppexample.cpp is definitely a C++ one, CMake treats mainlib library as C++.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Thank you very much! that helps me to found that I also need to add "Libs.private: -lstdc++" inside of the "libmainlib.pc" file – Esmeralda Quintero Aug 30 '19 at 19:03
  • 1
    If you have `.pc` file for your library, you may also use [pkg_check_modules](https://cmake.org/cmake/help/v3.15/module/FindPkgConfig.html#command:pkg_check_modules) for work with this library in CMake. Since CMake 3.6 this command may produce IMPORTED target, so you need only to pass that target to `target_link_libraries` and everything will work. – Tsyvarev Aug 30 '19 at 20:43