├── src ├── CMakeLists.txt ├── include │ ├── utils.h │ ├── elf_loader.h │ ├── relocator.h │ ├── elf_reader.h │ ├── memory_manager.h │ ├── common.h │ └── soinfo_manager.h ├── main.cpp ├── elf_reader.cpp ├── utils.cpp ├── elf_loader.cpp ├── memory_manager.cpp ├── relocator.cpp └── soinfo_manager.cpp ├── LICENSE └── README.md /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25) 2 | project(ElfLoader) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | include_directories(include) 7 | 8 | set(SOURCES 9 | main.cpp 10 | elf_loader.cpp 11 | elf_reader.cpp 12 | memory_manager.cpp 13 | relocator.cpp 14 | soinfo_manager.cpp 15 | utils.cpp 16 | ) 17 | 18 | find_library(log-lib log) 19 | 20 | add_executable(elf_loader ${SOURCES}) 21 | 22 | target_link_libraries(elf_loader ${log-lib}) 23 | -------------------------------------------------------------------------------- /src/include/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | namespace Utils { 6 | bool safe_add(off64_t* out, off64_t a, size_t b); 7 | 8 | soinfo* get_soinfo(const char* so_name); 9 | 10 | void* getMapData(int fd, off64_t base_offset, size_t elf_offset, size_t size); 11 | 12 | ElfW(Addr) get_export_func(const char* path, const char* func_name); 13 | 14 | inline size_t page_start(size_t addr) { 15 | return addr & ~(PAGE_SIZE - 1); 16 | } 17 | 18 | inline size_t page_offset(size_t addr) { 19 | return addr & (PAGE_SIZE - 1); 20 | } 21 | } -------------------------------------------------------------------------------- /src/include/elf_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "elf_reader.h" 5 | #include "memory_manager.h" 6 | #include "soinfo_manager.h" 7 | #include "relocator.h" 8 | 9 | class ElfLoader { 10 | public: 11 | ElfLoader(); 12 | ~ElfLoader(); 13 | 14 | bool LoadLibrary(const char* path); 15 | 16 | void CallConstructors(); 17 | 18 | void* GetSymbol(const char* name); 19 | 20 | private: 21 | std::unique_ptr reader_; 22 | std::unique_ptr memory_manager_; 23 | std::unique_ptr soinfo_manager_; 24 | std::unique_ptr relocator_; 25 | 26 | soinfo* loaded_si_; 27 | }; -------------------------------------------------------------------------------- /src/include/relocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "soinfo_manager.h" 5 | 6 | class Relocator { 7 | public: 8 | Relocator(); 9 | ~Relocator(); 10 | 11 | bool RelocateImage(soinfo* si); 12 | 13 | bool LinkImage(soinfo* si); 14 | 15 | uint32_t gnu_hash(const char* name); 16 | unsigned elf_hash(const char* name); 17 | 18 | ElfW(Sym)* gnu_lookup(uint32_t hash, const char* name, soinfo* si); 19 | ElfW(Sym)* elf_lookup(unsigned hash, const char* name, soinfo* si); 20 | 21 | private: 22 | bool ProcessRelaRelocation(soinfo* si, const ElfW(Rela)* rela); 23 | 24 | ElfW(Addr) FindSymbolAddress(const char* name, soinfo* si); 25 | }; -------------------------------------------------------------------------------- /src/include/elf_reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | class ElfReader { 6 | public: 7 | ElfReader(); 8 | ~ElfReader(); 9 | 10 | bool Open(const char* path); 11 | bool Read(); 12 | void Close(); 13 | 14 | const ElfW(Ehdr)* GetHeader() const { return &header_; } 15 | const ElfW(Phdr)* GetProgramHeaders() const { return phdr_table_; } 16 | size_t GetProgramHeaderCount() const { return phdr_num_; } 17 | 18 | const char* GetPath() const { return path_.c_str(); } 19 | int GetFd() const { return fd_; } 20 | size_t GetFileSize() const { return file_size_; } 21 | void* GetMappedAddr() const { return mapped_file_; } 22 | 23 | private: 24 | bool ReadElfHeader(); 25 | bool ReadProgramHeaders(); 26 | bool VerifyElfHeader(); 27 | 28 | std::string path_; 29 | int fd_; 30 | size_t file_size_; 31 | off64_t file_offset_; 32 | 33 | void* mapped_file_; 34 | 35 | ElfW(Ehdr) header_; 36 | ElfW(Phdr)* phdr_table_; 37 | size_t phdr_num_; 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Yuuki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/include/memory_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | class MemoryManager { 6 | public: 7 | MemoryManager(); 8 | ~MemoryManager(); 9 | 10 | bool ReserveAddressSpace(const ElfW(Phdr)* phdr_table, size_t phdr_num); 11 | 12 | bool LoadSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num, 13 | void* mapped_file, size_t file_size); 14 | 15 | bool FindPhdr(const ElfW(Phdr)* phdr_table, size_t phdr_num); 16 | 17 | bool ProtectSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num); 18 | 19 | void* GetLoadStart() const { return load_start_; } 20 | size_t GetLoadSize() const { return load_size_; } 21 | ElfW(Addr) GetLoadBias() const { return load_bias_; } 22 | const ElfW(Phdr)* GetLoadedPhdr() const { return loaded_phdr_; } 23 | 24 | private: 25 | bool CheckPhdr(ElfW(Addr) loaded, const ElfW(Phdr)* phdr_table, size_t phdr_num); 26 | size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, 27 | size_t phdr_count, 28 | ElfW(Addr)* min_vaddr); 29 | 30 | void* load_start_; 31 | size_t load_size_; 32 | ElfW(Addr) load_bias_; 33 | const ElfW(Phdr)* loaded_phdr_; 34 | }; 35 | -------------------------------------------------------------------------------- /src/include/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define LOG_TAG "CustomLinker" 23 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 24 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 25 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 26 | 27 | #if defined(__LP64__) 28 | #define ELFW(what) ELF64_ ## what 29 | #else 30 | #define ELFW(what) ELF32_ ## what 31 | #endif 32 | 33 | #define PAGE_SIZE 4096 34 | #define PAGE_MASK (~(PAGE_SIZE - 1)) 35 | #define PAGE_START(addr) ((addr) & PAGE_MASK) 36 | #define PAGE_END(addr) PAGE_START((addr) + PAGE_SIZE - 1) 37 | #define PAGE_OFFSET(addr) ((addr) & (PAGE_SIZE - 1)) 38 | 39 | // 权限标志转换 40 | #define PFLAGS_TO_PROT(x) (((x) & PF_R) ? PROT_READ : 0) | \ 41 | (((x) & PF_W) ? PROT_WRITE : 0) | \ 42 | (((x) & PF_X) ? PROT_EXEC : 0) 43 | 44 | struct soinfo; 45 | class ElfReader; 46 | class MemoryManager; 47 | class Relocator; -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "elf_loader.h" 2 | 3 | int (*yuuki_test_func) (int, int) = nullptr; 4 | 5 | int main(int argc, char* argv[]) { 6 | 7 | if (argc < 2) { 8 | printf("Usage: %s \n", argv[0]); 9 | return 1; 10 | } 11 | 12 | LOGI("Starting custom linker for: %s", argv[1]); 13 | 14 | // 检查文件是否存在 15 | if (access(argv[1], F_OK) != 0) { 16 | LOGE("File does not exist: %s", argv[1]); 17 | return 1; 18 | } 19 | 20 | if (access(argv[1], R_OK) != 0) { 21 | LOGE("File is not readable: %s", argv[1]); 22 | return 1; 23 | } 24 | 25 | ElfLoader loader; 26 | if (loader.LoadLibrary(argv[1])) { 27 | printf("Successfully loaded %s\n", argv[1]); 28 | 29 | void* test_func = loader.GetSymbol("yuuki_test"); 30 | if (test_func) { 31 | printf("Found yuuki_test function at %p\n", test_func); 32 | yuuki_test_func = (int (*)(int, int)) test_func; 33 | 34 | // 测试函数调用 35 | printf("Testing function call: 1 + 1 = %d\n", yuuki_test_func(1, 1)); 36 | printf("Testing function call: 5 + 3 = %d\n", yuuki_test_func(5, 3)); 37 | } else { 38 | printf("Failed to find yuuki_test function\n"); 39 | } 40 | 41 | return 0; 42 | } else { 43 | printf("Failed to load %s\n", argv[1]); 44 | return 1; 45 | } 46 | } 47 | // logcat | grep "CustomLinker" 48 | // logcat | grep "TEST_SO" 49 | // ./data/local/tmp/elf_loader /storage/emulated/0/yuuki/test.so -------------------------------------------------------------------------------- /src/include/soinfo_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | // soinfo结构体定义(简化版) 6 | struct soinfo { 7 | const char* name; 8 | ElfW(Addr) base; 9 | size_t size; 10 | ElfW(Addr) load_bias; 11 | 12 | const ElfW(Phdr)* phdr; 13 | size_t phnum; 14 | 15 | ElfW(Addr) entry; 16 | 17 | // Dynamic段信息 18 | ElfW(Dyn)* dynamic; 19 | size_t dynamic_count; 20 | 21 | // 符号表相关 22 | const char* strtab; 23 | ElfW(Sym)* symtab; 24 | size_t nbucket; 25 | size_t nchain; 26 | uint32_t* bucket; 27 | uint32_t* chain; 28 | 29 | // 重定位相关 30 | ElfW(Rela)* plt_rela; 31 | size_t plt_rela_count; 32 | ElfW(Rela)* rela; 33 | size_t rela_count; 34 | 35 | // GNU hash 36 | size_t gnu_nbucket; 37 | uint32_t* gnu_bucket; 38 | uint32_t* gnu_chain; 39 | uint32_t gnu_maskwords; 40 | uint32_t gnu_shift2; 41 | ElfW(Addr)* gnu_bloom_filter; 42 | 43 | // 初始化函数 44 | void (*init_func)(); 45 | void (**init_array)(); 46 | size_t init_array_count; 47 | void (**fini_array)(); 48 | size_t fini_array_count; 49 | 50 | // 依赖库 51 | std::vector needed_libs; 52 | 53 | uint32_t flags; 54 | }; 55 | 56 | class SoinfoManager { 57 | public: 58 | SoinfoManager(); 59 | ~SoinfoManager(); 60 | 61 | soinfo* GetOrCreateSoinfo(const char* name); 62 | 63 | bool UpdateSoinfo(soinfo* si, MemoryManager* mm, ElfReader* reader); 64 | 65 | bool PrelinkImage(soinfo* si); 66 | 67 | soinfo* FindSoinfo(const char* name); 68 | soinfo* GetCurrentSoinfo(); 69 | 70 | private: 71 | bool ParseDynamic(soinfo* si); 72 | void ApplyRelaSections(soinfo* si); 73 | 74 | std::unordered_map> soinfo_map_; 75 | }; 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # soLoader 2 | 3 | ## 项目概述 4 | 本项目实现了一个自定义的动态链接器(linker),能够加载和运行 Android 平台的共享库(.so 文件)。通过此项目,你可以绕过系统默认的 linker,实现自定义的库加载逻辑。 5 | 6 | ## 功能特性 7 | ✅ 自定义 ELF 文件加载器 8 | 9 | ✅ 支持 Android 平台的 SO 文件加载 10 | 11 | ✅ 实现基本的重定位功能 12 | 13 | ✅ 支持符号查找和解析 14 | 15 | ## 演示 16 | 17 | 18 | 19 | https://github.com/user-attachments/assets/11c13db5-334e-4053-9ca3-a3d8baaccaae 20 | 21 | 22 | 23 | ## 使用示例 24 | ```c 25 | #include "elf_loader.h" 26 | 27 | int (*yuuki_test_func) (int, int) = nullptr; 28 | 29 | int main(int argc, char* argv[]) { 30 | 31 | if (argc < 2) { 32 | printf("Usage: %s \n", argv[0]); 33 | return 1; 34 | } 35 | 36 | LOGI("Starting custom linker for: %s", argv[1]); 37 | 38 | if (access(argv[1], F_OK)!= 0) { 39 | LOGE("File does not exist: %s", argv[1]); 40 | return 1; 41 | } 42 | 43 | if (access(argv[1], R_OK) != 0) { 44 | LOGE("File is not readable: %s", argv[1]); 45 | return 1; 46 | } 47 | 48 | ElfLoader loader; 49 | if (loader.LoadLibrary(argv[1])) { 50 | printf("Successfully loaded %s\n", argv[1]); 51 | 52 | void* test_func = loader.GetSymbol("yuuki_test"); 53 | if (test_func) { 54 | printf("Found yuuki_test function at %p\n", test_func); 55 | yuuki_test_func = (int (*)(int, int)) test_func; 56 | 57 | // 测试函数调用 58 | printf("Testing function call: 1 + 1 = %d\n", yuuki_test_func(1, 1)); 59 | printf("Testing function call: 5 + 3 = %d\n", yuuki_test_func(5, 3)); 60 | } else { 61 | printf("Failed to find yuuki_test function\n"); 62 | } 63 | 64 | return 0; 65 | } else { 66 | printf("Failed to load %s\n", argv[1]); 67 | return 1; 68 | } 69 | } 70 | 71 | ``` 72 | 73 | 74 | ## 实现原理 75 | 76 | 本项目主要实现了以下核心功能: 77 | 78 | 1. **ELF 文件解析**:解析 ELF 文件头、程序头、节区头等结构 79 | 80 | 2. **内存映射**:将 SO 文件按需映射到内存 81 | 82 | 3. **重定位处理**:处理各种重定位类型(R_ARM_JUMP_SLOT, R_ARM_GLOB_DAT 等) 83 | 84 | 4. **符号解析**:实现符号查找和解析逻辑 85 | 86 | 5. **依赖加载**:递归加载依赖的 SO 文件 87 | 88 | 89 | ## 应用场景 90 | 91 | - 插件化架构实现 92 | 93 | - 热修复技术 94 | 95 | - SO 文件保护与加固 96 | 97 | - 动态加载研究 98 | 99 | ## 注意事项 100 | 101 | 1. 本项目仅供学习和研究使用 102 | 103 | 2. 某些 Android 版本可能有兼容性问题 104 | 105 | 3. 复杂的 SO 文件可能需要额外的重定位支持 106 | 107 | 4. 生产环境使用需充分测试 108 | 109 | 110 | ## 参考 111 | https://github.com/ngiokweng/ng1ok-linker 112 | 113 | https://bbs.kanxue.com/thread-282316.htm 114 | 115 | https://cs.android.com/android/platform/superproject/main/+/main:bionic/linker/ 116 | -------------------------------------------------------------------------------- /src/elf_reader.cpp: -------------------------------------------------------------------------------- 1 | #include "elf_reader.h" 2 | #include 3 | 4 | ElfReader::ElfReader() : fd_(-1), file_size_(0), file_offset_(0), 5 | mapped_file_(nullptr), phdr_table_(nullptr), phdr_num_(0) { 6 | memset(&header_, 0, sizeof(header_)); 7 | } 8 | 9 | ElfReader::~ElfReader() { 10 | Close(); 11 | } 12 | 13 | bool ElfReader::Open(const char* path) { 14 | path_ = path; 15 | 16 | struct stat sb; 17 | fd_ = open(path, O_RDONLY | O_CLOEXEC); 18 | if (fd_ < 0) { 19 | LOGE("Cannot open %s: %s", path, strerror(errno)); 20 | return false; 21 | } 22 | 23 | if (fstat(fd_, &sb) < 0) { 24 | LOGE("Cannot stat %s: %s", path, strerror(errno)); 25 | close(fd_); 26 | fd_ = -1; 27 | return false; 28 | } 29 | 30 | file_size_ = sb.st_size; 31 | 32 | // 映射整个文件到内存 33 | mapped_file_ = mmap(nullptr, file_size_, PROT_READ, MAP_PRIVATE, fd_, 0); 34 | if (mapped_file_ == MAP_FAILED) { 35 | LOGE("Cannot mmap %s: %s", path, strerror(errno)); 36 | close(fd_); 37 | fd_ = -1; 38 | return false; 39 | } 40 | 41 | return true; 42 | } 43 | 44 | bool ElfReader::Read() { 45 | if (!ReadElfHeader()) { 46 | return false; 47 | } 48 | 49 | if (!VerifyElfHeader()) { 50 | return false; 51 | } 52 | 53 | if (!ReadProgramHeaders()) { 54 | return false; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | void ElfReader::Close() { 61 | if (mapped_file_ != nullptr && mapped_file_ != MAP_FAILED) { 62 | munmap(mapped_file_, file_size_); 63 | mapped_file_ = nullptr; 64 | } 65 | 66 | if (fd_ >= 0) { 67 | close(fd_); 68 | fd_ = -1; 69 | } 70 | 71 | if (phdr_table_ != nullptr) { 72 | free(phdr_table_); 73 | phdr_table_ = nullptr; 74 | } 75 | } 76 | 77 | bool ElfReader::ReadElfHeader() { 78 | if (file_size_ < sizeof(ElfW(Ehdr))) { 79 | LOGE("File too small for ELF header"); 80 | return false; 81 | } 82 | 83 | memcpy(&header_, mapped_file_, sizeof(header_)); 84 | return true; 85 | } 86 | 87 | bool ElfReader::VerifyElfHeader() { 88 | if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) { 89 | LOGE("Invalid ELF magic"); 90 | return false; 91 | } 92 | 93 | if (header_.e_ident[EI_CLASS] != ELFCLASS64) { 94 | LOGE("Not a 64-bit ELF file"); 95 | return false; 96 | } 97 | 98 | if (header_.e_machine != EM_AARCH64) { 99 | LOGE("Not an ARM64 ELF file"); 100 | return false; 101 | } 102 | 103 | if (header_.e_version != EV_CURRENT) { 104 | LOGE("Invalid ELF version"); 105 | return false; 106 | } 107 | 108 | if (header_.e_type != ET_DYN) { 109 | LOGE("Not a shared object"); 110 | return false; 111 | } 112 | 113 | LOGD("ELF Header: type=%d, machine=%d, entry=0x%lx, phoff=0x%lx, phnum=%d", 114 | header_.e_type, header_.e_machine, header_.e_entry, 115 | header_.e_phoff, header_.e_phnum); 116 | 117 | return true; 118 | } 119 | 120 | bool ElfReader::ReadProgramHeaders() { 121 | phdr_num_ = header_.e_phnum; 122 | 123 | if (phdr_num_ == 0) { 124 | LOGE("No program headers"); 125 | return false; 126 | } 127 | 128 | if (header_.e_phentsize != sizeof(ElfW(Phdr))) { 129 | LOGE("Invalid program header size"); 130 | return false; 131 | } 132 | 133 | size_t size = phdr_num_ * sizeof(ElfW(Phdr)); 134 | 135 | if (header_.e_phoff + size > file_size_) { 136 | LOGE("Program headers out of file bounds"); 137 | return false; 138 | } 139 | 140 | phdr_table_ = static_cast(malloc(size)); 141 | if (phdr_table_ == nullptr) { 142 | LOGE("Cannot allocate memory for program headers"); 143 | return false; 144 | } 145 | 146 | memcpy(phdr_table_, static_cast(mapped_file_) + header_.e_phoff, size); 147 | 148 | return true; 149 | } 150 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | namespace Utils { 4 | 5 | bool safe_add(off64_t* out, off64_t a, size_t b) { 6 | if (a < 0 || __builtin_add_overflow(a, b, out)) { 7 | return false; 8 | } 9 | return true; 10 | } 11 | 12 | soinfo* get_soinfo(const char* so_name) { 13 | typedef soinfo* (*FunctionPtr)(ElfW(Addr)); 14 | 15 | char line[1024]; 16 | ElfW(Addr) linker_base = 0; 17 | ElfW(Addr) so_addr = 0; 18 | 19 | FILE* fp = fopen("/proc/self/maps", "r"); 20 | if (!fp) { 21 | LOGE("Cannot open /proc/self/maps"); 22 | return nullptr; 23 | } 24 | 25 | while (fgets(line, sizeof(line), fp)) { 26 | if (strstr(line, "linker64") && !linker_base) { 27 | char* addr = strtok(line, "-"); 28 | linker_base = strtoull(addr, nullptr, 16); 29 | } else if (strstr(line, so_name) && !so_addr) { 30 | char* addr = strtok(line, "-"); 31 | so_addr = strtoull(addr, nullptr, 16); 32 | } 33 | 34 | if (linker_base && so_addr) { 35 | break; 36 | } 37 | } 38 | 39 | fclose(fp); 40 | 41 | if (!linker_base || !so_addr) { 42 | LOGE("Cannot find addresses"); 43 | return nullptr; 44 | } 45 | 46 | ElfW(Addr) func_offset = get_export_func("/system/bin/linker64", "find_containing_library"); 47 | if (!func_offset) { 48 | LOGE("Cannot find find_containing_library"); 49 | return nullptr; 50 | } 51 | 52 | ElfW(Addr) find_containing_library_addr = linker_base + func_offset; 53 | FunctionPtr find_containing_library = reinterpret_cast(find_containing_library_addr); 54 | 55 | return find_containing_library(so_addr); 56 | } 57 | 58 | void* getMapData(int fd, off64_t base_offset, size_t elf_offset, size_t size) { 59 | off64_t offset; 60 | if (!safe_add(&offset, base_offset, elf_offset)) { 61 | return nullptr; 62 | } 63 | 64 | off64_t page_min = page_start(offset); 65 | off64_t end_offset; 66 | if (!safe_add(&end_offset, offset, size)) { 67 | return nullptr; 68 | } 69 | if (!safe_add(&end_offset, end_offset, page_offset(offset))) { 70 | return nullptr; 71 | } 72 | 73 | size_t map_size = static_cast(end_offset - page_min); 74 | 75 | uint8_t* map_start = static_cast( 76 | mmap(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min)); 77 | 78 | if (map_start == MAP_FAILED) { 79 | return nullptr; 80 | } 81 | 82 | return map_start + page_offset(offset); 83 | } 84 | 85 | ElfW(Addr) get_export_func(const char* path, const char* func_name) { 86 | struct stat sb; 87 | int fd = open(path, O_RDONLY); 88 | if (fd < 0) { 89 | return 0; 90 | } 91 | 92 | if (fstat(fd, &sb) < 0) { 93 | close(fd); 94 | return 0; 95 | } 96 | 97 | void* base = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 98 | if (base == MAP_FAILED) { 99 | close(fd); 100 | return 0; 101 | } 102 | 103 | ElfW(Ehdr) header; 104 | memcpy(&header, base, sizeof(header)); 105 | 106 | size_t shdr_size = header.e_shnum * sizeof(ElfW(Shdr)); 107 | ElfW(Shdr)* shdr_table = static_cast(malloc(shdr_size)); 108 | memcpy(shdr_table, static_cast(base) + header.e_shoff, shdr_size); 109 | 110 | char* shstrtab = static_cast(base) + shdr_table[header.e_shstrndx].sh_offset; 111 | 112 | void* symtab = nullptr; 113 | char* strtab = nullptr; 114 | uint32_t symtab_size = 0; 115 | 116 | for (size_t i = 0; i < header.e_shnum; ++i) { 117 | const ElfW(Shdr)* shdr = &shdr_table[i]; 118 | char* section_name = shstrtab + shdr->sh_name; 119 | 120 | if (strcmp(section_name, ".symtab") == 0) { 121 | symtab = static_cast(base) + shdr->sh_offset; 122 | symtab_size = shdr->sh_size; 123 | } 124 | if (strcmp(section_name, ".strtab") == 0) { 125 | strtab = static_cast(base) + shdr->sh_offset; 126 | } 127 | 128 | if (strtab && symtab) { 129 | break; 130 | } 131 | } 132 | 133 | ElfW(Addr) result = 0; 134 | 135 | if (symtab && strtab) { 136 | ElfW(Sym)* sym_table = static_cast(symtab); 137 | int sym_num = symtab_size / sizeof(ElfW(Sym)); 138 | 139 | for (int i = 0; i < sym_num; i++) { 140 | const ElfW(Sym)* sym = &sym_table[i]; 141 | char* sym_name = strtab + sym->st_name; 142 | 143 | if (strstr(sym_name, func_name)) { 144 | result = sym->st_value; 145 | break; 146 | } 147 | } 148 | } 149 | 150 | free(shdr_table); 151 | munmap(base, sb.st_size); 152 | close(fd); 153 | 154 | return result; 155 | } 156 | 157 | } -------------------------------------------------------------------------------- /src/elf_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "elf_loader.h" 2 | 3 | ElfLoader::ElfLoader() : loaded_si_(nullptr) { 4 | reader_ = std::make_unique(); 5 | memory_manager_ = std::make_unique(); 6 | soinfo_manager_ = std::make_unique(); 7 | relocator_ = std::make_unique(); 8 | } 9 | 10 | ElfLoader::~ElfLoader() { 11 | } 12 | 13 | bool ElfLoader::LoadLibrary(const char* path) { 14 | LOGI("Loading library: %s", path); 15 | 16 | if (!reader_->Open(path)) { 17 | LOGE("Failed to open %s", path); 18 | return false; 19 | } 20 | 21 | if (!reader_->Read()) { 22 | LOGE("Failed to read ELF file"); 23 | return false; 24 | } 25 | 26 | if (!memory_manager_->ReserveAddressSpace(reader_->GetProgramHeaders(), 27 | reader_->GetProgramHeaderCount())) { 28 | LOGE("Failed to reserve address space"); 29 | return false; 30 | } 31 | 32 | if (!memory_manager_->LoadSegments(reader_->GetProgramHeaders(), 33 | reader_->GetProgramHeaderCount(), 34 | reader_->GetMappedAddr(), 35 | reader_->GetFileSize())) { 36 | LOGE("Failed to load segments"); 37 | return false; 38 | } 39 | 40 | if (!memory_manager_->FindPhdr(reader_->GetProgramHeaders(), 41 | reader_->GetProgramHeaderCount())) { 42 | LOGE("Failed to find program headers"); 43 | return false; 44 | } 45 | 46 | const char* basename = strrchr(path, '/'); 47 | basename = basename ? basename + 1 : path; 48 | loaded_si_ = soinfo_manager_->GetOrCreateSoinfo(basename); 49 | 50 | if (!soinfo_manager_->UpdateSoinfo(loaded_si_, memory_manager_.get(), reader_.get())) { 51 | LOGE("Failed to update soinfo"); 52 | return false; 53 | } 54 | 55 | if (!soinfo_manager_->PrelinkImage(loaded_si_)) { 56 | LOGE("Failed to prelink image"); 57 | return false; 58 | } 59 | 60 | if (!memory_manager_->ProtectSegments(reader_->GetProgramHeaders(), 61 | reader_->GetProgramHeaderCount())) { 62 | LOGE("Failed to protect segments"); 63 | return false; 64 | } 65 | 66 | if (!relocator_->LinkImage(loaded_si_)) { 67 | LOGE("Failed to link image"); 68 | return false; 69 | } 70 | 71 | reader_->Close(); 72 | 73 | LOGI("Successfully loaded %s", path); 74 | return true; 75 | } 76 | 77 | void ElfLoader::CallConstructors() { 78 | if (loaded_si_ == nullptr) { 79 | return; 80 | } 81 | 82 | LOGD("Constructors already called during linking"); 83 | } 84 | 85 | void* ElfLoader::GetSymbol(const char* name) { 86 | if (loaded_si_ == nullptr) { 87 | LOGE("loaded_si_ is null"); 88 | return nullptr; 89 | } 90 | 91 | if (name == nullptr) { 92 | LOGE("Symbol name is null"); 93 | return nullptr; 94 | } 95 | 96 | LOGD("Looking for symbol: %s", name); 97 | LOGD("soinfo state: symtab=%p, strtab=%p, gnu_bucket=%p, bucket=%p", 98 | loaded_si_->symtab, loaded_si_->strtab, loaded_si_->gnu_bucket, loaded_si_->bucket); 99 | 100 | if (loaded_si_->symtab != nullptr) { 101 | if (loaded_si_->gnu_bucket != nullptr) { 102 | LOGD("Trying GNU hash lookup for %s", name); 103 | uint32_t hash = relocator_->gnu_hash(name); 104 | LOGD("GNU hash for %s: 0x%x", name, hash); 105 | 106 | ElfW(Sym)* sym = relocator_->gnu_lookup(hash, name, loaded_si_); 107 | if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { 108 | ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias; 109 | LOGD("Found symbol %s via GNU hash: st_value=0x%lx, load_bias=0x%lx, final_addr=0x%lx", 110 | name, sym->st_value, loaded_si_->load_bias, addr); 111 | 112 | if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) { 113 | return reinterpret_cast(addr); 114 | } else { 115 | LOGE("Symbol %s address 0x%lx out of range [0x%lx, 0x%lx)", 116 | name, addr, loaded_si_->base, loaded_si_->base + loaded_si_->size); 117 | } 118 | } else { 119 | LOGD("Symbol %s not found via GNU hash", name); 120 | } 121 | } 122 | 123 | if (loaded_si_->bucket != nullptr) { 124 | LOGD("Trying ELF hash lookup for %s", name); 125 | unsigned hash = relocator_->elf_hash(name); 126 | LOGD("ELF hash for %s: 0x%x", name, hash); 127 | 128 | ElfW(Sym)* sym = relocator_->elf_lookup(hash, name, loaded_si_); 129 | if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { 130 | ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias; 131 | LOGD("Found symbol %s via ELF hash: st_value=0x%lx, load_bias=0x%lx, final_addr=0x%lx", 132 | name, sym->st_value, loaded_si_->load_bias, addr); 133 | 134 | if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) { 135 | return reinterpret_cast(addr); 136 | } else { 137 | LOGE("Symbol %s address 0x%lx out of range [0x%lx, 0x%lx)", 138 | name, addr, loaded_si_->base, loaded_si_->base + loaded_si_->size); 139 | } 140 | } else { 141 | LOGD("Symbol %s not found via ELF hash", name); 142 | } 143 | } 144 | 145 | if (loaded_si_->gnu_bucket == nullptr && loaded_si_->bucket == nullptr) { 146 | LOGD("No hash tables available, trying linear search"); 147 | 148 | if (loaded_si_->strtab != nullptr) { 149 | size_t sym_count = 0; 150 | if (loaded_si_->nchain > 0) { 151 | sym_count = loaded_si_->nchain; 152 | } else { 153 | LOGD("Cannot determine symbol table size"); 154 | return nullptr; 155 | } 156 | 157 | LOGD("Trying linear search with %zu symbols", sym_count); 158 | for (size_t i = 0; i < sym_count; ++i) { 159 | ElfW(Sym)* sym = &loaded_si_->symtab[i]; 160 | if (sym->st_name != 0 && sym->st_shndx != SHN_UNDEF) { 161 | const char* sym_name = loaded_si_->strtab + sym->st_name; 162 | if (strcmp(sym_name, name) == 0) { 163 | ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias; 164 | LOGD("Found symbol %s via linear search: st_value=0x%lx, load_bias=0x%lx, final_addr=0x%lx", 165 | name, sym->st_value, loaded_si_->load_bias, addr); 166 | 167 | if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) { 168 | return reinterpret_cast(addr); 169 | } else { 170 | LOGE("Symbol %s address 0x%lx out of range [0x%lx, 0x%lx)", 171 | name, addr, loaded_si_->base, loaded_si_->base + loaded_si_->size); 172 | } 173 | } 174 | } 175 | } 176 | LOGD("Symbol %s not found via linear search", name); 177 | } 178 | } 179 | } else { 180 | LOGE("Symbol table is null"); 181 | } 182 | 183 | LOGE("Symbol %s not found in any method", name); 184 | return nullptr; 185 | } -------------------------------------------------------------------------------- /src/memory_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "memory_manager.h" 2 | 3 | MemoryManager::MemoryManager() : load_start_(nullptr), load_size_(0), 4 | load_bias_(0), loaded_phdr_(nullptr) { 5 | } 6 | 7 | MemoryManager::~MemoryManager() { 8 | } 9 | 10 | bool MemoryManager::ReserveAddressSpace(const ElfW(Phdr)* phdr_table, size_t phdr_num) { 11 | ElfW(Addr) min_vaddr; 12 | load_size_ = phdr_table_get_load_size(phdr_table, phdr_num, &min_vaddr); 13 | 14 | if (load_size_ == 0) { 15 | LOGE("No loadable segments"); 16 | return false; 17 | } 18 | 19 | LOGD("Load size: 0x%zx, min_vaddr: 0x%lx", load_size_, min_vaddr); 20 | 21 | void* start = mmap(nullptr, load_size_, PROT_NONE, 22 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 23 | if (start == MAP_FAILED) { 24 | LOGE("Cannot reserve %zu bytes: %s", load_size_, strerror(errno)); 25 | return false; 26 | } 27 | 28 | load_start_ = start; 29 | load_bias_ = reinterpret_cast(start) - min_vaddr; 30 | 31 | LOGD("Reserved address space at %p, bias: 0x%lx", start, load_bias_); 32 | 33 | return true; 34 | } 35 | 36 | bool MemoryManager::LoadSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num, 37 | void* mapped_file, size_t file_size) { 38 | LOGD("Starting LoadSegments: phdr_num=%zu, file_size=%zu", phdr_num, file_size); 39 | 40 | for (size_t i = 0; i < phdr_num; ++i) { 41 | const ElfW(Phdr)* phdr = &phdr_table[i]; 42 | 43 | if (phdr->p_type != PT_LOAD) { 44 | continue; 45 | } 46 | 47 | LOGD("Processing LOAD segment %zu: vaddr=0x%lx, memsz=0x%lx, filesz=0x%lx, offset=0x%lx", 48 | i, phdr->p_vaddr, phdr->p_memsz, phdr->p_filesz, phdr->p_offset); 49 | 50 | ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 51 | ElfW(Addr) seg_end = seg_start + phdr->p_memsz; 52 | 53 | ElfW(Addr) seg_page_start = PAGE_START(seg_start); 54 | ElfW(Addr) seg_page_end = PAGE_END(seg_end); 55 | 56 | ElfW(Addr) seg_file_end = seg_start + phdr->p_filesz; 57 | 58 | ElfW(Addr) file_start = phdr->p_offset; 59 | ElfW(Addr) file_end = file_start + phdr->p_filesz; 60 | 61 | ElfW(Addr) file_page_start = PAGE_START(file_start); 62 | 63 | if (file_end > file_size) { 64 | LOGE("Invalid file size: file_end=0x%lx > file_size=0x%zx", file_end, file_size); 65 | return false; 66 | } 67 | 68 | if (phdr->p_filesz > 0) { 69 | void* seg_addr = reinterpret_cast(seg_page_start); 70 | size_t seg_size = seg_page_end - seg_page_start; 71 | 72 | if (mprotect(seg_addr, seg_size, PROT_READ | PROT_WRITE) < 0) { 73 | LOGE("Cannot mprotect for loading: %s", strerror(errno)); 74 | return false; 75 | } 76 | 77 | void* src = static_cast(mapped_file) + phdr->p_offset; 78 | void* dst = reinterpret_cast(seg_start); 79 | 80 | LOGD("Copying segment %zu: src=%p (offset=0x%lx), dst=%p, size=0x%lx", 81 | i, src, phdr->p_offset, dst, phdr->p_filesz); 82 | 83 | if (static_cast(src) + phdr->p_filesz > static_cast(mapped_file) + file_size) { 84 | LOGE("Source copy would exceed file bounds"); 85 | return false; 86 | } 87 | 88 | if (reinterpret_cast(dst) + phdr->p_filesz > seg_page_end) { 89 | LOGE("Destination copy would exceed segment bounds"); 90 | return false; 91 | } 92 | 93 | memcpy(dst, src, phdr->p_filesz); 94 | 95 | LOGD("Successfully copied segment %zu", i); 96 | } 97 | 98 | if (phdr->p_memsz > phdr->p_filesz) { 99 | ElfW(Addr) bss_start = seg_start + phdr->p_filesz; 100 | ElfW(Addr) bss_end = seg_start + phdr->p_memsz; 101 | size_t bss_size = bss_end - bss_start; 102 | 103 | LOGD("Zeroing BSS: start=0x%lx, size=0x%zx", bss_start, bss_size); 104 | memset(reinterpret_cast(bss_start), 0, bss_size); 105 | } 106 | 107 | ElfW(Addr) aligned_file_end = PAGE_END(seg_file_end); 108 | if (seg_page_end > aligned_file_end) { 109 | size_t zeromap_size = seg_page_end - aligned_file_end; 110 | void* zeromap = mmap(reinterpret_cast(aligned_file_end), 111 | zeromap_size, 112 | PROT_READ | PROT_WRITE, 113 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 114 | -1, 0); 115 | if (zeromap == MAP_FAILED) { 116 | LOGE("Cannot zero fill gap: %s", strerror(errno)); 117 | return false; 118 | } 119 | LOGD("Zero-filled gap: addr=%p, size=0x%zx", zeromap, zeromap_size); 120 | } 121 | } 122 | 123 | LOGD("LoadSegments complete"); 124 | return true; 125 | } 126 | 127 | bool MemoryManager::FindPhdr(const ElfW(Phdr)* phdr_table, size_t phdr_num) { 128 | const ElfW(Phdr)* phdr_limit = phdr_table + phdr_num; 129 | 130 | for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) { 131 | if (phdr->p_type == PT_PHDR) { 132 | return CheckPhdr(load_bias_ + phdr->p_vaddr, phdr_table, phdr_num); 133 | } 134 | } 135 | 136 | for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) { 137 | if (phdr->p_type == PT_LOAD) { 138 | if (phdr->p_offset == 0) { 139 | ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr; 140 | const ElfW(Ehdr)* ehdr = reinterpret_cast(elf_addr); 141 | ElfW(Addr) offset = ehdr->e_phoff; 142 | return CheckPhdr(reinterpret_cast(ehdr) + offset, phdr_table, phdr_num); 143 | } 144 | break; 145 | } 146 | } 147 | 148 | LOGD("Using original phdr_table as loaded_phdr"); 149 | loaded_phdr_ = phdr_table; 150 | return true; 151 | } 152 | 153 | bool MemoryManager::ProtectSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num) { 154 | for (size_t i = 0; i < phdr_num; ++i) { 155 | const ElfW(Phdr)* phdr = &phdr_table[i]; 156 | 157 | if (phdr->p_type != PT_LOAD) { 158 | continue; 159 | } 160 | 161 | ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 162 | ElfW(Addr) seg_page_start = PAGE_START(seg_start); 163 | ElfW(Addr) seg_page_end = PAGE_END(seg_start + phdr->p_memsz); 164 | 165 | int prot = PFLAGS_TO_PROT(phdr->p_flags); 166 | 167 | if (mprotect(reinterpret_cast(seg_page_start), 168 | seg_page_end - seg_page_start, prot) < 0) { 169 | LOGE("Cannot protect segment %zu: %s", i, strerror(errno)); 170 | return false; 171 | } 172 | 173 | LOGD("Protected segment %zu: 0x%lx-0x%lx, prot: %d", 174 | i, seg_page_start, seg_page_end, prot); 175 | } 176 | 177 | return true; 178 | } 179 | 180 | bool MemoryManager::CheckPhdr(ElfW(Addr) loaded, const ElfW(Phdr)* phdr_table, size_t phdr_num) { 181 | const ElfW(Phdr)* phdr_limit = phdr_table + phdr_num; 182 | ElfW(Addr) loaded_end = loaded + (phdr_num * sizeof(ElfW(Phdr))); 183 | 184 | for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) { 185 | if (phdr->p_type != PT_LOAD) { 186 | continue; 187 | } 188 | 189 | ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 190 | ElfW(Addr) seg_end = phdr->p_filesz + seg_start; 191 | 192 | if (seg_start <= loaded && loaded_end <= seg_end) { 193 | loaded_phdr_ = reinterpret_cast(loaded); 194 | return true; 195 | } 196 | } 197 | 198 | LOGE("Loaded phdr %p not in loadable segment", reinterpret_cast(loaded)); 199 | return false; 200 | } 201 | 202 | size_t MemoryManager::phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, 203 | size_t phdr_count, 204 | ElfW(Addr)* min_vaddr) { 205 | ElfW(Addr) min_addr = UINTPTR_MAX; 206 | ElfW(Addr) max_addr = 0; 207 | 208 | bool found_pt_load = false; 209 | 210 | for (size_t i = 0; i < phdr_count; ++i) { 211 | const ElfW(Phdr)* phdr = &phdr_table[i]; 212 | 213 | if (phdr->p_type != PT_LOAD) { 214 | continue; 215 | } 216 | 217 | found_pt_load = true; 218 | 219 | if (phdr->p_vaddr < min_addr) { 220 | min_addr = phdr->p_vaddr; 221 | } 222 | 223 | if (phdr->p_vaddr + phdr->p_memsz > max_addr) { 224 | max_addr = phdr->p_vaddr + phdr->p_memsz; 225 | } 226 | } 227 | 228 | if (!found_pt_load) { 229 | return 0; 230 | } 231 | 232 | min_addr = PAGE_START(min_addr); 233 | max_addr = PAGE_END(max_addr); 234 | 235 | if (min_vaddr != nullptr) { 236 | *min_vaddr = min_addr; 237 | } 238 | 239 | return max_addr - min_addr; 240 | } -------------------------------------------------------------------------------- /src/relocator.cpp: -------------------------------------------------------------------------------- 1 | #include "relocator.h" 2 | 3 | #define R_AARCH64_NONE 0 4 | #define R_AARCH64_ABS64 257 5 | #define R_AARCH64_GLOB_DAT 1025 6 | #define R_AARCH64_JUMP_SLOT 1026 7 | #define R_AARCH64_RELATIVE 1027 8 | #define R_AARCH64_TLS_TPREL64 1030 9 | #define R_AARCH64_TLS_DTPREL32 1031 10 | #define R_AARCH64_IRELATIVE 1032 11 | 12 | Relocator::Relocator() { 13 | } 14 | 15 | Relocator::~Relocator() { 16 | } 17 | 18 | bool Relocator::RelocateImage(soinfo* si) { 19 | LOGD("Starting relocation for %s", si->name); 20 | 21 | if (!si) { 22 | LOGE("soinfo is null"); 23 | return false; 24 | } 25 | 26 | if (si->rela != nullptr && si->rela_count > 0) { 27 | LOGD("Processing %zu RELA relocations", si->rela_count); 28 | 29 | if (si->rela_count > 100000) { 30 | LOGE("RELA count too large: %zu", si->rela_count); 31 | return false; 32 | } 33 | 34 | for (size_t i = 0; i < si->rela_count; ++i) { 35 | if (!ProcessRelaRelocation(si, &si->rela[i])) { 36 | LOGE("Failed to process RELA relocation %zu", i); 37 | // 继续处理其他重定位,不要因为一个失败就退出 38 | // return false; 39 | } 40 | } 41 | } else { 42 | LOGD("No RELA relocations to process"); 43 | } 44 | 45 | if (si->plt_rela != nullptr && si->plt_rela_count > 0) { 46 | LOGD("Processing %zu PLT RELA relocations", si->plt_rela_count); 47 | 48 | if (si->plt_rela_count > 10000) { 49 | LOGE("PLT RELA count too large: %zu", si->plt_rela_count); 50 | return false; 51 | } 52 | 53 | for (size_t i = 0; i < si->plt_rela_count; ++i) { 54 | if (!ProcessRelaRelocation(si, &si->plt_rela[i])) { 55 | LOGE("Failed to process PLT RELA relocation %zu", i); 56 | // 继续处理其他重定位 57 | // return false; 58 | } 59 | } 60 | } else { 61 | LOGD("No PLT RELA relocations to process"); 62 | } 63 | 64 | LOGD("Relocation complete for %s", si->name); 65 | return true; 66 | } 67 | 68 | bool Relocator::LinkImage(soinfo* si) { 69 | if (!si) { 70 | LOGE("soinfo is null in LinkImage"); 71 | return false; 72 | } 73 | 74 | if (!RelocateImage(si)) { 75 | LOGE("Failed to relocate image"); 76 | return false; 77 | } 78 | 79 | if (si->init_func != nullptr) { 80 | LOGD("Calling init function at %p", si->init_func); 81 | try { 82 | si->init_func(); 83 | } catch (...) { 84 | LOGE("Exception in init function"); 85 | return false; 86 | } 87 | } 88 | 89 | if (si->init_array != nullptr && si->init_array_count > 0) { 90 | LOGD("Calling %zu init_array functions", si->init_array_count); 91 | 92 | if (si->init_array_count > 1000) { 93 | LOGE("init_array_count too large: %zu", si->init_array_count); 94 | return false; 95 | } 96 | 97 | for (size_t i = 0; i < si->init_array_count; ++i) { 98 | void (*func)() = si->init_array[i]; 99 | if (func != nullptr) { 100 | LOGD("Calling init_array[%zu] at %p", i, func); 101 | try { 102 | func(); 103 | } catch (...) { 104 | LOGE("Exception in init_array[%zu]", i); 105 | } 106 | } 107 | } 108 | } 109 | 110 | return true; 111 | } 112 | 113 | bool Relocator::ProcessRelaRelocation(soinfo* si, const ElfW(Rela)* rela) { 114 | if (!si || !rela) { 115 | LOGE("Invalid parameters in ProcessRelaRelocation"); 116 | return false; 117 | } 118 | 119 | ElfW(Addr) reloc = static_cast(rela->r_offset + si->load_bias); 120 | ElfW(Word) type = ELFW(R_TYPE)(rela->r_info); 121 | ElfW(Word) sym = ELFW(R_SYM)(rela->r_info); 122 | 123 | LOGD("Processing relocation: offset=0x%lx, type=%d, sym=%d, addend=0x%lx", 124 | rela->r_offset, type, sym, rela->r_addend); 125 | 126 | if (reloc < si->base || reloc >= si->base + si->size) { 127 | LOGE("Relocation address 0x%lx out of range [0x%lx, 0x%lx)", 128 | reloc, si->base, si->base + si->size); 129 | return false; 130 | } 131 | 132 | ElfW(Addr) sym_addr = 0; 133 | const char* sym_name = nullptr; 134 | 135 | if (sym != 0) { 136 | if (!si->symtab) { 137 | LOGE("Symbol table is null"); 138 | return false; 139 | } 140 | 141 | const ElfW(Sym)* s = &si->symtab[sym]; 142 | 143 | if (si->strtab && s->st_name != 0) { 144 | sym_name = si->strtab + s->st_name; 145 | LOGD("Symbol name: %s", sym_name); 146 | } 147 | 148 | if (s->st_shndx != SHN_UNDEF) { 149 | sym_addr = s->st_value + si->load_bias; 150 | LOGD("Local symbol: addr=0x%lx", sym_addr); 151 | } else if (sym_name) { 152 | sym_addr = FindSymbolAddress(sym_name, si); 153 | if (sym_addr == 0) { 154 | LOGD("Cannot find symbol: %s (may be optional)", sym_name); 155 | } 156 | } 157 | } 158 | 159 | void* page_start = reinterpret_cast(PAGE_START(reloc)); 160 | size_t page_size = PAGE_SIZE; 161 | 162 | int old_prot = PROT_READ | PROT_WRITE; 163 | if (mprotect(page_start, page_size, old_prot) != 0) { 164 | LOGD("mprotect failed for relocation, trying anyway: %s", strerror(errno)); 165 | } 166 | 167 | switch (type) { 168 | case R_AARCH64_NONE: 169 | LOGD("R_AARCH64_NONE"); 170 | break; 171 | 172 | case R_AARCH64_ABS64: 173 | LOGD("R_AARCH64_ABS64: writing 0x%lx to 0x%lx", sym_addr + rela->r_addend, reloc); 174 | *reinterpret_cast(reloc) = sym_addr + rela->r_addend; 175 | break; 176 | 177 | case R_AARCH64_GLOB_DAT: 178 | LOGD("R_AARCH64_GLOB_DAT: writing 0x%lx to 0x%lx", sym_addr + rela->r_addend, reloc); 179 | *reinterpret_cast(reloc) = sym_addr + rela->r_addend; 180 | break; 181 | 182 | case R_AARCH64_JUMP_SLOT: 183 | LOGD("R_AARCH64_JUMP_SLOT: writing 0x%lx to 0x%lx", sym_addr + rela->r_addend, reloc); 184 | *reinterpret_cast(reloc) = sym_addr + rela->r_addend; 185 | break; 186 | 187 | case R_AARCH64_RELATIVE: 188 | LOGD("R_AARCH64_RELATIVE: writing 0x%lx to 0x%lx", si->load_bias + rela->r_addend, reloc); 189 | *reinterpret_cast(reloc) = si->load_bias + rela->r_addend; 190 | break; 191 | 192 | case R_AARCH64_IRELATIVE: 193 | { 194 | ElfW(Addr) resolver = si->load_bias + rela->r_addend; 195 | LOGD("R_AARCH64_IRELATIVE: resolver at 0x%lx", resolver); 196 | 197 | if (resolver < si->base || resolver >= si->base + si->size) { 198 | LOGE("Invalid resolver address: 0x%lx", resolver); 199 | return false; 200 | } 201 | 202 | try { 203 | ElfW(Addr) resolved = ((ElfW(Addr) (*)())resolver)(); 204 | *reinterpret_cast(reloc) = resolved; 205 | LOGD("R_AARCH64_IRELATIVE: resolved to 0x%lx", resolved); 206 | } catch (...) { 207 | LOGE("Exception in IRELATIVE resolver"); 208 | return false; 209 | } 210 | } 211 | break; 212 | 213 | default: 214 | LOGD("Unknown relocation type %d, skipping", type); 215 | break; 216 | } 217 | 218 | return true; 219 | } 220 | 221 | ElfW(Addr) Relocator::FindSymbolAddress(const char* name, soinfo* si) { 222 | if (!name || !si) { 223 | return 0; 224 | } 225 | 226 | if (si->symtab != nullptr) { 227 | if (si->gnu_bucket != nullptr) { 228 | uint32_t hash = gnu_hash(name); 229 | ElfW(Sym)* sym = gnu_lookup(hash, name, si); 230 | if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { 231 | ElfW(Addr) addr = sym->st_value + si->load_bias; 232 | LOGD("Found symbol %s in current SO at 0x%lx", name, addr); 233 | return addr; 234 | } 235 | } 236 | 237 | if (si->bucket != nullptr) { 238 | unsigned hash = elf_hash(name); 239 | ElfW(Sym)* sym = elf_lookup(hash, name, si); 240 | if (sym != nullptr && sym->st_shndx != SHN_UNDEF) { 241 | ElfW(Addr) addr = sym->st_value + si->load_bias; 242 | LOGD("Found symbol %s in current SO at 0x%lx", name, addr); 243 | return addr; 244 | } 245 | } 246 | } 247 | 248 | for (const auto& lib : si->needed_libs) { 249 | void* handle = dlopen(lib.c_str(), RTLD_NOW | RTLD_NOLOAD); 250 | if (handle != nullptr) { 251 | void* addr = dlsym(handle, name); 252 | if (addr != nullptr) { 253 | LOGD("Found symbol %s in %s at %p", name, lib.c_str(), addr); 254 | dlclose(handle); 255 | return reinterpret_cast(addr); 256 | } 257 | dlclose(handle); 258 | } 259 | } 260 | 261 | void* addr = dlsym(RTLD_DEFAULT, name); 262 | if (addr != nullptr) { 263 | LOGD("Found symbol %s globally at %p", name, addr); 264 | return reinterpret_cast(addr); 265 | } 266 | 267 | LOGD("Symbol %s not found", name); 268 | return 0; 269 | } 270 | 271 | ElfW(Sym)* Relocator::gnu_lookup(uint32_t hash, const char* name, soinfo* si) { 272 | if (!si->gnu_bucket || !si->gnu_chain || !si->symtab || !si->strtab) { 273 | return nullptr; 274 | } 275 | 276 | uint32_t h2 = hash >> si->gnu_shift2; 277 | 278 | uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8; 279 | uint32_t word_num = (hash / bloom_mask_bits) & si->gnu_maskwords; 280 | ElfW(Addr) bloom_word = si->gnu_bloom_filter[word_num]; 281 | 282 | if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & 283 | (bloom_word >> (h2 % bloom_mask_bits))) == 0) { 284 | return nullptr; 285 | } 286 | 287 | uint32_t n = si->gnu_bucket[hash % si->gnu_nbucket]; 288 | 289 | if (n == 0) { 290 | return nullptr; 291 | } 292 | 293 | do { 294 | ElfW(Sym)* s = si->symtab + n; 295 | if (((si->gnu_chain[n] ^ hash) >> 1) == 0 && 296 | strcmp(si->strtab + s->st_name, name) == 0) { 297 | return s; 298 | } 299 | } while ((si->gnu_chain[n++] & 1) == 0); 300 | 301 | return nullptr; 302 | } 303 | 304 | ElfW(Sym)* Relocator::elf_lookup(unsigned hash, const char* name, soinfo* si) { 305 | if (!si->bucket || !si->chain || !si->symtab || !si->strtab) { 306 | return nullptr; 307 | } 308 | 309 | for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) { 310 | ElfW(Sym)* s = si->symtab + n; 311 | if (s->st_name != 0 && strcmp(si->strtab + s->st_name, name) == 0) { 312 | return s; 313 | } 314 | } 315 | return nullptr; 316 | } 317 | 318 | uint32_t Relocator::gnu_hash(const char* name) { 319 | uint32_t h = 5381; 320 | for (const uint8_t* c = reinterpret_cast(name); *c != '\0'; c++) { 321 | h += (h << 5) + *c; 322 | } 323 | return h; 324 | } 325 | 326 | unsigned Relocator::elf_hash(const char* name) { 327 | unsigned h = 0, g; 328 | for (const unsigned char* p = reinterpret_cast(name); *p; p++) { 329 | h = (h << 4) + *p; 330 | g = h & 0xf0000000; 331 | h ^= g; 332 | h ^= g >> 24; 333 | } 334 | return h; 335 | } -------------------------------------------------------------------------------- /src/soinfo_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "soinfo_manager.h" 2 | #include "utils.h" 3 | #include "memory_manager.h" 4 | #include "elf_reader.h" 5 | 6 | SoinfoManager::SoinfoManager() { 7 | } 8 | 9 | SoinfoManager::~SoinfoManager() { 10 | } 11 | 12 | soinfo* SoinfoManager::GetOrCreateSoinfo(const char* name) { 13 | auto it = soinfo_map_.find(name); 14 | if (it != soinfo_map_.end()) { 15 | return it->second.get(); 16 | } 17 | 18 | auto si = std::make_unique(); 19 | memset(si.get(), 0, sizeof(soinfo)); 20 | si->name = strdup(name); 21 | 22 | soinfo* result = si.get(); 23 | soinfo_map_[name] = std::move(si); 24 | 25 | return result; 26 | } 27 | 28 | bool SoinfoManager::UpdateSoinfo(soinfo* si, MemoryManager* mm, ElfReader* reader) { 29 | if (!si || !mm || !reader) { 30 | return false; 31 | } 32 | 33 | si->base = reinterpret_cast(mm->GetLoadStart()); 34 | si->size = mm->GetLoadSize(); 35 | si->load_bias = mm->GetLoadBias(); 36 | 37 | const ElfW(Phdr)* loaded_phdr = mm->GetLoadedPhdr(); 38 | if (loaded_phdr != nullptr) { 39 | si->phdr = loaded_phdr; 40 | } else { 41 | si->phdr = reader->GetProgramHeaders(); 42 | LOGD("Using original program headers"); 43 | } 44 | 45 | si->phnum = reader->GetProgramHeaderCount(); 46 | 47 | const ElfW(Ehdr)* header = reader->GetHeader(); 48 | si->entry = si->load_bias + header->e_entry; 49 | 50 | LOGD("Updated soinfo: base=0x%lx, size=0x%zx, bias=0x%lx, entry=0x%lx, phdr=%p", 51 | si->base, si->size, si->load_bias, si->entry, si->phdr); 52 | 53 | return true; 54 | } 55 | 56 | bool SoinfoManager::PrelinkImage(soinfo* si) { 57 | LOGD("Starting PrelinkImage for %s", si->name); 58 | 59 | if (!ParseDynamic(si)) { 60 | LOGE("Failed to parse dynamic section"); 61 | return false; 62 | } 63 | 64 | if (si->strtab != nullptr && si->dynamic != nullptr) { 65 | for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { 66 | if (d->d_tag == DT_NEEDED && si->needed_libs.empty()) { 67 | const char* needed = si->strtab + d->d_un.d_val; 68 | si->needed_libs.push_back(needed); 69 | LOGD("Processing deferred DT_NEEDED: %s", needed); 70 | } 71 | } 72 | } 73 | 74 | ApplyRelaSections(si); 75 | 76 | LOGD("PrelinkImage complete for %s", si->name); 77 | return true; 78 | } 79 | 80 | soinfo* SoinfoManager::FindSoinfo(const char* name) { 81 | auto it = soinfo_map_.find(name); 82 | if (it != soinfo_map_.end()) { 83 | return it->second.get(); 84 | } 85 | return nullptr; 86 | } 87 | 88 | soinfo* SoinfoManager::GetCurrentSoinfo() { 89 | return Utils::get_soinfo("libcustom_linker.so"); 90 | } 91 | 92 | bool SoinfoManager::ParseDynamic(soinfo* si) { 93 | if (!si || !si->phdr) { 94 | LOGE("Invalid soinfo or phdr is null"); 95 | return false; 96 | } 97 | 98 | LOGD("Starting ParseDynamic: phdr=%p, phnum=%zu", si->phdr, si->phnum); 99 | 100 | const ElfW(Phdr)* phdr_limit = si->phdr + si->phnum; 101 | bool found_dynamic = false; 102 | 103 | for (const ElfW(Phdr)* phdr = si->phdr; phdr < phdr_limit; ++phdr) { 104 | if (phdr->p_type == PT_DYNAMIC) { 105 | si->dynamic = reinterpret_cast(si->load_bias + phdr->p_vaddr); 106 | si->dynamic_count = phdr->p_memsz / sizeof(ElfW(Dyn)); 107 | found_dynamic = true; 108 | LOGD("Found PT_DYNAMIC at vaddr=0x%lx, memsz=0x%lx", phdr->p_vaddr, phdr->p_memsz); 109 | break; 110 | } 111 | } 112 | 113 | if (!found_dynamic || !si->dynamic) { 114 | LOGE("No PT_DYNAMIC segment found"); 115 | return false; 116 | } 117 | 118 | LOGD("Dynamic section at %p, count=%zu", si->dynamic, si->dynamic_count); 119 | 120 | if (si->dynamic_count == 0 || si->dynamic_count > 1000) { 121 | LOGE("Invalid dynamic count: %zu", si->dynamic_count); 122 | return false; 123 | } 124 | 125 | size_t dyn_count = 0; 126 | for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL && dyn_count < si->dynamic_count; ++d, ++dyn_count) { 127 | 128 | LOGD("Processing dynamic entry %zu: tag=0x%lx, val/ptr=0x%lx", 129 | dyn_count, (unsigned long)d->d_tag, (unsigned long)d->d_un.d_val); 130 | 131 | switch (d->d_tag) { 132 | case DT_SYMTAB: 133 | si->symtab = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 134 | LOGD("DT_SYMTAB: raw_ptr=0x%lx, final_addr=%p", 135 | (unsigned long)d->d_un.d_ptr, si->symtab); 136 | break; 137 | 138 | case DT_STRTAB: 139 | si->strtab = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 140 | LOGD("DT_STRTAB: raw_ptr=0x%lx, final_addr=%p", 141 | (unsigned long)d->d_un.d_ptr, si->strtab); 142 | break; 143 | 144 | case DT_STRSZ: 145 | LOGD("DT_STRSZ: %lu", (unsigned long)d->d_un.d_val); 146 | break; 147 | 148 | case DT_HASH: { 149 | uint32_t* hash = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 150 | si->nbucket = hash[0]; 151 | si->nchain = hash[1]; 152 | si->bucket = hash + 2; 153 | si->chain = si->bucket + si->nbucket; 154 | LOGD("DT_HASH: raw_ptr=0x%lx, nbucket=%zu, nchain=%zu", 155 | (unsigned long)d->d_un.d_ptr, si->nbucket, si->nchain); 156 | break; 157 | } 158 | 159 | case DT_GNU_HASH: { 160 | uint32_t* hash = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 161 | si->gnu_nbucket = hash[0]; 162 | uint32_t symbias = hash[1]; 163 | si->gnu_maskwords = hash[2]; 164 | si->gnu_shift2 = hash[3]; 165 | si->gnu_bloom_filter = reinterpret_cast(hash + 4); 166 | si->gnu_bucket = reinterpret_cast(si->gnu_bloom_filter + si->gnu_maskwords); 167 | si->gnu_chain = si->gnu_bucket + si->gnu_nbucket - symbias; 168 | LOGD("DT_GNU_HASH: raw_ptr=0x%lx, nbucket=%zu, symbias=%u", 169 | (unsigned long)d->d_un.d_ptr, si->gnu_nbucket, symbias); 170 | break; 171 | } 172 | 173 | case DT_JMPREL: 174 | si->plt_rela = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 175 | LOGD("DT_JMPREL: raw_ptr=0x%lx, final_addr=%p", 176 | (unsigned long)d->d_un.d_ptr, si->plt_rela); 177 | break; 178 | 179 | case DT_PLTRELSZ: 180 | si->plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela)); 181 | LOGD("DT_PLTRELSZ: raw_val=%lu, count=%zu", 182 | (unsigned long)d->d_un.d_val, si->plt_rela_count); 183 | break; 184 | 185 | case DT_PLTREL: 186 | LOGD("DT_PLTREL: %lu", (unsigned long)d->d_un.d_val); 187 | break; 188 | 189 | case DT_RELA: 190 | si->rela = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 191 | LOGD("DT_RELA: raw_ptr=0x%lx, final_addr=%p", 192 | (unsigned long)d->d_un.d_ptr, si->rela); 193 | break; 194 | 195 | case DT_RELASZ: 196 | si->rela_count = d->d_un.d_val / sizeof(ElfW(Rela)); 197 | LOGD("DT_RELASZ: raw_val=%lu, count=%zu", 198 | (unsigned long)d->d_un.d_val, si->rela_count); 199 | break; 200 | 201 | case DT_RELAENT: 202 | LOGD("DT_RELAENT: %lu", (unsigned long)d->d_un.d_val); 203 | break; 204 | 205 | case DT_INIT: 206 | si->init_func = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 207 | LOGD("DT_INIT: raw_ptr=0x%lx, final_addr=%p", 208 | (unsigned long)d->d_un.d_ptr, si->init_func); 209 | break; 210 | 211 | case DT_INIT_ARRAY: 212 | si->init_array = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 213 | LOGD("DT_INIT_ARRAY: raw_ptr=0x%lx, final_addr=%p", 214 | (unsigned long)d->d_un.d_ptr, si->init_array); 215 | break; 216 | 217 | case DT_INIT_ARRAYSZ: 218 | si->init_array_count = d->d_un.d_val / sizeof(void*); 219 | LOGD("DT_INIT_ARRAYSZ: raw_val=%lu, count=%zu", 220 | (unsigned long)d->d_un.d_val, si->init_array_count); 221 | break; 222 | 223 | case DT_FINI: 224 | LOGD("DT_FINI: 0x%lx", (unsigned long)d->d_un.d_ptr); 225 | break; 226 | 227 | case DT_FINI_ARRAY: 228 | si->fini_array = reinterpret_cast(si->load_bias + d->d_un.d_ptr); 229 | LOGD("DT_FINI_ARRAY: raw_ptr=0x%lx, final_addr=%p", 230 | (unsigned long)d->d_un.d_ptr, si->fini_array); 231 | break; 232 | 233 | case DT_FINI_ARRAYSZ: 234 | si->fini_array_count = d->d_un.d_val / sizeof(void*); 235 | LOGD("DT_FINI_ARRAYSZ: raw_val=%lu, count=%zu", 236 | (unsigned long)d->d_un.d_val, si->fini_array_count); 237 | break; 238 | 239 | case DT_FLAGS: 240 | si->flags = d->d_un.d_val; 241 | LOGD("DT_FLAGS: 0x%x", si->flags); 242 | break; 243 | 244 | case DT_FLAGS_1: 245 | LOGD("DT_FLAGS_1: 0x%lx", (unsigned long)d->d_un.d_val); 246 | break; 247 | 248 | case DT_SONAME: 249 | LOGD("DT_SONAME: offset=%lu", (unsigned long)d->d_un.d_val); 250 | break; 251 | 252 | case DT_RUNPATH: 253 | LOGD("DT_RUNPATH: offset=%lu", (unsigned long)d->d_un.d_val); 254 | break; 255 | 256 | case DT_NEEDED: 257 | // 跳过,稍后处理 258 | LOGD("DT_NEEDED: offset=%lu (deferred)", (unsigned long)d->d_un.d_val); 259 | break; 260 | 261 | default: 262 | LOGD("Unknown dynamic tag: 0x%lx, value=0x%lx", 263 | (unsigned long)d->d_tag, (unsigned long)d->d_un.d_val); 264 | break; 265 | } 266 | 267 | // 添加安全检查,防止无限循环 268 | if (dyn_count > si->dynamic_count) { 269 | LOGE("Dynamic parsing exceeded expected count"); 270 | break; 271 | } 272 | } 273 | 274 | if (si->symtab == nullptr) { 275 | LOGD("Warning: DT_SYMTAB not found or is null"); 276 | } 277 | 278 | if (si->strtab == nullptr) { 279 | LOGD("Warning: DT_STRTAB not found or is null"); 280 | } 281 | 282 | if (si->strtab != nullptr) { 283 | dyn_count = 0; 284 | for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL && dyn_count < si->dynamic_count; ++d, ++dyn_count) { 285 | if (d->d_tag == DT_NEEDED) { 286 | if (d->d_un.d_val < 65536) { 287 | const char* needed = si->strtab + d->d_un.d_val; 288 | if (strlen(needed) > 0 && strlen(needed) < 256) { 289 | si->needed_libs.push_back(needed); 290 | LOGD("DT_NEEDED: %s", needed); 291 | } else { 292 | LOGD("DT_NEEDED: invalid string at offset %lu", (unsigned long)d->d_un.d_val); 293 | } 294 | } else { 295 | LOGD("DT_NEEDED: offset too large: %lu", (unsigned long)d->d_un.d_val); 296 | } 297 | } 298 | } 299 | } 300 | 301 | LOGD("Dynamic parsing complete: symtab=%p, strtab=%p, needed_libs=%zu", 302 | si->symtab, si->strtab, si->needed_libs.size()); 303 | 304 | return true; 305 | } 306 | 307 | void SoinfoManager::ApplyRelaSections(soinfo* si) { 308 | LOGD("RELA sections: rela_count=%zu, plt_rela_count=%zu", 309 | si->rela_count, si->plt_rela_count); 310 | } --------------------------------------------------------------------------------