我对我们的一位合作伙伴提供的共享库有疑问,并将其范围缩小到此测试用例:

我自己的测试库已编译为 libCrashTestLib.dylib :

#include <exception>
#include <iostream>

extern "C" void Test() {
  try {
    throw std::exception{};
  } catch (const std::exception& e) { }
  std::cout << "Success" << std::endl;
}

我的主要可执行文件加载了我的库并调用了Test函数:
#include <string>
#include <dlfcn.h>

int main(int argc, char** argv) {
  std::string lib_path = "/path/to/libCrashTestLib.dylib";

  void* handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
  void (*test_pointer)() = reinterpret_cast<void(*)()>(dlsym(handle, "Test"));
  test_pointer();
  dlclose(handle);
}

这可以正常工作并打印Success

但是,当我只是链接(甚至没有调用)我们合作伙伴提供的库时,它会给我一个EXC_BAD_ACCESS,在该代码中我会抛出异常。

我要寻找的是两件事之一:
  • 一个编译器或链接器开关,可以帮助我摆脱这种行为。
  • 一种创建我自己的库的方法,当我链接该程序时,该程序会崩溃,因此我可以为我们的合作伙伴提供更多详细信息,说明需要进行哪些修改。

  • 为了完整起见,这是我正在使用的CMakeLists.txt:
    cmake_minimum_required(VERSION 3.0)
    project(CrashTest CXX)
    
    set(CMAKE_CXX_STANDARD 14)
    set(CMAKE_CXX_STANDARD_REQUIRED on)
    
    find_library(VendorApi NAMES vendorapi PATHS ${CMAKE_SOURCE_DIR})
    
    add_library(CrashTestLib SHARED lib.cpp)
    # Uncomment this line to make it crash:
    #target_link_libraries(CrashTestLib VendorApi)
    
    add_executable(CrashTestMain main.cpp)
    

    最佳答案

    崩溃的最可能原因是供应商的库是使用libstdc++编译的,而您的代码是libc++编译的。

    我可以在构造类作为库加载时间的一部分时发出cout来很容易地引发崩溃。

    #include <exception>
    #include <iostream>
    #include <string>
    
    class bongo {
    
        public:
        bongo() {
            std::cout << "Log something" << std::endl;
        }
    };
    
    static class bongo *boing;
    
    void __attribute__((constructor)) start_something(void) {
        boing = new bongo;
    }
    
    
    extern "C" void Test() {
      try {
        throw std::exception{};
      } catch (const std::exception& e) { }
      std::cout << "Success" << std::endl;
    }
    

    加载程序代码:
    #include <string>
    #include <dlfcn.h>
    
    int main(int argc, char** argv) {
      std::string lib_path = "./libCrashTestLib.dylib";
    
      void* handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
      void (*test_pointer)() = reinterpret_cast<void(*)()>(dlsym(handle, "Test"));
      test_pointer();
      dlclose(handle);
    }
    

    生成文件:
    CXXFLAGS += -std=c++11
    STDLIB = -stdlib=libstdc++
    
    all: libCrashTestLib.dylib testLib
    
    libCrashTestLib.dylib: CrashTest.cpp
        $(CXX) $(CXXFLAGS) $(STDLIB) -shared -o $@ $<
    
    testLib: testLib.cpp
        $(CXX) $(CXXFLAGS) -o $@ $<
    
    clean:
        rm -f *.dylib testLib
    

    调用方式:
    $ make clean && make STDLIB= && ./testLib
    rm -f *.dylib testLib
    c++ -std=c++11  -shared -o libCrashTestLib.dylib CrashTest.cpp
    c++ -std=c++11 -o testLib testLib.cpp
    Log something
    Success
    

    调用失败:
    $ make clean && make && ./testLib
    rm -f *.dylib testLib
    c++ -std=c++11 -stdlib=libstdc++ -shared -o libCrashTestLib.dylib CrashTest.cpp
    c++ -std=c++11 -o testLib testLib.cpp
    Segmentation fault: 11
    

    您可以使用otool -L检查链接到二进制文件的C++库的版本:
    $ otool -L libCrashTestLib.dylib
    libCrashTestLib.dylib:
        libCrashTestLib.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 104.1.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
    

    10-08 06:09