现在的位置: 首页 > 综合 > 正文

android linker 浅析

2013年10月05日 ⁄ 综合 ⁄ 共 5114字 ⁄ 字号 评论关闭

  Android 的加载/链接器linker 主要用于实现共享库的加载与链接。它支持应用程序对库函数的隐式和显式调用。对于隐式调用,应用程序的编译与静态库大致相同,只是在静态链接的时候通过--dynamic-linker /system/bin/linker 指定动态链接器,(该信息将被存放在ELF文件的.interp节中,内核执行目标映像文件前将通过该信息加载并运行相应的解释器程序linker.)并链接相应的共享库。与ld.so不同的是,Linker目前没有提供Lazy Binding机制,所有外部过程引用都在映像执行之前解析。对于显式调用,可以同过linker中提供的接口dlopendlsym,dlerrordlclose来动态加载和链接共享库。

Android中的共享库和可执行映像都默认采用ELF格式的文件,其基本格式如下:


       每个ELF文件的开始部分都包含一个ELF头,其中包含了整个文件的基本信息,包括目标代码的格式,体系结构,各程序头或节头的偏移和大小,组织结构和访问权限等信息。

       程序头表包含了加载到内存中的各种段的索引及属性信息,它将告诉加载器如何加载映像。每个段中有包含了一个或几个节区,每个节区应是唯一的。无论是可执行程序还是共享库都包含以下几个的节区:

1. GOT表和PLT表:

       不同映像间的函数和数据引用都是通过它们实现的。GOT(全局偏移表)给出了映像中所有被引用符号(函数或变量)的值。每个普通PLT表项相当于一个函数的桩函数(stub),支持懒绑定的情况下,当发生对外部函数的调用时,程序会通过PLT表将控制交给动态连接器,后者解析出函数的绝对地址,修改GOT中相应的值,之后的调用将不再需要连接器的绑定。由于linker是不支持懒绑定的,所以在进程初始化时,动态链接器首先解析出外部过程引用的绝对地址,一次性的修改所有相应的GOT表项。对共享对象来说,由于GOTPLT节以及代码段和数据段之间的相对位置是固定的,所有引用都是基于一个固定地址(GOT)的偏移量,所以实现了PIC代码,重定位时只需要修改可写段中的GOT表。而可执行程序在连接过程中则可能发生对不可写段的修改。如果只读段和可写段不是以固定的相对位置加载的,那么在重定位是还需要修改所有指向GOT的指针。   

                                       

2. dynamic节:

       与重定位有关的基本目录结构,例如:

Dynamic section at offset 0x61014 contains 20 entries:

  Tag        Type                         Name/Value

 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

 0x0000000c (INIT)                             0xb8a8

 0x0000000d (FINI)                           0x555c4

 0x00000004 (HASH)                       0x8128

 0x00000005 (STRTAB)                       0xa004

 0x00000006 (SYMTAB)                     0x8aa4

 0x0000000a (STRSZ)                         2902 (bytes)

 0x0000000b (SYMENT)                     16 (bytes)

 0x00000015 (DEBUG)                                0x0

 0x00000003 (PLTGOT)                     0x710dc

 0x00000002 (PLTRELSZ)                     2464 (bytes)

 0x00000014 (PLTREL)                       REL

 0x00000017 (JMPREL)                       0xaf08

 0x00000011 (REL)                           0xae98

 0x00000012 (RELSZ)                        112 (bytes)

 0x00000013 (RELENT)                      8 (bytes)

3. dynsymdynstr节:

       与重定位有关的符号表和字符串表:

Symbol table '.dynsym' contains 69 entries:

   Num:    Value   Size  Type    Bind       Vis        Ndx   Name

     0:   00000000   0  NOTYPE  LOCAL   DEFAULT  UND

     ……

     6:  00002568    28  FUNC    GLOBAL  DEFAULT    7   __ashldi3

     7:  00000001    58  FUNC    GLOBAL  DEFAULT  UND  _ZNK7android7RefBase9decS

     8:  00000001    32  FUNC    GLOBAL  DEFAULT  UND  ioctl

     9:  00000001    18  FUNC    GLOBAL  DEFAULT  UND  _ZN7android7String8D1Ev

    10:  00000001    16  FUNC    GLOBAL  DEFAULT  UND  _ZNK7android8EventHub16ge

    11:  00000001    32  FUNC    GLOBAL  DEFAULT  UND  strerror

    12:  00003024     0  NOTYPE  GLOBAL  DEFAULT  ABS  __exidx_end

