深入剖析Linux动态库在内存的装载

这是之前写的一个总结,现在回过头来看还是有一些收获,可以自己写一个动态库来进行测试,本文是以我自己写的动态库来进行测试的,使用工具readelf。

 

.so文件是elf格式文件中的一种,它遵循elf格式的相关规则,在对so文件进行内存装载时我们先学习一下elf格式中的相关内容(这里只是做基础的介绍,较为详细的内容可以参考《ELF文件格式分析》,也可以查阅网络资料,不过大多数网络资料都是从这里面提取的)。

一、可执行链接格式

可执行链接格式(Executable and Linking Format)最初是由 UNIX 系统实验室(UNIX System Laboratories,USL)开发并发布的,作为应用程序二进制接口(Application Binary Interface,ABI)的一部分。

目标文件有三种类型:

  • 可重定位文件(Relocatable File) .o)包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。
  • 可执行文件(Executable File) .exe) 包含适合于执行的一个程序,此文件规定了exec() 如何创建一个程序的进程映像。
  • 共享目标文件(Shared Object File) .so) 包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理, 生成另外一个目标文件。其次动态链接器(Dynamic Linker)可能将它与某 个可执行文件以及其它共享目标一起组合,创建进程映像。

目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。

 

1、ELF文件格式

ELF文件格式排布如下图1所示,分为链接视图和执行视图,我们的so共享库文件对应着链接视图来进行分析即可

 

图1 ELF文件格式

从图1中可以看出链接视图主要分为ELF头部、程序头部表、节区以及节区头部表。这些内容的定义可以在文件目录中的elf.h中看到,这里不加赘述,过多赘述,直接进入主题,来了解动态库在内存中的装载(以libcac.so为例)。

二、确定装载内容

一个so文件中有这么多的内容格式,哪些才是需要被我们装载的内容呢,通过分析dlopen的源码。

https://blog.csdn.net/SweeNeil/article/details/83744843

https://blog.csdn.net/SweeNeil/article/details/83749186

在其中发现真正被加载进内存的是Program Header对应的内容,而且该内容在Program Header中对应的type类型还应该为:PT_LOAD

通过对我们编写的程序的运行(elfso.c)可以看到这部分如下图2所示,红色方框中标识的type为1即表示为type类型为PT_LOAD这是需要被加载进内存的。

 

图2 libcac.so程序头内容

同样的也可以使用readelf工具来进行相关内容的查看,使用readelf查看获得的内容如下图3所示,红色方框中的内容就是需要被加载到内存中的,他们的type类型都为LOAD,可以看到使用readelf工具查看得到的信息和我们自己写的程序得到的信息是一致的。

 

图3 readelf查看libcac.so中程序头的内容

在标志为PT_LOAD的程序头中,它包含了需要映射的内容在文件中的位置以及映射的相关内容(这些内容可以再elf.h文件中程序头的定义中得到)。

三、动态库在内存中的装载

1、加载所有需要加载到内存的内容

通过上述第二节,知道了需要被加载进内存的内容,那么要如何加载呢,首先分析dlopen中的加载进内存的方式。

部分代码如下:

struct loadcmd

{

     ElfW(Addr) mapstart, mapend, dataend, allocend;

     off_t mapoff;

     int prot;

} loadcmds[l->l_phnum], *c;

size_t nloadcmds = 0;

 

case PT_LOAD:

c = &loadcmds[nloadcmds++];

c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);

c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1));

c->dataend = ph->p_vaddr + ph->p_filesz;

c->allocend = ph->p_vaddr + ph->p_memsz;

c->mapoff = ph->p_offset & ~(GLRO(dl_pagesize) - 1);

if (nloadcmds > 1 && c[-1].mapend != c->mapstart)

has_holes = true;

         ……

maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;

l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,

                                     c->prot,

                                     MAP_COPY|MAP_FILE,

                                     fd, c->mapoff);

l->l_map_end = l->l_map_start + maplength;

l->l_addr = l->l_map_start - c->mapstart;

 

__mprotect ((caddr_t) (l->l_addr + c->mapend),

                         loadcmds[nloadcmds - 1].mapstart - c->mapend,

                         PROT_NONE);

l->l_map_start = c->mapstart + l->l_addr;

l->l_map_end = l->l_map_start + maplength;

l->l_contiguous = !has_holes;

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值