安卓linker源码阅读01
---恢复内容开始---
感觉想看源码还是得先了解安卓的makefile,这样知道是怎么编译的参考
Android编译系统参考手册
http://android.cloudchou.com/build/core/main.php
深入浅出Android makefile
http://blog.csdn.net/lizzywu/article/details/12842031
参考:看雪 linker阅读笔记一
\bionic\linker\arch\arm\begin.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 在\bionic\linker\linker.cpp
第一行就用了一个类,跟过去
bionic\libc\private\KernelArgumentBlock.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阅读笔记一