当前位置: 首页 > news >正文

怎么制作网站链接手机/网站怎么优化

怎么制作网站链接手机,网站怎么优化,微信公众号小程序开发多少钱,wordpress discuz论坛本文主要讲述Android 加载动态链接库的过程,为了分析工作中遇到的一个问题 x86的系统是如何运行arm的动态链接库的。参考博客:https://pqpo.me/2017/05/31/system-loadlibrary/ 深入理解 System.loadLibraryhttps://www.jianshu.com/p/bf8b4a90f825 Andr…

本文主要讲述Android 加载动态链接库的过程,为了分析工作中遇到的一个问题 x86的系统是如何运行arm的动态链接库的。

参考博客:

https://pqpo.me/2017/05/31/system-loadlibrary/ 深入理解 System.loadLibrary

https://www.jianshu.com/p/bf8b4a90f825 Android Native库的加载及动态链接

https://blog..net/groundhappy/article/details/80493358 android的native_bridge

基于android7.0代码,涉及文件:

libcore\ojluni\src\main\java\java\lang\System.java

libcore\ojluni\src\main\java\java\lang\Runtime.java

libcore\dalvik\src\main\java\dalvik\system\PathClassLoader.java

libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java

libcore\ojluni\src\main\native\Runtime.c

art\runtime\openjdkjvm\OpenjdkJvm.cc

art\runtime\java_vm_ext.cc

system\core\libnativeloader\native_loader.cpp

bionic\linker\dlfcn.cpp

bionic\linker\linker.cpp

android是基于linux系统的,在开始前像看下Linux系统下是如何加载动态链接库有助于理解Android的动态库加载流程。

Linux环境下加载动态库主要包括如下函数,位于头文件dlfcn.h中:同样android的函数也位于dlfcn.h头文件中。

void *dlopen(const char *filename, int flag); //打开动态链接库

char *dlerror(void); //获取错误信息

void *dlsym(void *handle, const char *symbol); //获取方法指针

int dlclose(void *handle); //关闭动态链接库

用一个简单的C++代码,作为动态链接库包含计算相关的函数:(懒 使用的参考文章中demo)

extern "C"

int add(int a, int b) {

return a + b;

}

extern "C"

int mul(int a, int b) {

return a*b;

}

extern “C” 表示告诉编译器以C的方式编译,不要修改函数名,否则C++会修改函数名。

然后通过下述命令编译成动态链接库:

g++ -fPIC -shared caculate.cpp -o libcaculate.so

这样会在同级目录下生成一个动态库文件:libcaculate.so

然后编写加载动态库并使用的代码:

[main_call.cpp]

#include

#include

using namespace std;

static const char * const LIB_PATH = "./libcaculate.so";

typedef int (*CACULATE_FUNC)(int, int);

int main() {

void* symAdd = nullptr;

void* symMul = nullptr;

char* errorMsg = nullptr;

dlerror();

//1.打开动态库,拿到一个动态库句柄

void* handle = dlopen(LIB_PATH, RTLD_NOW);

if(handle == nullptr) {

cout << "load error!" << endl;

return -1;

}

// 查看是否有错误

if ((errorMsg = dlerror()) != nullptr) {

cout << "errorMsg:" << errorMsg << endl;

return -1;

}

cout << "load success!" << endl;

//2.通过句柄和方法名获取方法指针地址

symAdd = dlsym(handle, "add");

if(symAdd == nullptr) {

cout << "dlsym failed!" << endl;

if ((errorMsg = dlerror()) != nullptr) {

cout << "error message:" << errorMsg << endl;

return -1;

}

}

//3.将方法地址强制类型转换成方法指针

CACULATE_FUNC addFunc = reinterpret_cast(symAdd);

//4.调用动态库中的方法

cout << "1 + 2 = " << addFunc(1, 2) << endl;

//5.通过句柄关闭动态库

dlclose(handle);

return 0;

}

主要就用到了上面的4个函数过程如下

1、打开动态库,拿到一个动态库句柄

2、通过句柄和方法名获取方法指针地址

3、将方法地址强制类型转换成方法指针

4、调用动态库中的方法

5、通过句柄关闭动态库。

中间会使用dlerror检测是否有错误。

有必要解释一下的是方法指针地址到方法指针的转换,为了方便这里定义了一个方法指针的别名:

typedef int (*CACULATE_FUNC)(int, int);

指明该方法接受两个int类型参数返回一个int值。

拿到地址之后强制类型转换成方法指针用于调用:

CACULATE_FUNC addFunc = reinterpret_cast(symAdd);

最后只要编译运行即可:

g++ -std=c++11 -ldl main_call.cpp -o main

.main

因为代码中使用了c++11标准新加的特性,所以编译的时候带上-std=c++11,另外使用了头文件dlfcn.h需要带上-ldl,编译生成的main文件即是二进制可执行文件,需要将动态库放在同级目录下执行。

上面就是Linux环境下创建动态库,加载并使用动态库的全部过程。

由于Android基于Linux系统,所有Android系统底层也是通过这种方式加载并使用动态库的。

Android 链接器Linker之前的工作

