我从here编译了gRPC Android示例。
我想从adb shell中以可执行文件的形式运行程序。
将这些行添加到grpc-helloworld.cc
:
#include <iostream>
int main() {
std::cout << "qwerty" << std::endl;
return 0;
}
这些行分别指向其CMakeLists.txt
:
add_executable(avocado
src/main/cpp/grpc-helloworld.cc)
target_include_directories(avocado
PRIVATE ${HELLOWORLD_PROTO_HEADERS})
target_link_libraries(avocado
helloworld_proto_lib
android
${log-lib})
然后我推送了生成的可执行文件和libs文件并尝试运行它:
LD_LIBRARY_PATH=. ./avocado
我遇到以下错误:
[libprotobuf致命 /home/buga/grpc/third_party/protobuf/src/google/protobuf/stubs/common.cc:79] 该程序是针对协议缓冲区3.0.0版编译的 运行时库,与安装的版本不兼容 (3.5.1)。请与程序作者联系以获取更新。如果编译 您自己编写程序,请确保您的标头来自相同的 版本的协议缓冲区作为链接时库。 (版 验证失败 “ out / soong / .intermediates / frameworks / av / drm / libmediadrm / libmediadrm / android_arm64_armv8-a_kryo300_shared_core / gen / proto / frameworks / av / drm / libmediadrm / protos / plugin_metrics.pb.cc”。)终止 除了未捕获的google :: protobuf :: FatalException类型的异常: 程序是根据协议缓冲区的3.0.0版本编译的 运行时库,与安装的版本不兼容 (3.5.1)。请与程序作者联系以获取更新。如果编译 您自己编写程序,请确保您的标头来自相同的 版本的协议缓冲区作为链接时库。 (版 验证失败 “ out / soong / .intermediates / frameworks / av / drm / libmediadrm / libmediadrm / android_arm64_armv8-a_kryo300_shared_core / gen / proto / frameworks / av / drm / libmediadrm / protos / plugin_metrics.pb.cc”。) 中止
我在做什么错了?
我们意识到protobuf库有一个名为libprotobuf-cpp-full.so
和libprotobuf-cpp-lite.so
的版本,看来它们的版本是3.0.0。这与我们编译为静态库或共享库的版本(3.5.1)冲突。
答案 0 :(得分:1)
我不太确定为什么会这样。链接器加载helloworld_proto_lib
后,它将覆盖所有已加载的protobuf符号,并且由于某种原因,与您无关的另一个库会使程序崩溃。但这并没有告诉您任何新鲜事。
这是解决此问题的一种方法:
制作主extern "C"
,然后更改其名称。例如:
extern "C" int my_main() {
std::cout << "qwerty" << std::endl;
return 0;
}
这将包含可执行文件的实际主体,它将动态加载库helloworld_proto_lib
和grpc-helloworld
。操作方法如下:
#include <iostream>
#include <android/dlext.h>
#include <dlfcn.h>
int main() {
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD;
void* proto_lib = android_dlopen_ext("/path/to/libhelloworld_proto_lib.so", RTLD_LAZY, &extinfo);
void* helloworld = dlopen("/path/to/libgrpc-helloworld.so", RTLD_LAZY);
int (*my_main)() = (int (*)())dlsym(helloworld, "my_main");
return my_main();
}
https://developer.android.com/ndk/reference/group/libdl中描述了android_dlopen_ext
中的函数#include <android/dlext.h>
及其标志参数。在上面的代码中,我们传递了标志ANDROID_DLEXT_FORCE_LOAD
,该标志记录为:
设置后,请勿使用stat(2)检查库是否已加载。
在由于某些原因多个ELF文件共享相同文件名的情况下(例如,由于已经删除并覆盖了已经加载的库),此标志允许强制加载库。
请注意,如果该库具有与旧库相同的DT_SONAME,而其他库在其DT_NEEDED列表中具有该名称,则第一个库将用于解析任何依赖项。
我认为粗体字解释了此解决方案为何起作用。
由于您将动态加载helloworld_proto_lib
,因此现在可以从可执行文件定义中将其删除,并且不需要任何原始标头:
add_executable(avocado
src/main/cpp/grpc-avocado.cc)
target_link_libraries(avocado
android
${log-lib})
您现在可以构建,推送可执行文件avocado
和两个库libgrpc-helloworld.so
,libhelloworld_proto_lib.so
,然后运行。您不需要LD_LIBRARY_PATH
。祝您在项目的其余部分中一切顺利!