4. .rel.dyn.rel.plt节:

       .rel.dyn节的表项对应了出外部过程调用的符号以外的所有重定位对象,.rel.plt则对应所有外部过程调用的重定位信息。每个重定位项记录了符号的符号表索引,重定位的操作地址,重定位类型的信息(见3.3节)。重定位所在的节区往往与重定位类型有关,例如:

Relocation section '.rel.plt' at offset 0x2f08 contains 308 entries:

 Offset     Info      Type                  Sym.Value  Sym. Name

000710e8  00000116   R_ARM_JUMP_SLOT   0000b8d0   fileno

000710ec  00000216   R_ARM_JUMP_SLOT   0000b8dc   getpagesize

000710f0  00000316   R_ARM_JUMP_SLOT   0000b8e8   fputs

000710f4  00000416   R_ARM_JUMP_SLOT   0000b8f4   abort

000710f8  00000516   R_ARM_JUMP_SLOT   0000b900   __errno_location

 

Relocation section '.rel.dyn' at offset 0x2e98 contains 14 entries:

 Offset     Info      Type            Sym.Value  Sym. Name

000715b8  00001e15  R_ARM_GLOB_DAT    00071000   __fini_array_end

000715bc  00002f15  R_ARM_GLOB_DAT    00000000   __gmon_start__

000715c8  0000f515  R_ARM_GLOB_DAT    00071000   __fini_array_start

000715cc  00010015  R_ARM_GLOB_DAT    00071000   __init_array_end

000715d0  00012e15  R_ARM_GLOB_DAT    00071000   __init_array_start

00072a00  00002714  R_ARM_COPY         00072a00   __timezone

00072a04  00005514  R_ARM_COPY         00072a04   __daylight

 

       R_ARM_JUMP_SLOTR_ARM_GLOB_DAT属性的重定位地址一般位于GOT表,R_ARM_COPYR_ARM_ABS32属性的重定位一般位于.data节或.text节中。

Linker的加载与启动

       Linker是共享库的加载/链接器,也可以称为解释器(interpreter)。共享库以ELF文件的形式保存在文件系统中,核心的load_elf_binary会首先将其映像文件映射到内存,然后映射并执行其解释器也就是linker的代码。linker的代码段是进程间共享的,但数据段为各进程私有。

linker执行完后会自动跳转到目标映像的入口地址。

       /*in sys_execve->do_execve->search_binary_handler->load_elf_binary*/

       elf_entry = load_elf_interp(&loc->interp_elf_ex,interpreter,&interp_map_addr, load_bias);

       ………..

       start_thread(regs, elf_entry, bprm->p);       //start to execute linker

       android,linker代码的运行域由地址0xb0000100开始(see /bionic/linker/Android.mk),直接从_start开始执行。do_execve会预先将应用程序参数(argc,argv[],envcenvp[]还有一些"辅助向量(Auxiliary Vector)"等(see load_elf_binary>create_elf_tables)存放在分配好的用户空间堆栈中,通过堆栈将这些参数和指针(位于linux_binprm结构体bprm)传递给用户空间的目标进程。

       Linker会提取出它所需要的信息,例如目标映像中程序头表在用户空间的地址以及应用程序入口等。

       /*in __linker_init()*/

               /* extract information passed from the kernel */

    while(vecs[0] != 0){

        switch(vecs[0]){

        case AT_PHDR:

            si->phdr = (Elf32_Phdr*) vecs[1];

            break;

        case AT_PHNUM:

            si->phnum = (int) vecs[1];

            break;

        case AT_ENTRY:

            si->entry = vecs[1];                /*entry of the executable image.*/

            break;

        }

        vecs += 2;

}

加载依赖的共享库

Linker会首先调用__linker_init执行一段自举代码,完成其自身的初始化,初始化与目标映像相关的数据结构。Linker会首先调用alloc_info为目标映像分配一个soinfo结构体,它用于存放与映像文件有关的所有信息,这样可以使可执行映像与共享对象共享连接与重定位函数,后面的程序将通过soinfoflags域判断目标映像是共享库还是可执行程序。

    si = alloc_info(argv[0]);                  /*name of exe */

抱歉!评论已关闭.