db073aac9086587d64726eeaafbf16dd.png

流程图来自参考的另一篇博客

下面从System.loadLibrary() 开始分析

public static void loadLibrary(String libname) {

//VMStack.getCallingClassLoader() 返回应用类加载器这里是:PathClassLoader

Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);

}

下面看* loadLibrary0()*

synchronized void loadLibrary0(ClassLoader loader, String libname) {

if (libname.indexOf((int)File.separatorChar) != -1) {

throw new UnsatisfiedLinkError(

"Directory separator should not appear in library name: " + libname);

}

String libraryName = libname;

if (loader != null) {

//findLibrary()返回的是库的全路径名,loader是PathClassLoader 最终会

//调用父类的findLibrary()方法。

String filename = loader.findLibrary(libraryName);

//这里可以通过Logger 来打印log 因为这时候 util.log是无法执行到这里

Logger logger = Logger.getLogger("lly");

logger.info("filename == "+filename);

logger.info("mapLibraryName == "+System.mapLibraryName(libraryName));

if (filename == null) {

// It's not necessarily true that the ClassLoader used

// System.mapLibraryName, but the default setup does, and it's

// misleading to say we didn't find "libMyLibrary.so" when we

// actually searched for "liblibMyLibrary.so.so".

throw new UnsatisfiedLinkError(loader + " couldn't find \"" +

System.mapLibraryName(libraryName) + "\"");

}

//装载动态库

String error = doLoad(filename, loader);

if (error != null) {

throw new UnsatisfiedLinkError(error);

}

return;

}

......

}

参数loader为Android的应用类加载器,它是PathClassLoader 类型的对象,继承自BaseDexClassLoader对象

public String findLibrary(String name) {

return pathList.findLibrary(name);

}

最终会调用DexPathList 的findLibrary()方法

public String findLibrary(String libraryName) {

//生成平台相关的库名称这里会返回libxxx.so

String fileName = System.mapLibraryName(libraryName);

for (Element element : nativeLibraryPathElements) {

//查找动态库返回的全路径名

String path = element.findNativeLibrary(fileName);

if (path != null) {

return path;

}

}

return null;

}

回到loadLibrary0(),有了动态库的全路径名就可以装载库了,下面看doLoad()。

private String doLoad(String name, ClassLoader loader) {

String librarySearchPath = null;

if (loader != null && loader instanceof BaseDexClassLoader) {

BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;

librarySearchPath = dexClassLoader.getLdLibraryPath();

}

// nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless

// of how many ClassLoaders are in the system, but dalvik doesn't support synchronized

// internal natives.

synchronized (this) {

return nativeLoad(name, loader, librarySearchPath);

}

}

nativeLoad最终调用Runtime.c中的Runtime_nativeLoad(),接着调用OpenjdkJvm.cc 中的 JVM_NativeLoad() ,最终会调用到 Java_vm_ext.cc 中的LoadNativeLibrary() so加载的过程主要在这个函数中完成,参照上面的Linux加载so的流程,我们分析下这个方法:

bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,

const std::string& path,

jobject class_loader,

jstring library_path,

std::string* error_msg){

//1、打开动态链接库

void* handle = android::OpenNativeLibrary(env,

runtime_->GetTargetSdkVersion(),

path_str,

class_loader,

library_path);

//这里是x86为兼容arm库文件采用的方案 使用houdini技术,在运行时动态转化指令集,从而实现对arm库的支持。

bool needs_native_bridge = false;

if (handle == nullptr) {

if (android::NativeBridgeIsSupported(path_str)) {

handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);

needs_native_bridge = true;

}

}

if (handle == nullptr) {

//检查错误信息

*error_msg = dlerror();

VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;

return false;

}

if (env->ExceptionCheck() == JNI_TRUE) {

LOG(ERROR) << "Unexpected exception:";

env->ExceptionDescribe();

env->ExceptionClear();

}

// Create a new entry.

// TODO: move the locking (and more of this logic) into Libraries.

bool created_library = false;

{

// Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.

std::unique_ptr new_library(

new SharedLibrary(env, self, path, handle, class_loader, class_loader_allocator));

MutexLock mu(self, *Locks::jni_libraries_lock_);

library = libraries_->Get(path);

if (library == nullptr) { // We won race to get libraries_lock.

library = new_library.release();

libraries_->Put(path, library);

created_library = true;

}

}

if (!created_library) {

LOG(INFO) << "WOW: we lost a race to add shared library: "

<< "\"" << path << "\" ClassLoader=" << class_loader;

return library->CheckOnLoadResult();

}

VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";

bool was_successful = false;

void* sym;

if (needs_native_bridge) {

library->SetNeedsNativeBridge();

}

//2、获取方法地址

sym = library->FindSymbol("JNI_OnLoad", nullptr);

if (sym == nullptr) {

VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";

was_successful = true;

} else {

// Call JNI_OnLoad. We have to override the current class

// loader, which will always be "null" since the stuff at the

// top of the stack is around Runtime.loadLibrary(). (See

// the comments in the JNI FindClass function.)

ScopedLocalRef old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));

