android|linker加载so

文章记录了linker加载so的部分逻辑,记录重要源码部分。

0x1 so的加载

native 层的调用的过程是

JavaVMExt::LoadNativeLibrary
OpenNativeLibrary
NativeLoaderNamespace::Load
android_dlopen_ext
__loader_android_dlopen_ext
dlopen_ext
do_dlopen 关键位置 加载so 并执行so 构造函数

定位关键函数

do_dlopen

void* do_dlopen(const char* name, int flags,
const android_dlextinfo* extinfo,
const void* caller_addr)
{
std::string trace_prefix = std::string("dlopen: ") + (name == nullptr ? "(nullptr)" : name);
ScopedTrace trace(trace_prefix.c_str());
ScopedTrace loading_trace((trace_prefix + " - loading and linking").c_str());
soinfo* const caller = find_containing_library(caller_addr);
android_namespace_t* ns = get_caller_namespace(caller);

// ...
const char* translated_name = name;
// ...
ProtectedDataGuard guard;
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller); // 寻找并加载so 返回soinfo
loading_trace.End();

if (si != nullptr) {
void* handle = si->to_handle();
LD_LOG(kLogDlopen,
"... dlopen calling constructors: realpath="%s", soname="%s", handle=%p",
si->get_realpath(), si->get_soname(), handle);
si->call_constructors(); // 调用so init initarray 方法
failure_guard.Disable();
LD_LOG(kLogDlopen,
"... dlopen successful: realpath="%s", soname="%s", handle=%p",
si->get_realpath(), si->get_soname(), handle);
return handle;
}

return nullptr;
}

通过find_library加载so,si->call_constructors()[调用so 的init initarray 方法](#0x3 调用so init initarray 方法)

find_library

static soinfo* find_library(android_namespace_t* ns,
const char* name, int rtld_flags,
const android_dlextinfo* extinfo,
soinfo* needed_by)
{
soinfo* si = nullptr;

if (name == nullptr) {
si = solist_get_somain();
} else if (!find_libraries(ns,
needed_by,
&name,
1,
&si,
nullptr,
0,
rtld_flags,
extinfo,
false /* add_as_children */)) {
if (si != nullptr) {
soinfo_unload(si);
}
return nullptr;
}

si->increment_ref_count();

return si;
}

此函数调用find_libraries,该函数获取一个soinfo结构体;

find_libraries

bool find_libraries(android_namespace_t* ns,
soinfo* start_with,
const char* const library_names[],
size_t library_names_count,
soinfo* soinfos[],
std::vector<soinfo*>* ld_preloads,
size_t ld_preloads_count,
int rtld_flags,
const android_dlextinfo* extinfo,
bool add_as_children,
std::vector<android_namespace_t*>* namespaces)
{
// Step 0: prepare.
std::unordered_map<const soinfo*, ElfReader> readers_map;
LoadTaskList load_tasks;

for (size_t i = 0; i < library_names_count; ++i) {
const char* name = library_names[i];
load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
}

// ...

if (soinfos == nullptr) { // 分配soinfos 空间
size_t soinfos_size = sizeof(soinfo*)*library_names_count;
soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size));
memset(soinfos, 0, soinfos_size);
}
//...

ZipArchiveCache zip_archive_cache;
soinfo_list_t new_global_group_members;

// Step 1: expand the list of load_tasks to include all DT_NEEDED libraries (do not load them just yet)
//获取load_tasks列表以包含所有的DT_NEEDED库(暂时不要加载它们)
for (size_t i = 0; i<load_tasks.size(); ++i) {
LoadTask* task = load_tasks[i];
soinfo* needed_by = task->get_needed_by();

bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
task->set_extinfo(is_dt_needed ? nullptr : extinfo);
task->set_dt_needed(is_dt_needed);

// ...
android_namespace_t* start_ns = const_cast<android_namespace_t*>(task->get_start_from());

LD_LOG(kLogDlopen, "find_library_internal(ns=%s@%p): task=%s, is_dt_needed=%d",
start_ns->get_name(), start_ns, task->get_name(), is_dt_needed);
// 加载so库
if (!find_library_internal(start_ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) {
return false;
}

soinfo* si = task->get_soinfo();

// ...

for (auto&& task : load_list) {
address_space_params* address_space =
(reserved_address_recursive || !task->is_dt_needed()) ? &extinfo_params : &default_params;
if (!task->load(address_space)) { // 加载PT_LOAD 段
return false;
}
}

// Step 3: pre-link all DT_NEEDED libraries in breadth first order.
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
if (!si->is_linked() && !si->prelink_image()) {// 动态链接 so init initarray 等函数
return false;
}
register_soinfo_tls(si);
}

//...
}

