您的位置:首页 > 其它

安卓linker源码阅读01

2015-08-08 22:50 357 查看
---恢复内容开始---

感觉想看源码还是得先了解安卓的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(linker_addr);

ElfW(Phdr)* phdr = reinterpret_cast(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(reinterpret_cast(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(elf) + phdr->p_offset - phdr->p_vaddr;

}

}

return 0;

}

// return reinterpret_cast(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(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阅读笔记一
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: