原本功能是在javaVMExt::LoadNativeLibrary函数中实现的
(art\runtime\jin_internal.cc文件中):
该函数正常情况下使用dlopen打开so文件,但当x86系统试图打开arm版本的so ,返回错误时,这时就会使用NativeBridge自带的加载过程。
bool JavaVMExt::LoadNativeLibrary(const std::string& path,
Handle class_loader,
std::string* detail) {
......
const char* path_str = path.empty() ? nullptr : path.c_str();
void* handle = dlopen(path_str, RTLD_LAZY);
bool needs_native_bridge = false;
if (handle == nullptr) {
if (android::NativeBridgeIsSupported(path_str)) {
handle = android::NativeBridgeLoadLibrary(path_str, RTLD_LAZY);
needs_native_bridge = true;
}
}
......
bool created_library = false;
{
MutexLock mu(self, libraries_lock);
library = libraries->Get(path);
if (library == nullptr) { // We won race to get libraries_lock
library = new SharedLibrary(path, handle, class_loader.Get());
libraries->Put(path, library);
created_library = true;
}
}
......
bool was_successful = false;
void* sym = nullptr;
if (UNLIKELY(needs_native_bridge)) {
library->SetNeedsNativeBridge();
sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
} else {
sym = dlsym(handle, "JNI_OnLoad");
}
......
}
android::NativeBridgeRuntimeCallbacks
android::NativeBridgeCallbacks全局变量
callbacks->isSupported
callbacks->loadLibrary(libpath, flag)
callbacks->getTrampoline
添加链接描述
“ro.dalvik.vm.native.bridge” 指定动态链接库
如果决定要启用NativeBridge,则还需要调用android::PreInitializeNativeBridge对其做预初始化:
2)android.os.Build.SUPPORTED_ABIS,
更正过的
struct NativeBridgeCallbacks {
// Version number of the interface.
uint32_t version;
bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
const char* instruction_set);
void* (*loadLibrary)(const char* libpath, int flag);
void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
bool (*isSupported)(const char* libpath);
const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
bool (*isCompatibleWith)(uint32_t bridge_version);
NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
};
多出两个函数一个叫做isCompatibleWith,一个叫做getSignalHandler。
3)persist.sys.nativebridge系统属性设置为1
4)ro.product.cpu.abilist系统属性
这个属性会控制特定版本的系统可以安装的app类型,为了是x86系统可以安装只包含arm指令集的apk,我没就需要更改这个属性。
ro.product.cpu.abilist=x86,armeabi-v7a,armeabi
ro.product.cpu.abilisit32=x86,armeabi-v7a,armeabi
5)ro.dalvik.vm.isa.arm系统属性(Android-X86系统中的build.prop)
ro.dalvik.vm.isa.arm=x86
ro.dalvik.vm.isa.=
看了半天发现好像剩下的只剩使用指定程序打开某一类程序
而原始NativeBridgeCallbacks结构体的定义如下(代码位于system\core\include\nativebridge\native_bridge.h文件中):
struct NativeBridgeCallbacks {
uint32_t version;
bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
const char* instruction_set);
void* (*loadLibrary)(const char* libpath, int flag);
void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
bool (*isSupported)(const char* libpath);
const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
};
但这里的实现却是在libnb.so里面,libnb.so里面的实现又是通过回调转到libhoudini.so。所以,这个所谓的nativebridge本质上就是实现以上几个接口后实现的功能。
system的native_bridge.cpp通过外部实现的这几个接口来传递编码转换的初始化函数。
总结:
libnb里面有7个回调接口,原本native_bridge.cpp里面只利用了5个,为了增加houdin我们利用增加的在native_bridge里面做了检查和信号处理。
getsignalHandler利用在这里调用这个函数的地方在这里(代码位于art\runtime\native_bridge_art_interface.cc文件中):
void InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
if (android::InitializeNativeBridge(env, instruction_set)) {
if (android::NativeBridgeGetVersion() >= 2U) {
#ifdef _NSIG
for (int signal = 0; signal < _NSIG; ++signal) {
android::NativeBridgeSignalHandlerFn fn = android::NativeBridgeGetSignalHandler(signal);
if (fn != nullptr) {
SetSpecialSignalHandlerFn(signal, fn);
}
}
#endif
}
}
}
最后函数就会利用getTrampoline跳到转化指令集里面执行。
不过有一点还不清楚,那就是绑定指定程序执行的操作有啥用。先不管了。
android 5.0 具体变化文件:
art/runtime/sigchainlib/sigchain_dummy.cc 、 sigchain.cc 、 sigchain.h
art/runtime/jni_internal.cc 、 native_bridge_art_interface.cc 、native_bridge_art_interaface.h
libnb.so位置 device/generic/common/nativebridge/
native_bridge.cc 位于、system/core/libnativebridge/native_bridge.cc
经过对代码的不断跟踪和错误修正,最终结果如下:
我们这仅仅是编译了简单的jni函数,实现了native层的hello world但根据日志中显示加载文件为arm/libplthook-lib.so
可以看出我们成功的执行了arm指令下的so文件中的字符串转换。并且我们编译的程序so库中只包含arm指令,没有x86的so。感兴趣的同学可以自己试试。
并且,那个标定指令使用指定程序打开某一类程序
这一部分的东西并没什么卵用,因为我到这里根本没有设置相应的文件。
大功告成。
学习源码这么长时间,第一次添加自己的逻辑,感觉不错。哈哈哈哈