【问题标题】:Android dynamic_cast not work when dynamic loading from shared libraries从共享库动态加载时,Android dynamic_cast 不起作用
【发布时间】:2020-10-23 05:13:15
【问题描述】:

我在将 vsomeip 移植到 Android 时遇到了 dynamic_cast 问题。

为了简化案例,我创建了一个相当简单的测试 app/lib 结构,如下所示:

它包含一个库和一个应用程序:

  • libbase_1.so:实现类 Final 继承自 Base_1 和 Base_2,提供 get() 以返回 std::shared

  • test_app:动态加载 libbase_1.so(通过 dl_open),获取 std::shared,并动态转换为 std::shared

动态转换在 x86 和其他 arm 平台上运行良好(例如:使用 linaro 工具链);但无法在 Android 上运行,它总是得到 nullptr

AOSP 的环境是:

  • NDK 版本:R18
  • 构建系统:AOSP
  • 主机操作系统:Ubuntu 18.04
  • 编译器:Clang++ (9.0.3)
  • TARGET_ARCH:armv8a

我遵循了下面的一些参考,但都不起作用:

有人知道吗?非常感谢~~


完整的可运行 src 可以从以下位置下载: https://drive.google.com/file/d/1YNsZVPVIn7_y247byBfl6JaTVtjR6tYn/view?usp=sharing

也贴在这里:

//Android.bp:mm可以直接构建

libbase_1_srcs = [
   "src_1.cpp",
   "src_2.cpp",
   "src_3.cpp",
   "lib_1.cpp",
]

main_srcs = [
   "main.cpp",
   "src_1.cpp",
   "src_2.cpp",
]

cc_defaults {
    name: "test_cast_defaults",
    cppflags: [
   "-std=c++11",
   "-frtti",
    ]
}

cc_library_shared {
    name: "libbase_1",
    vendor: true,
    srcs: libbase_1_srcs,
    defaults: [
        "test_cast_defaults"
    ],
    rtti: true,
}

cc_binary {
    name: "test_cast",
    srcs: main_srcs,
    vendor: true,
    defaults: [
        "test_cast_defaults"
    ],
    shared_libs: [
    ],
    rtti: true,
}

//CMakeLists.txt:测试X86或其他

cmake_minimum_required(VERSION 3.1)
set(project_name "test_cast")
project(${project_name})

add_library(base_1 SHARED lib_1.cpp src_1.cpp src_2.cpp src_3.cpp)
target_include_directories(base_1 PUBLIC ${${project_name}_SOURCE_DIR}/)

add_executable(test_cast main.cpp src_1.cpp src_2.cpp)
target_include_directories(test_cast PUBLIC ${${project_name}_SOURCE_DIR}/)
target_link_libraries(test_cast PUBLIC dl)

//main.cpp

#include <dlfcn.h>
#include <iostream>
#include "lib_1.h"
#include <memory>

#define libname "libbase_1.so"
#define funcname "get_plugin"

int main(void)
{
    void *handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL);
    void *func;
    if (handle == nullptr) {
        std::cout << "Can not find: " << libname << std::endl;
        return -1;
    }
    func = dlsym(handle, funcname);
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        std::cout << "dlsym err: " << dlsym_error <<std::endl;
        return -1;
    }

    plugin_init_func func_get = reinterpret_cast<plugin_init_func>(func);
    get_plugin_func func_create = (*func_get)();

    auto base_1 = (*func_create)();
    auto base_2 = std::dynamic_pointer_cast<Base_2>(base_1);

    if (base_2) {
        std::cout << "dl: dynamic cast success!" << std::endl;
    } else {
        std::cout << "dl: dynamic cast failed!" << std::endl;
    }
    return 0;
}

//lib_1.cpp

#include "lib_1.h"

get_plugin_func get_plugin(void)
{
    return Final::get;
}

//lib_1.h

#ifndef __LIB_1_H__
#define __LIB_1_H__
#include "header_3.h"
typedef std::shared_ptr<Base_1> (*get_plugin_func)();
typedef get_plugin_func (*plugin_init_func)();
extern "C" {
    get_plugin_func get_plugin(void);
};
#endif