保留了部分重要代码,代码主要作用

  • 通过find_library_internal加载so
  • 通过task->load(address_space)[加载PT_LOAD 段](#加载PT_LOAD 段)
  • 通过si->prelink_image()[动态链接 so](#0x2 so的链接) 给init initarray 等变量赋值

继续跟进find_library_internal

find_library_internal

static bool find_library_internal(android_namespace_t* ns,
LoadTask* task,
ZipArchiveCache* zip_archive_cache,
LoadTaskList* load_tasks,
int rtld_flags)
{
soinfo* candidate;
// 查询so是否已经加载过
if (find_loaded_library_by_soname(ns, task->get_name(), true /* search_linked_namespaces */,
&candidate)) {
LD_LOG(kLogDlopen,
"find_library_internal(ns=%s, task=%s): Already loaded (by soname): %s",
ns->get_name(), task->get_name(), candidate->get_realpath());
task->set_soinfo(candidate);
return true;
}

// Library might still be loaded, the accurate detection
// of this fact is done by load_library.
TRACE("[ "%s" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder... ]",
task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);

// 说明该so没有被加载,就调用此函数进行加载
if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
true /* search_linked_namespaces */)) {
return true;
}

// ...
}

检测so是否被加载过,没有被加载调用load_library进行加载

load_library

static bool load_library(android_namespace_t* ns,
LoadTask* task,
ZipArchiveCache* zip_archive_cache,
LoadTaskList* load_tasks,
int rtld_flags,
bool search_linked_namespaces)
{
const char* name = task->get_name();
soinfo* needed_by = task->get_needed_by();
const android_dlextinfo* extinfo = task->get_extinfo();

if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
//...

task->set_fd(extinfo->library_fd, false);
task->set_file_offset(file_offset);
// 加载so
return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}

// Open the file.
off64_t file_offset;
std::string realpath;
int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
// ...

task->set_fd(fd, true);
task->set_file_offset(file_offset);
// 加载so
return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
}

删除不相干代码,发现每条分支都会调用load_library加载so,继续深入。

load_library函数是整个so加载过程非常重要的函数!它创建了soinfo

static bool load_library(android_namespace_t* ns,
LoadTask* task,
LoadTaskList* load_tasks,
int rtld_flags,
const std::string& realpath,
bool search_linked_namespaces)
{
off64_t file_offset = task->get_file_offset();
const char* name = task->get_name();
const android_dlextinfo* extinfo = task->get_extinfo();

// ...

soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags); // 分配soinfo结构体

task->set_soinfo(si);

// Read the ELF header and some of the segments.
// 读取elf header 和 一些段信息
if (!task->read(realpath.c_str(), file_stat.st_size)) {
task->remove_cached_elf_reader();
task->set_soinfo(nullptr);
soinfo_free(si);
return false;
}

// ...

return true;
}

跟踪task->read最后会进入ElfReader

bionic/linker/linker_phdr.cpp

bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
if (did_read_) {
return true;
}
name_ = name;
fd_ = fd;
file_offset_ = file_offset;
file_size_ = file_size;

if (ReadElfHeader() && // 读取elf头
VerifyElfHeader() && // 验证elf头
ReadProgramHeaders() && // 读取程序头
ReadSectionHeaders() && // 读取节的头
ReadDynamicSection() && // 读取 Dynamic 节 为后面的so链接做准备
ReadPadSegmentNote()) { // 读取 pd_pad 段数据
did_read_ = true;
}

return did_read_;
}

Read函数主要是对elf文件头校验和将ProgramHeadersSectionHeaders,DynamicSection等映射到内存中

