安卓linker源码阅读01

---恢复内容开始---

感觉想看源码还是得先了解安卓的makefile,这样知道是怎么编译的

参考
Android编译系统参考手册
http://android.cloudchou.com/build/core/main.php
深入浅出Android makefile
http://blog.csdn.net/lizzywu/article/details/12842031

参考:看雪 linker阅读笔记一

ioniclinkerarcharmegin.s

ENTRY(_start)
mov r0, sp
bl __linker_init

/* linker init returns the _entry address in the main image */
mov pc, r0
END(_start)

调用 __linker_init函数,返回值为main函数入口地址

__linker_init 在ioniclinkerlinker.cpp

第一行就用了一个类,跟过去
bioniclibcprivateKernelArgumentBlock.h

从内核解析出来argc,argv,envp(程序环境变量),auxv(辅助向量,key:value的形式,类似python的字典)

unsigned long getauxval(unsigned long type, bool* found_match = NULL)
给key(参数type),查value(参数found_match),注意看注释:Similar to ::getauxval but doesn't require the libc global variables
去看看man手册,http://www.man7.org/linux/man-pages/man3/getauxval.3.html,一会儿能用到

ElfW(auxv_t)* v = auxv

ElfW(auxv_t) 就是下面这个结构,在elf.h

typedef struct {
__u32 a_type;
union {
__u32 a_val;
} a_un;
} Elf32_auxv_t;

ElfW()宏在link.h里:

define ElfW(type) Elf32_ ## type

加起来就是Elf32_auxv_t了

继续__linker_init

ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)>(linker_addr);
ElfW(Phdr)
phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);

看man手册,就知道是什么意思了。获取镜像基址,可执行文件的入口点,获取header指针,程序头执指针

继续__linker_init

soinfo linker_so("[dynamic linker]", nullptr);

linker_so.base = linker_addr;
linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
linker_so.dynamic = nullptr;
linker_so.phdr = phdr;
linker_so.phnum = elf_hdr->e_phnum;
linker_so.flags |= FLAG_LINKER;

======================================================================================
/* Returns the size of the extent of all the possibly non-contiguous

  • loadable segments in an ELF program header table. This corresponds
  • to the page-aligned size in bytes that needs to be reserved in the
  • process' address space. If there are no loadable segments, 0 is
  • returned.
  • If out_min_vaddr or out_max_vaddr are not null, they will be
  • set to the minimum and maximum addresses of pages to be reserved,
  • or 0 if there is nothing to load.
    */

/*
从ELF program header table中读取,返回不连续的可以被加载的segment
要在内存中保留的内存范围大小
*/

size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
ElfW(Addr)* out_min_vaddr,
ElfW(Addr)* out_max_vaddr) {
/*
实际上只用到了2个参数:
phdr_table
phdr_count
*/
ElfW(Addr) min_vaddr = UINTPTR_MAX;
ElfW(Addr) max_vaddr = 0;

bool found_pt_load = false;
//遍历所有的phdr,找到PT_LOAD标志的segment
for (size_t i = 0; i < phdr_count; ++i) {
const ElfW(Phdr)* phdr = &phdr_table[i];

if (phdr->p_type != PT_LOAD) {
  continue;
}
found_pt_load = true;

if (phdr->p_vaddr < min_vaddr) {
  min_vaddr = phdr->p_vaddr;
}

//phdr->p_vaddr 段的第一个字节将被放到内存中的虚拟地址
if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
max_vaddr = phdr->p_vaddr + phdr->p_memsz;
}
}
if (!found_pt_load) {
min_vaddr = 0;
}

//PAGE_START PAGE_END 页对齐的宏
min_vaddr = PAGE_START(min_vaddr);
max_vaddr = PAGE_END(max_vaddr);

if (out_min_vaddr != nullptr) {
*out_min_vaddr = min_vaddr;
}
if (out_max_vaddr != nullptr) {
*out_max_vaddr = max_vaddr;
}
return max_vaddr - min_vaddr;
}

===========================================

/* Compute the load-bias of an existing executable. This shall only

  • be used to compute the load bias of an executable or shared library
  • that was loaded by the kernel itself.
  • Input:
  • elf -> address of ELF header, assumed to be at the start of the file.
  • Return:
  • load bias, i.e. add the value of any p_vaddr in the file to get
  • the corresponding address in memory.
  • bias 偏移,此函数计算load的偏移。

/
static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)
elf) {
ElfW(Addr) offset = elf->e_phoff;

//phdr_table 第一个phdr phdr_end 最后一个phdr
const ElfW(Phdr)* phdr_table = reinterpret_cast<const ElfW(Phdr)>(reinterpret_cast<uintptr_t>(elf) + offset);
const ElfW(Phdr)
phdr_end = phdr_table + elf->e_phnum;

for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) {
if (phdr->p_type == PT_LOAD) {
return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr;
}
}
return 0;
}

// return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr;
//这句没看懂,elf文件基地址+程序头的文件偏移 - 程序头的内存偏移这是个啥
//这个就是计算一个文件头和虚拟内存偏移的差值,后面用的时候再加上这个虚拟内存值

继续__linker_init

if (!(linker_so.PrelinkImage() && linker_so.LinkImage(nullptr)))

===========================================
/* Return the address and size of the ELF file's .dynamic section in memory,

  • or null if missing.
  • Input:
  • phdr_table -> program header table
  • phdr_count -> number of entries in tables
  • load_bias -> load bias
  • Output:
  • dynamic -> address of table in memory (null on failure).
  • Return:
  • void
    /
    void phdr_table_get_dynamic_section(const ElfW(Phdr)
    phdr_table, size_t phdr_count,
    ElfW(Addr) load_bias, ElfW(Dyn)** dynamic) {
    dynamic = nullptr;
    for (const ElfW(Phdr)
    phdr = phdr_table, *phdr_limit = phdr + phdr_count; phdr < phdr_limit; phdr++) {
    if (phdr->p_type == PT_DYNAMIC) {
    dynamic = reinterpret_cast<ElfW(Dyn)>(load_bias + phdr->p_vaddr);
    return;
    }
    }
    }

//靠,这里又不懂了,原来返回的是一个PT_LOAD的segment的偏移(文件地址-内存地址),
//到了这儿,和一个PT_DYNAMIC的segmet加在一起,这是在干啥

bool soinfo::LinkImage(const android_dlextinfo* extinfo)函数中,重要的是
2184这几行的代码
if (plt_rel != nullptr) {
DEBUG("[ relocating %s plt ]", name);
if (Relocate(plt_rel, plt_rel_count)) { //这里1
return false;
}
}
if (rel != nullptr) {
DEBUG("[ relocating %s ]", name);
if (Relocate(rel, rel_count)) { //这里2
return false;
}

ifuncs不造是什么

然后到2229行

===========================================

===========================================

===========================================

===========================================

---恢复内容结束---

感觉想看源码还是得先了解安卓的makefile,这样知道是怎么编译的

参考
Android编译系统参考手册
http://android.cloudchou.com/build/core/main.php
深入浅出Android makefile
http://blog.csdn.net/lizzywu/article/details/12842031

看雪:linker阅读笔记一

原文地址:https://www.cnblogs.com/Lnju/p/4714109.html