//header_1.h

#ifndef __HEADER_1__
#define __HEADER_1__
class Base_1 {
public:
    virtual ~Base_1();
};
#endif

//header_2.h

#ifndef __HEADER_2__
#define __HEADER_2__
class Base_2 {
public:
    virtual ~Base_2();
};
#endif

//header_3.h

#ifndef __HEADER_3__
#define __HEADER_3__
#include "header_1.h"
#include "header_2.h"
#include <iostream>
#include <memory>
class Final : public Base_2,
              public Base_1 {
public:
    static std::shared_ptr<Base_1> get(void) {
        std::shared_ptr<Base_1> base_1 = std::make_shared<Final>();
        auto base_2 = std::dynamic_pointer_cast<Base_2>(base_1);
        if (base_2) {
            std::cout << "in lib: dynamic cast success!" << std::endl;
        } else {
            std::cout << "in lib: dynamic cast failed!" << std::endl;
        }
        return base_1;
    }
    ~Final();
};
#endif

//src_1.cpp

#include <header_1.h>
#include <iostream>
Base_1::~Base_1()
{
    std::cout << "in: " << __func__ << std::endl;
}

//src_2.cpp

#include "header_2.h"
#include <iostream>
Base_2::~Base_2()
{
    std::cout << "in: " << __func__ << std::endl;
}

//src_3.cpp

#include "header_3.h"
#include <iostream>
Final::~Final() {
    std::cout << "in: " << __func__ << std::endl;
}

成功运行(X86):

in lib: dynamic cast success!
dl: dynamic cast success!
in: ~Final
in: ~Base_1
in: ~Base_2

失败运行(Android):

in lib: dynamic cast success!
dl: dynamic cast failed!
in: ~Final
in: ~Base_1
in: ~Base_2

【问题讨论】:

    标签: c++ android-ndk


    【解决方案1】:

    当我通过 vsomeip 和 clang 编译器使用 CommonAPI 时,我遇到了同样的问题,只有解决方法帮助我修复了它。更多细节在这里:https://github.com/nkh-lab/genivi-capi-someip-examples/blob/ndk/CMakeLists.txt

    问题是我们在libvsomeip3.solibvsomeip3-cfg.sodynamic_pointer_cast 中有相同的类型信息vsomeip_v3::configuration_plugin 使用了错误的类型信息(例如类型信息表中的第一个条目)。因此,我们需要更改上述库的加载顺序,然后dynamic_pointer_cast 将使用正确的加载顺序。

    也在GENIVI/vsomeip/issues回复

    【讨论】:

    【解决方案2】:

    (这不是一个实际的解决方案,只是一个大注释)

    我认为您不应该在主可执行文件和共享库中包含相同的对象模块。这是避免它的一种可能方法(Makefile):

    CXXFLAGS += -g -W -Wall -Wextra -fPIC
    LDFLAGS  += -g
    
    clean:
            rm *.o *.so main 2>/dev/null || true
    
    main: LDFLAGS += -export-dynamic
    main: LDLIBS += -ldl
    main: main.o src_1.o src_2.o src_3.o
            ${CXX} ${LDFLAGS} -o $@ $^ ${LDLIBS}
    
    libbase_1.so: LDFLAGS += -shared
    libbase_1.so: lib_1.o
            ${CXX} ${LDFLAGS} -o $@ $^ ${LDLIBS}
    

    【讨论】:

    • 谢谢,你是对的。正常的编码风格不应该是这样的。这是为了避免用于演示的代码复杂。但是您上面的 Makefile 将不起作用,因为 libbase_1 缺少由应用程序动态加载的类实现。
    • 我在 WSL/Ubuntu18.04 上测试过。 (-export-dynamic 部分是其中的重要部分。)
    • 对不起,我没有注意到标志,是的,makefile可以运行^_^。
    • 还可以将Final::get 的实现从header_3.h 移动到src_3.c
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 2014-03-04
    • 2019-12-13
    • 1970-01-01
    相关资源
    最近更新 更多