跟踪这个几个函数前先属下一下几个结构体,接下来的函数会使用到。注释是使用gpt翻译的

struct Elf64_Ehdr {
unsigned char e_ident[16]; // 用于存储 ELF 文件的标识信息。这通常包括一个魔数(magic number)用于标识这是一个 ELF 文件。
Elf64_Half e_type; //这个字段表示 ELF 文件的类型。例如,它可能是一个可执行文件、共享库、重定位文件等
Elf64_Half e_machine; // 目标架构
Elf64_Word e_version; // ELF 文件的版本
Elf64_Addr e_entry; //程序开始执行的虚拟地址
Elf64_Off e_phoff;//程序头部(program header)在文件中的偏移量
Elf64_Off e_shoff; //节区头部(section header)在文件中的偏移量
Elf64_Word e_flags; //一些与文件相关的标志位
Elf64_Half e_ehsize; // ELF 文件头部的大小
Elf64_Half e_phentsize;//程序头部中每个条目的大小
Elf64_Half e_phnum; //程序头部中的条目数量
Elf64_Half e_shentsize; // 节区头部中每个条目的大小
Elf64_Half e_shnum; //节区头部中的条目数量。
Elf64_Half e_shstrndx; // 指向节区头部中的字符串表节区(section)
};
struct Elf64_Phdr {
Elf64_Word p_type; // 表示 段的类型
Elf64_Word p_flags; // 段相关的标志位
Elf64_Off p_offset; // 段在文件中的偏移量
Elf64_Addr p_vaddr; // 段在内存中的虚拟地址
Elf64_Addr p_paddr; // 物理地址
Elf64_Xword p_filesz; // 段在文件中的大小
Elf64_Xword p_memsz; // 段在内存中的大小
Elf64_Xword p_align; // 对齐约束
};
struct Elf64_Shdr {
Elf64_Word sh_name; // 节(section)的名称索引
Elf64_Word sh_type; // 节(section)的类型
Elf64_Xword sh_flags; // 节的属性标志
Elf64_Addr sh_addr; // 节的虚拟地址
Elf64_Off sh_offset; // 节在文件中的偏移量
Elf64_Xword sh_size; // 节的大小
Elf64_Word sh_link; // 节的链接。这个值的意义取决于节的类型。例如,对于符号表节,sh_link 可能表示关联的字符串表节的索引。
Elf64_Word sh_info; // 节的附加信息。其意义也取决于节的类型。例如,对于符号表节,sh_info 可能表示符号表中的条目数量。
Elf64_Xword sh_addralign; // 节的地址对齐约束
Elf64_Xword sh_entsize;// 如果节包含固定大小的条目(例如,符号表),那么这个字段表示每个条目的大小
};

// Dynamic table entry for ELF64.
// 在 ELF 文件的 .dynsym、.dynstr、.hash、.gnu_hash、.dynamic、.got 等节中,这些结构体条目被用来表示符号表、字符串表、动态符号表、重定位信息等
struct Elf64_Dyn
{

Elf64_Sxword d_tag; // 动态表条目的类型
union
{
Elf64_Xword d_val; // 条目的整数值。
Elf64_Addr d_ptr; // 条目的指针值。
} d_un;
};

ReadProgramHeaders

bool ElfReader::ReadProgramHeaders() {
phdr_num_ = header_.e_phnum;

// Like the kernel, we only accept program header tables that
// are smaller than 64KiB.
if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) {
DL_ERR(""%s" has invalid e_phnum: %zd", name_.c_str(), phdr_num_);
return false;
}

// Boundary checks
size_t size = phdr_num_ * sizeof(ElfW(Phdr));
if (!CheckFileRange(header_.e_phoff, size, alignof(ElfW(Phdr)))) {
DL_ERR_AND_LOG(""%s" has invalid phdr offset/size: %zu/%zu",
name_.c_str(),
static_cast<size_t>(header_.e_phoff),
size);
return false;
}
// 使用mmap将program headers 映射到内存
if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, size)) {
DL_ERR(""%s" phdr mmap failed: %s", name_.c_str(), strerror(errno));
return false;
}

phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data());// 保存 program headers地址 到全局
return true;
}
bool ElfReader::ReadSectionHeaders() {
shdr_num_ = header_.e_shnum;

//...

size_t size = shdr_num_ * sizeof(ElfW(Shdr));
if (!CheckFileRange(header_.e_shoff, size, alignof(const ElfW(Shdr)))) {
DL_ERR_AND_LOG(""%s" has invalid shdr offset/size: %zu/%zu",
name_.c_str(),
static_cast<size_t>(header_.e_shoff),
size);
return false;
}
// 使用mmap将section headers 映射到内存
if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, size)) {
DL_ERR(""%s" shdr mmap failed: %s", name_.c_str(), strerror(errno));
return false;
}

shdr_table_ = static_cast<const ElfW(Shdr)*>(shdr_fragment_.data()); // 保存 section headers地址到全局
return true;
}
bool ElfReader::ReadDynamicSection() {
// 1. Find .dynamic section (in section headers)
const ElfW(Shdr)* dynamic_shdr = nullptr;
for (size_t i = 0; i < shdr_num_; ++i) {
if (shdr_table_[i].sh_type == SHT_DYNAMIC) {
dynamic_shdr = &shdr_table_ [i];//
break;
}
}

// ...

// 获取字符串节结构体
const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link];

if (strtab_shdr->sh_type != SHT_STRTAB) {
DL_ERR_AND_LOG(""%s" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
return false;
}

if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, alignof(const ElfW(Dyn)))) {
DL_ERR_AND_LOG(""%s" has invalid offset/size of .dynamic section", name_.c_str());
return false;
}
// 将dynamic节 对应的数据体数据映射到内存
if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
DL_ERR(""%s" dynamic section mmap failed: %s", name_.c_str(), strerror(errno));
return false;
}
// 将dynamic节的地址保存到全局
dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data());

if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size, alignof(const char))) {
DL_ERR_AND_LOG(""%s" has invalid offset/size of the .strtab section linked from .dynamic section",
name_.c_str());
return false;
}
// 将符号表数据并映射到内存
if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
DL_ERR(""%s" strtab section mmap failed: %s", name_.c_str(), strerror(errno));
return false;
}
// 将符号表地址保存到全局
strtab_ = static_cast<const char*>(strtab_fragment_.data());
strtab_size_ = strtab_fragment_.size();
return true;
}
加载PT_LOAD 段
  bool load(address_space_params* address_space) {
ElfReader& elf_reader = get_elf_reader();
if (!elf_reader.Load(address_space)) {
return false;
}

si_->base = elf_reader.load_start();
si_->size = elf_reader.load_size();
si_->set_mapped_by_caller(elf_reader.is_mapped_by_caller());
si_->load_bias = elf_reader.load_bias();
si_->phnum = elf_reader.phdr_count();
si_->phdr = elf_reader.loaded_phdr();
si_->set_gap_start(elf_reader.gap_start());
si_->set_gap_size(elf_reader.gap_size());
si_->set_should_pad_segments(elf_reader.should_pad_segments());

return true;
}

继续跟踪elf_reader.Load

ElfReader::Load

bool ElfReader::Load(address_space_params* address_space) {
CHECK(did_read_);
if (did_load_) {
return true;
}
bool reserveSuccess = ReserveAddressSpace(address_space); // 分配足够的空间
if (reserveSuccess && LoadSegments() && FindPhdr() && // 加载 pt_loader段
FindGnuPropertySection()) {
did_load_ = true;
#if defined(__aarch64__)
// For Armv8.5-A loaded executable segments may require PROT_BTI.
if (note_gnu_property_.IsBTICompatible()) {
did_load_ = (phdr_table_protect_segments(phdr_table_, phdr_num_, load_bias_,
&note_gnu_property_) == 0);
}
#endif
}
if (reserveSuccess && !did_load_) {
if (load_start_ != nullptr && load_size_ != 0) {
if (!mapped_by_caller_) {
munmap(load_start_, load_size_);
}
}
}

return did_load_;
}

继续跟踪LoadSegments

函数中会用到的一些方法

