Android Linker(二) Soinfo的创建-----动态库的加载链接

阅读源码版本:6.0.1

阅读工具:Android Studio 2.2.2  Notepad++

参考链接:

http://www.tuicool.com/articles/AfMZRbZ

http://docs.oracle.com/cd/E19253-01/819-7050/6n918j8nq/index.html#chapter6-71736

 

上层调用的终点找到了,之后就开始对加载链接的工作内容进行分析。

通过类对象对文件内容进行解析,对应文件为bionic/linker/linker_phdr.h/.cpp.

 

动态库的加载过程:

bool ElfReader::Load(constandroid_dlextinfo* extinfo) {
  return ReadElfHeader() &&
         VerifyElfHeader() &&
         ReadProgramHeader() &&
         ReserveAddressSpace(extinfo) &&
         LoadSegments() &&
         FindPhdr();
}

 

ELFReader提供外部接口函数调用私有函数对动态库文件进行操作,接下来对调用的私有函数进行分析。

 

bool ElfReader::ReadElfHeader()

没啥好说的,就是读取文件头部。

 

bool ElfReader::VerifyElfHeader()

相关文件:art\runtime\elf.h bionic\libc\kernel\uapi\linux\elf.h

校验头部信息,Android Linker只针对了部分属性做校验。

1、校验前4个字节对应7F 45 4C 46(\x7f,ELF)

2、根据获取设备系统位数校验动态库对应系统位数,第5个字节:1 32位 2(64位)

3、第六个字节对应:ELFDATA2LSB(0x1)

4、17~18字节对应文件类型,只能为ET_DYN(3)表示动态链接库

5、第21~24字节对应版本,只能为EV_CURRENT(1)

6、19~20字节对应机器架构,具体到机型来定,ARM对应0x28

可以看出,Linker只是简单的对头部信息做了校验,很多加固因此利用了这一特性对头部做了修改。

 

bool ElfReader::ReadProgramHeader()

操作程序段头部信息,读取头部表数目,计算头部表占据的页面大小,然后将程序头部表映射到内存中,并将内存地址赋值。

...

ElfW(Addr) page_min = PAGE_START(header_.e_phoff);//程序头部表在页边界对齐后的起始地址

ElfW(Addr) page_max = PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ElfW(Phdr))));//结束地址

ElfW(Addr) page_offset = PAGE_OFFSET(header_.e_phoff);//偏移地址

页面大小 = page_max  -  page_min 

 

 

bool ElfReader::ReserveAddressSpace(constandroid_dlextinfo* extinfo)

这里就是创建内存空间,用来加载所有segments。

 

调用phdr_table_get_load_size获取加载大小

phdr_table_get_load_size:遍历phdr表,获取最小虚拟地址和最大虚拟地址,相差就是加载大小。

 

mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -10);

之后调用mmap开始分配私有匿名内存映射,私有映射表示对该映射区域写入操作会产生一个映射文件的复制,做的任何修改都不会被写会原来的文件。

 

bool ElfReader::LoadSegments()

开始加载PT_Load段


中间会有一个对内存偏移和文件偏移的判断,如果内存偏移大于文件偏移,会将中间多出内存设为0


 

bool ElfReader::FindPhdr()

查找PT_PHDR段是否存在,存在直接使用该段的地址,不存在就查找第一个可加载段。

 

动态库整个加载过程大致如此了,对后面两个函数的详细过程可能需要好好理解。

 

动态库的链接过程:

ELFReader对象创建完成后,开始创建新的soinfo对象。

 

对应文件bionic/linker/linker.cpp

soinfo* si = soinfo_alloc(realpath.c_str(), &file_stat, file_offset, rtld_flags);

 

开始进行动态链接:

si->prelink_image()

 

bool soinfo::prelink_image()

调用phdr_table_get_dynamic_section函数,查找存在的动态节区,没有dynamic就设置为空。

 

遍历动态链接节区,根据标记值进行相应操作。 

初始化完成,开始做解析、重定位操作

调用prelink_image函数

该函数主要是获取PT_DYNAMIC段,然后解析并保存相关内容,包括了init_array、string表、system table表等等,这是linker比较关键的一步操作。

for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {

    .....

}

 

还有对arm会解析异常处理段

 

bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,const android_dlextinfo* extinfo)

返回到find_libraries函数中,获取到soinfo结构对象,做相应判断操作后调用link_image函数,对上面获取信息进行处理,进行重定位,定位涉及了动态链接段解析的相关参数,这里简单标注下。

 

 

->->void soinfo::call_constructors()

紧接着调用了call_constructors函数,该函数会判断是否首次加载,若首次加载则会获取所有依赖动态库接着就开始执行init


 

至此,linker的工作基本完成。

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值