self->SetClassLoaderOverride(class_loader);

VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";

typedef int (*JNI_OnLoadFn)(JavaVM*, void*);

//3、强制类型转换成函数指针

JNI_OnLoadFn jni_on_load = reinterpret_cast(sym);

//4、调用函数

int version = (*jni_on_load)(this, nullptr);

......

library->SetResult(was_successful);

return was_successful;

}

arm的so可以运行在x86的系统上原因就是因为这个分支:

if (handle == nullptr) {

if (android::NativeBridgeIsSupported(path_str)) {

handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);

needs_native_bridge = true;

}

}

当调用OpenNativeLibrary()打开so时会去读取so文件的信息,x86的头文件和arm的头文件信息是不一样的,所有在用x86的的手机上运行arm的so文件时handle为空,这时候会根据so文件的绝对路径来判断是否支持houdini,如果支持的话会用NativeBridgeLoadLibrary()

重新打开so文件,进行下一步操作。测试已知支持arm so文件的路径有:

/data/app/包名/lib/arm/libxxx.so

/system/priv-app/应用名称/lib/arm/libxxx.so

到这里其实我的问题已经解决了,关于为什么会去这些路径下找,由于Native Bridge不开源,是以so的方式提供的,没有办法跟进去,望知道的分享一下。

下面看下Android 链接器Linker的装载过程

a82f8a8083bad94eff9578496dfb4d28.png

其中会在load_library 读取ELF文件头以及一些段信息

static bool load_library(android_namespace_t* ns,

LoadTask* task,

LoadTaskList* load_tasks,

int rtld_flags,

const std::string& realpath){

......

if (!task->read(realpath.c_str(), file_stat.st_size)) {

soinfo_free(si);

task->set_soinfo(nullptr);

return false;

}

.......

return true;

}

看下Read方法

bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {

CHECK(!did_read_);

CHECK(!did_load_);

name_ = name;

fd_ = fd;

file_offset_ = file_offset;

file_size_ = file_size;

if (ReadElfHeader() &&

VerifyElfHeader() &&

ReadProgramHeaders() &&

ReadSectionHeaders() &&

ReadDynamicSection()) {

did_read_ = true;

}

__libc_format_log(ANDROID_LOG_DEBUG, "lly", "did_read_ == %d",did_read_);

return did_read_;

}

ReadElfHeader() : 读取ELF文件头信息

VerifyElfHeader() : 校验ELF(文件类型等)

ReadProgramHeaders() : 根据ELF文件头信息获取程序头表

ReadSectionHeaders() : 根据ELF文件头信息获取段头表

ReadDynamicSection() : 获取Dynamic Section的信息

常见的 has unexpected e_machine: 40 就是在 VerifyElfHeader()方法中提示的。

最后看下Native库的动态链接过程:

7de93d2297d8ff98b9cd8b0f816de97f.png

http://www.lbrq.cn/news/807823.html

相关文章:

  • 网站加载页模板/外贸推广方式
  • 做民宿的网站有哪些/网站历史权重查询
  • wordpress没用/seo排名优化
  • web网站交互设计工具/手机域名访问网站怎么进入
  • 网页制作作品/天津seo结算
  • 临沂高端网站建设/关键词排名推广软件
  • 网站开发概要设计/权威发布
  • 招聘网站续费怎么做分录/系统优化工具
  • 工贸一体化企业建设电子商务网站的误区/百度竞价推广是什么
  • 做网站跟app/市场推广的方法和规划
  • 互联网网站建设营销/网销是什么工作好做吗
  • 红色经典ppt模板免费下载/seo 技术优化
  • 网站宣传页/今日头条最新
  • 商派商城网站建设公司/深圳搜索引擎优化推广便宜
  • wordpress二次开发函数/seo渠道
  • 做排行榜的网站/短视频seo排名
  • 小网站大全/百度推广关键词规划师
  • 用织梦的网站怎么做推广/互联网营销师培训大纲
  • 亳州市建设工程质量监督站网站/广告网站留电话
  • 虚拟机做的网站怎么让外网访问不了网/seo网站推广批发
  • 怎样快速做网站/2021百度热搜年度榜
  • 安阳建设局网站/鱼头seo软件
  • 郑州人才市场网站/网络营销推广方案
  • 大连英文网站建设/广告关键词有哪些
  • 嘉兴装修公司做网站/百度关键词优化大师
  • 福建网站建设公/专业海外网站推广
  • 网站正在建设中 页面/关键词排名点击软件网站
  • 私人网站怎么注册/营销策划公司名字
  • 网站建设如果登录失败/公司企业网站模板
  • 长春做企业网站/免费网站做seo
  • Shopify Draggable + Vue 3 完整指南:打造现代化拖拽交互体验
  • DDD中的核心权衡:模型纯度与逻辑完整性
  • Docker网络技术深度研究与实战手册
  • Vue.js 指令系统完全指南:深入理解 v- 指令
  • 如何在 Ubuntu 24.04 或 22.04 Linux 上安装和运行 Redis 服务器
  • Mac m系列芯片安装node14版本使用nvm + Rosetta 2