#define PAGE_SIZE 4096  // 一页的大小
#define PAGE_MASK (~(PAGE_SIZE - 1))

inline size_t page_size() {
#if defined(PAGE_SIZE)
return PAGE_SIZE;
#else
static const size_t page_size = getauxval(AT_PAGESZ);
return page_size;
#endif
}

constexpr size_t max_page_size() {
#if defined(PAGE_SIZE)
return PAGE_SIZE;
#else
return 65536;
#endif
}

// 返回包含地址'x'的页面的地址。
inline uintptr_t page_start(uintptr_t x) {
return x & ~(page_size() - 1);
}
// 返回地址'x'在其页面中的偏移量。
inline uintptr_t page_offset(uintptr_t x) {
return x & (page_size() - 1);
}

// 返回地址'x'之后的下一页地址,除非'x'本身位于页面的开头。
inline uintptr_t page_end(uintptr_t x) {
return page_start(x + page_size() - 1);
}

ElfReader::LoadSegments()

bool ElfReader::LoadSegments() {
for (size_t i = 0; i < phdr_num_; ++i) {
const ElfW(Phdr)* phdr = &phdr_table_[i];

if (phdr->p_type != PT_LOAD) { // pt_load 可执行段
continue;
}

// Segment addresses in memory.
// load_bias_ 是 加载到内存的地址和so文件地址的偏差值
ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
ElfW(Addr) seg_end = seg_start + phdr->p_memsz;

ElfW(Addr) seg_page_start = page_start(seg_start);
ElfW(Addr) seg_page_end = page_end(seg_end);

ElfW(Addr) seg_file_end = seg_start + phdr->p_filesz;

// File offsets.
ElfW(Addr) file_start = phdr->p_offset;
ElfW(Addr) file_end = file_start + phdr->p_filesz;

ElfW(Addr) file_page_start = page_start(file_start);
ElfW(Addr) file_length = file_end - file_page_start;

if (file_size_ <= 0) {
DL_ERR(""%s" invalid file size: %" PRId64, name_.c_str(), file_size_);
return false;
}

if (file_end > static_cast<size_t>(file_size_)) {
DL_ERR("invalid ELF file "%s" load segment[%zd]:"
" p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")",
name_.c_str(), i, reinterpret_cast<void*>(phdr->p_offset),
reinterpret_cast<void*>(phdr->p_filesz),
reinterpret_cast<void*>(file_end), file_size_);
return false;
}

if (file_length != 0) {
int prot = PFLAGS_TO_PROT(phdr->p_flags);
//...
// 将段映射到内存中
void* seg_addr = mmap64(reinterpret_cast<void*>(seg_page_start),
file_length,
prot,
MAP_FIXED|MAP_PRIVATE,
fd_,
file_offset_ + file_page_start); // file_offset_ 默认是 0
if (seg_addr == MAP_FAILED) {
DL_ERR("couldn't map "%s" segment %zd: %s", name_.c_str(), i, strerror(errno));
return false;
}

// Mark segments as huge page eligible if they meet the requirements
// (executable and PMD aligned).
if ((phdr->p_flags & PF_X) && phdr->p_align == kPmdSize &&
get_transparent_hugepages_supported()) {
madvise(seg_addr, file_length, MADV_HUGEPAGE);
}
}

// 如果段是可写的,并且不在页边界处结束,则零填充直到页限制。
if ((phdr->p_flags & PF_W) != 0 && page_offset(seg_file_end) > 0) {
memset(reinterpret_cast<void*>(seg_file_end), 0, page_size() - page_offset(seg_file_end));
}

seg_file_end = page_end(seg_file_end);

// Seg_file_end现在是文件内容之后的第一个页面地址。如果seg_end更大,我们需要将它们之间的任何内容归零。这是通过对所有额外页面使用私有匿名映射来实现的。
if (seg_page_end > seg_file_end) {
size_t zeromap_size = seg_page_end - seg_file_end;
void* zeromap = mmap(reinterpret_cast<void*>(seg_file_end),
zeromap_size,
PFLAGS_TO_PROT(phdr->p_flags),
MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
-1,
0);
if (zeromap == MAP_FAILED) {
DL_ERR("couldn't zero fill "%s" gap: %s", name_.c_str(), strerror(errno));
return false;
}

prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size, ".bss");
}
}
return true;
}

