Android dynamic_cast 从共享库动态加载时不起作用
Android dynamic_cast not work when dynamic loading from shared libraries
我在将 vsomeip 移植到 Android 时遇到 dynamic_cast 问题。
为了简化案例,我创建了一个相当简单的测试app/lib结构如下:
它包含一个库和一个应用程序:
libbase_1.so:实现继承自Base_1和Base_2的class Final,为return提供get() std::shared
test_app:动态加载libbase_1.so(通过dl_open),获取std::shared ,并动态转换为std::shared.
动态转换在 x86 和其他 arm 平台上运行良好(例如:使用 linaro 工具链);但无法在 Android 上工作,它总是得到 nullptr.
AOSP 的环境是:
- NDK版本:R18
- 构建系统:AOSP
- 主机OS:Ubuntu18.04
- 编译器:Clang++ (9.0.3)
- TARGET_ARCH: armv8a
我遵循了下面的一些参考,但都不起作用:
- Android NDK + Two Shared Libraries + RTTI + Dynamic cast = Impossible
- https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#rtti_exceptions-not-working-across-library-boundaries
- https://github.com/android/ndk/issues/533#issuecomment-335977747
有人知道吗?非常感谢~~
完整的可运行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
(这不是实际的解决方案,只是一个大评论)
我认为您不应将相同的对象模块包含到主可执行文件和共享库中。这是避免它的可能方法(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}
当我通过带有 clang 编译器的 vsomeip 使用 CommonAPI 时,我遇到了同样的问题,只有变通方法帮助我修复了它。此处有更多详细信息:https://github.com/nkh-lab/genivi-capi-someip-examples/blob/ndk/CMakeLists.txt
问题是我们在 libvsomeip3.so
和 libvsomeip3-cfg.so
和 dynamic_pointer_cast
中有相同的类型信息 vsomeip_v3::configuration_plugin
使用了错误的类型信息(例如类型信息中的第一个条目 table).因此,我们需要更改上述库的加载顺序,然后 dynamic_pointer_cast
将使用正确的库。
中回答
我在将 vsomeip 移植到 Android 时遇到 dynamic_cast 问题。
为了简化案例,我创建了一个相当简单的测试app/lib结构如下:
它包含一个库和一个应用程序:
libbase_1.so:实现继承自Base_1和Base_2的class Final,为return提供get() std::shared
test_app:动态加载libbase_1.so(通过dl_open),获取std::shared
,并动态转换为std::shared .
动态转换在 x86 和其他 arm 平台上运行良好(例如:使用 linaro 工具链);但无法在 Android 上工作,它总是得到 nullptr.
AOSP 的环境是:
- NDK版本:R18
- 构建系统:AOSP
- 主机OS:Ubuntu18.04
- 编译器:Clang++ (9.0.3)
- TARGET_ARCH: armv8a
我遵循了下面的一些参考,但都不起作用:
- Android NDK + Two Shared Libraries + RTTI + Dynamic cast = Impossible
- https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#rtti_exceptions-not-working-across-library-boundaries
- https://github.com/android/ndk/issues/533#issuecomment-335977747
有人知道吗?非常感谢~~
完整的可运行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
(这不是实际的解决方案,只是一个大评论)
我认为您不应将相同的对象模块包含到主可执行文件和共享库中。这是避免它的可能方法(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}
当我通过带有 clang 编译器的 vsomeip 使用 CommonAPI 时,我遇到了同样的问题,只有变通方法帮助我修复了它。此处有更多详细信息:https://github.com/nkh-lab/genivi-capi-someip-examples/blob/ndk/CMakeLists.txt
问题是我们在 libvsomeip3.so
和 libvsomeip3-cfg.so
和 dynamic_pointer_cast
中有相同的类型信息 vsomeip_v3::configuration_plugin
使用了错误的类型信息(例如类型信息中的第一个条目 table).因此,我们需要更改上述库的加载顺序,然后 dynamic_pointer_cast
将使用正确的库。