此部分代码逻辑就是将ELF中的可加载segments依次映射到内存中 至此so文件的读取、加载工作就分析完毕了

0x2 so的链接

如果一个目标文件参与动态链接,它的程序头部表将包含类型为 PT_DYNAMIC 的元素。此“段”包含.dynamic节区(这个节区是一个数组)

prelink_image

bool soinfo::prelink_image() {
if (flags_ & FLAG_PRELINKED) return true;
/* Extract dynamic section */
ElfW(Word) dynamic_flags = 0;
phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags); // 获取了dynamic节区信息

// ...

#if defined(__arm__)
(void) phdr_table_get_arm_exidx(phdr, phnum, load_bias,
&ARM_exidx, &ARM_exidx_count);
#endif

TlsSegment tls_segment;
if (__bionic_get_tls_segment(phdr, phnum, load_bias, &tls_segment)) {
if (!__bionic_check_tls_alignment(&tls_segment.alignment)) {
if (!relocating_linker) {
DL_ERR("TLS segment alignment in "%s" is not a power of 2: %zu",
get_realpath(), tls_segment.alignment);
}
return false;
}
tls_ = std::make_unique<soinfo_tls>();
tls_->segment = tls_segment;
}

// 从dynamic section提取有用信息。注意:“除了数组末尾的DT_NULL元素和DT_NEEDED元素的相对顺序外,表项可以以任何顺序出现。”
//
// source: http://www.sco.com/developers/gabi/1998-04-29/ch5.dynamic.html
uint32_t needed_count = 0;
for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { //linker采用遍历dynamic数组的方式,根据每个元素的flags()进行相应的处理
DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
switch (d->d_tag) {
case DT_SONAME:
// this is parsed after we have strtab initialized (see below).
break;

case DT_HASH:
nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
nchain_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
bucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
chain_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket_ * 4);
break;

case DT_GNU_HASH:
gnu_nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
// skip symndx
gnu_maskwords_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[2];
gnu_shift2_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[3];

gnu_bloom_filter_ = reinterpret_cast<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16);
gnu_bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
// amend chain for symndx = header[1]
gnu_chain_ = gnu_bucket_ + gnu_nbucket_ -
reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];

if (!powerof2(gnu_maskwords_)) {
DL_ERR("invalid maskwords for gnu_hash = 0x%x, in "%s" expecting power to two",
gnu_maskwords_, get_realpath());
return false;
}
--gnu_maskwords_;

flags_ |= FLAG_GNU_HASH;
break;

case DT_STRTAB:
strtab_ = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
break;

case DT_STRSZ:
strtab_size_ = d->d_un.d_val;
break;

case DT_SYMTAB:
symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
break;

case DT_SYMENT:
if (d->d_un.d_val != sizeof(ElfW(Sym))) {
DL_ERR("invalid DT_SYMENT: %zd in "%s"",
static_cast<size_t>(d->d_un.d_val), get_realpath());
return false;
}
break;

case DT_PLTREL:
#if defined(USE_RELA)
if (d->d_un.d_val != DT_RELA) {
DL_ERR("unsupported DT_PLTREL in "%s"; expected DT_RELA", get_realpath());
return false;
}
#else
if (d->d_un.d_val != DT_REL) {
DL_ERR("unsupported DT_PLTREL in "%s"; expected DT_REL", get_realpath());
return false;
}
#endif
break;

// ...

case DT_INIT: // init 函数赋值位置
init_func_ = reinterpret_cast<linker_ctor_function_t>(load_bias + d->d_un.d_ptr);
DEBUG("%s constructors (DT_INIT) found at %p", get_realpath(), init_func_);
break;

case DT_FINI:
fini_func_ = reinterpret_cast<linker_dtor_function_t>(load_bias + d->d_un.d_ptr);
DEBUG("%s destructors (DT_FINI) found at %p", get_realpath(), fini_func_);
break;

case DT_INIT_ARRAY: // init array 函数赋值位置
init_array_ = reinterpret_cast<linker_ctor_function_t*>(load_bias + d->d_un.d_ptr);
DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", get_realpath(), init_array_);
break;

case DT_INIT_ARRAYSZ:
init_array_count_ = static_cast<uint32_t>(d->d_un.d_val) / sizeof(ElfW(Addr));
break;

case DT_FINI_ARRAY:
fini_array_ = reinterpret_cast<linker_dtor_function_t*>(load_bias + d->d_un.d_ptr);
DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", get_realpath(), fini_array_);
break;

case DT_FINI_ARRAYSZ:
fini_array_count_ = static_cast<uint32_t>(d->d_un.d_val) / sizeof(ElfW(Addr));
break;

case DT_PREINIT_ARRAY:
preinit_array_ = reinterpret_cast<linker_ctor_function_t*>(load_bias + d->d_un.d_ptr);
DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", get_realpath(), preinit_array_);
break;

case DT_PREINIT_ARRAYSZ:
preinit_array_count_ = static_cast<uint32_t>(d->d_un.d_val) / sizeof(ElfW(Addr));
break;

case DT_TEXTREL:


default:
if (!relocating_linker) {
const char* tag_name;
if (d->d_tag == DT_RPATH) {
tag_name = "DT_RPATH";
} else if (d->d_tag == DT_ENCODING) {
tag_name = "DT_ENCODING";
} else if (d->d_tag >= DT_LOOS && d->d_tag <= DT_HIOS) {
tag_name = "unknown OS-specific";
} else if (d->d_tag >= DT_LOPROC && d->d_tag <= DT_HIPROC) {
tag_name = "unknown processor-specific";
} else {
tag_name = "unknown";
}
DL_WARN("Warning: "%s" unused DT entry: %s (type %p arg %p) (ignoring)",
get_realpath(),
tag_name,
reinterpret_cast<void*>(d->d_tag),
reinterpret_cast<void*>(d->d_un.d_val));
}
break;
}
}
// ...

// Validate each library's verdef section once, so we don't have to validate
// it each time we look up a symbol with a version.
if (!validate_verdef_section(this)) return false;

flags_ |= FLAG_PRELINKED;
return true;
}

获取了dynamic节区信息, linker采用遍历dynamic数组的方式,根据每个元素的flags()进行相应的处理,通过这个函数后就能通过soinfo结构体调用so中的initinit array 函数了

0x3 调用so init initarray 方法
void soinfo::call_constructors() {
if (constructors_called || g_is_ldd) {
return;
}
constructors_called = true;

if (!is_main_executable() && preinit_array_ != nullptr) {
// The GNU dynamic linker silently ignores these, but we warn the developer.
PRINT(""%s": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath());
}

get_children().for_each([] (soinfo* si) {
si->call_constructors(); // 调用子so 的构造方法
});

if (!is_linker()) {
bionic_trace_begin((std::string("calling constructors: ") + get_realpath()).c_str());
}

// 调用so 的构造方法 DT_INIT和DT_INIT_ARRAY
call_function("DT_INIT", init_func_, get_realpath());
call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());

if (!is_linker()) {
bionic_trace_end();
}
}

template <typename F>
static inline void call_array(const char* array_name __unused, F* functions, size_t count,
bool reverse, const char* realpath)
{
if (functions == nullptr) {
return;
}

TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath);

int begin = reverse ? (count - 1) : 0;
int end = reverse ? -1 : count;
int step = reverse ? -1 : 1;

for (int i = begin; i != end; i += step) {
TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]);
call_function("function", functions[i], realpath);
}

TRACE("[ Done calling %s for '%s' ]", array_name, realpath);
}

static void call_function(const char* function_name __unused,
linker_dtor_function_t function,
const char* realpath __unused)
{
if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
return;
}

TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
function();
TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
}


参考文资料: Android linker加载笔记:https://wooyun.js.org/drops/Android%20Linker%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.html

[完]


原文始发于微信公众号(逆向与采集):android|linker加载so

版权声明:admin 发表于 2024年3月1日 上午9:01。
转载请注明:android|linker加载so | CTF导航

相关文章