feat:增加自定义linker注入

This commit is contained in:
jiqiu2021
2025-06-26 19:59:33 +08:00
parent eb41d924b4
commit d793712a13
27 changed files with 2055 additions and 34 deletions

View File

@@ -29,8 +29,8 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
coreLibraryDesugaringEnabled false
}
}

View File

@@ -13,6 +13,8 @@ import android.widget.ProgressBar;
import android.app.Dialog;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -134,15 +136,24 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog
TextView packageName = dialogView.findViewById(R.id.packageName);
RecyclerView soListRecyclerView = dialogView.findViewById(R.id.soListRecyclerView);
TextView emptyText = dialogView.findViewById(R.id.emptyText);
SwitchMaterial switchHideInjection = dialogView.findViewById(R.id.switchHideInjection);
RadioGroup injectionMethodGroup = dialogView.findViewById(R.id.injectionMethodGroup);
RadioButton radioStandardInjection = dialogView.findViewById(R.id.radioStandardInjection);
RadioButton radioRiruInjection = dialogView.findViewById(R.id.radioRiruInjection);
RadioButton radioCustomLinkerInjection = dialogView.findViewById(R.id.radioCustomLinkerInjection);
appIcon.setImageDrawable(appInfo.getAppIcon());
appName.setText(appInfo.getAppName());
packageName.setText(appInfo.getPackageName());
// Load current config
boolean hideInjection = configManager.getHideInjection();
switchHideInjection.setChecked(hideInjection);
String injectionMethod = configManager.getAppInjectionMethod(appInfo.getPackageName());
if ("custom_linker".equals(injectionMethod)) {
radioCustomLinkerInjection.setChecked(true);
} else if ("riru".equals(injectionMethod)) {
radioRiruInjection.setChecked(true);
} else {
radioStandardInjection.setChecked(true);
}
// Setup SO list
List<ConfigManager.SoFile> globalSoFiles = configManager.getAllSoFiles();
@@ -165,8 +176,16 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog
.setTitle("配置注入")
.setView(dialogView)
.setPositiveButton("保存", (dialog, which) -> {
// Save hide injection setting
configManager.setHideInjection(switchHideInjection.isChecked());
// Save injection method
String selectedMethod;
if (radioCustomLinkerInjection.isChecked()) {
selectedMethod = "custom_linker";
} else if (radioRiruInjection.isChecked()) {
selectedMethod = "riru";
} else {
selectedMethod = "standard";
}
configManager.setAppInjectionMethod(appInfo.getPackageName(), selectedMethod);
// Save SO selection
if (soListRecyclerView.getAdapter() != null) {

View File

@@ -227,6 +227,24 @@ public class ConfigManager {
saveConfig();
}
public String getAppInjectionMethod(String packageName) {
AppConfig appConfig = config.perAppConfig.get(packageName);
if (appConfig == null) {
return "standard"; // Default to standard
}
return appConfig.injectionMethod != null ? appConfig.injectionMethod : "standard";
}
public void setAppInjectionMethod(String packageName, String method) {
AppConfig appConfig = config.perAppConfig.get(packageName);
if (appConfig == null) {
appConfig = new AppConfig();
config.perAppConfig.put(packageName, appConfig);
}
appConfig.injectionMethod = method;
saveConfig();
}
// Copy SO files directly to app's data directory
private void deploySoFilesToApp(String packageName) {
AppConfig appConfig = config.perAppConfig.get(packageName);
@@ -384,6 +402,7 @@ public class ConfigManager {
public static class AppConfig {
public boolean enabled = false;
public List<SoFile> soFiles = new ArrayList<>();
public String injectionMethod = "standard"; // "standard", "riru" or "custom_linker"
}
public static class SoFile {

View File

@@ -77,12 +77,42 @@
android:textColor="?android:attr/textColorTertiary"
android:visibility="gone" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchHideInjection"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="注入方式"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<RadioGroup
android:id="@+id/injectionMethodGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="隐藏注入"
android:layout_marginTop="8dp" />
android:orientation="vertical">
<RadioButton
android:id="@+id/radioStandardInjection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="标准注入"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginBottom="8dp"
android:text="使用标准dlopen方式注入"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp" />
<RadioButton
android:id="@+id/radioRiruInjection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Riru注入" />
<TextView
android:layout_width="wrap_content"
@@ -90,7 +120,23 @@
android:text="使用Riru Hide隐藏注入的SO文件"
android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_marginStart="56dp"
android:layout_marginTop="4dp" />
android:layout_marginStart="32dp"
android:layout_marginBottom="8dp" />
<RadioButton
android:id="@+id/radioCustomLinkerInjection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="自定义Linker注入" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用自定义ELF加载器进行注入"
android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_marginStart="32dp" />
</RadioGroup>
</LinearLayout>

View File

@@ -29,10 +29,14 @@ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}")
include_directories(
xdl/include
mylinker/include
)
aux_source_directory(xdl xdl-src)
# Build mylinker as a subdirectory
add_subdirectory(mylinker)
add_library(${MODULE_NAME} SHARED
main.cpp
hack_new.cpp
@@ -40,7 +44,7 @@ add_library(${MODULE_NAME} SHARED
newriruhide.cpp
pmparser.cpp
${xdl-src})
target_link_libraries(${MODULE_NAME} log)
target_link_libraries(${MODULE_NAME} log mylinker)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_custom_command(TARGET ${MODULE_NAME} POST_BUILD

View File

@@ -43,6 +43,16 @@ namespace Config {
std::string enabledStr = extractValue(appJson, "enabled");
appConfig.enabled = (enabledStr == "true");
// Parse injection method
std::string methodStr = extractValue(appJson, "injectionMethod");
if (methodStr == "2" || methodStr == "custom_linker") {
appConfig.injectionMethod = InjectionMethod::CUSTOM_LINKER;
} else if (methodStr == "1" || methodStr == "riru") {
appConfig.injectionMethod = InjectionMethod::RIRU;
} else {
appConfig.injectionMethod = InjectionMethod::STANDARD;
}
// Parse soFiles array
size_t soFilesPos = appJson.find("\"soFiles\"");
if (soFilesPos != std::string::npos) {
@@ -76,8 +86,10 @@ namespace Config {
}
g_config.perAppConfig[packageName] = appConfig;
LOGD("Loaded config for app: %s, enabled: %d, SO files: %zu",
packageName.c_str(), appConfig.enabled, appConfig.soFiles.size());
const char* methodName = appConfig.injectionMethod == InjectionMethod::CUSTOM_LINKER ? "custom_linker" :
appConfig.injectionMethod == InjectionMethod::RIRU ? "riru" : "standard";
LOGD("Loaded config for app: %s, enabled: %d, method: %s, SO files: %zu",
packageName.c_str(), appConfig.enabled, methodName, appConfig.soFiles.size());
}
ModuleConfig readConfig() {
@@ -188,4 +200,16 @@ namespace Config {
}
return g_config.hideInjection;
}
InjectionMethod getAppInjectionMethod(const std::string& packageName) {
if (!g_configLoaded) {
readConfig();
}
auto it = g_config.perAppConfig.find(packageName);
if (it != g_config.perAppConfig.end()) {
return it->second.injectionMethod;
}
return InjectionMethod::STANDARD;
}
}

View File

@@ -13,8 +13,15 @@ namespace Config {
std::string originalPath;
};
enum class InjectionMethod {
STANDARD = 0,
RIRU = 1,
CUSTOM_LINKER = 2
};
struct AppConfig {
bool enabled = false;
InjectionMethod injectionMethod = InjectionMethod::STANDARD;
std::vector<SoFile> soFiles;
};
@@ -35,6 +42,9 @@ namespace Config {
// Get hide injection setting
bool shouldHideInjection();
// Get injection method for specific app
InjectionMethod getAppInjectionMethod(const std::string& packageName);
}
#endif // CONFIG_H

View File

@@ -6,7 +6,8 @@
#define ZYGISK_IL2CPPDUMPER_HACK_H
#include <stddef.h>
#include <jni.h>
void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length);
void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length, JavaVM *vm);
#endif //ZYGISK_IL2CPPDUMPER_HACK_H

View File

@@ -1,6 +1,7 @@
#include "hack.h"
#include "config.h"
#include "log.h"
#include "mylinker.h"
#include <cstring>
#include <thread>
#include <dlfcn.h>
@@ -8,11 +9,12 @@
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <jni.h>
// External function from newriruhide.cpp
extern "C" void riru_hide(const char *name);
void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) {
void load_so_file_standard(const char *game_data_dir, const Config::SoFile &soFile) {
// Extract the mapped filename from storedPath (e.g., "1750851324251_libmylib.so")
const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/');
if (!mapped_name) {
@@ -31,10 +33,38 @@ void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) {
return;
}
// Load the SO file
// Load the SO file using standard dlopen (no hiding)
void *handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
LOGI("Successfully loaded SO: %s (mapped: %s)", soFile.name.c_str(), mapped_name);
LOGI("Successfully loaded SO via standard dlopen: %s (mapped: %s)", soFile.name.c_str(), mapped_name);
} else {
LOGE("Failed to load SO via standard dlopen: %s - %s", so_path, dlerror());
}
}
void load_so_file_riru(const char *game_data_dir, const Config::SoFile &soFile) {
// Extract the mapped filename from storedPath (e.g., "1750851324251_libmylib.so")
const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/');
if (!mapped_name) {
mapped_name = soFile.storedPath.c_str();
} else {
mapped_name++; // Skip the '/'
}
// The file should already be in app's files directory
char so_path[512];
snprintf(so_path, sizeof(so_path), "%s/files/%s", game_data_dir, mapped_name);
// Check if file exists
if (access(so_path, F_OK) != 0) {
LOGE("SO file not found: %s", so_path);
return;
}
// Load the SO file using dlopen (Riru method)
void *handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
LOGI("Successfully loaded SO via Riru: %s (mapped: %s)", soFile.name.c_str(), mapped_name);
// Hide if configured
if (Config::shouldHideInjection()) {
@@ -43,30 +73,81 @@ void load_so_file(const char *game_data_dir, const Config::SoFile &soFile) {
LOGI("Applied riru_hide to: %s", mapped_name);
}
} else {
LOGE("Failed to load SO: %s - %s", so_path, dlerror());
LOGE("Failed to load SO via Riru: %s - %s", so_path, dlerror());
}
}
void hack_thread_func(const char *game_data_dir, const char *package_name) {
void load_so_file_custom_linker(const char *game_data_dir, const Config::SoFile &soFile, JavaVM *vm) {
// Extract the mapped filename from storedPath
const char *mapped_name = strrchr(soFile.storedPath.c_str(), '/');
if (!mapped_name) {
mapped_name = soFile.storedPath.c_str();
} else {
mapped_name++; // Skip the '/'
}
// The file should already be in app's files directory
char so_path[512];
snprintf(so_path, sizeof(so_path), "%s/files/%s", game_data_dir, mapped_name);
// Check if file exists
if (access(so_path, F_OK) != 0) {
LOGE("SO file not found: %s", so_path);
return;
}
// Load the SO file using custom linker
if (mylinker_load_library(so_path, vm)) {
LOGI("Successfully loaded SO via custom linker: %s (mapped: %s)", soFile.name.c_str(), mapped_name);
// Custom linker doesn't appear in maps, so no need to hide
if (Config::shouldHideInjection()) {
LOGI("Custom linker injection is inherently hidden");
}
} else {
LOGE("Failed to load SO via custom linker: %s", so_path);
}
}
void hack_thread_func(const char *game_data_dir, const char *package_name, JavaVM *vm) {
LOGI("Hack thread started for package: %s", package_name);
// Wait a bit for app to initialize and files to be copied
sleep(2);
// Get injection method for this app
Config::InjectionMethod method = Config::getAppInjectionMethod(package_name);
const char* methodName = method == Config::InjectionMethod::CUSTOM_LINKER ? "Custom Linker" :
method == Config::InjectionMethod::RIRU ? "Riru" : "Standard";
LOGI("Using injection method: %s", methodName);
// Get SO files for this app
auto soFiles = Config::getAppSoFiles(package_name);
LOGI("Found %zu SO files to load", soFiles.size());
// Load each SO file
// Load each SO file using the configured method
for (const auto &soFile : soFiles) {
LOGI("Loading SO: %s (stored as: %s)", soFile.name.c_str(), soFile.storedPath.c_str());
load_so_file(game_data_dir, soFile);
if (method == Config::InjectionMethod::CUSTOM_LINKER) {
load_so_file_custom_linker(game_data_dir, soFile, vm);
} else if (method == Config::InjectionMethod::RIRU) {
load_so_file_riru(game_data_dir, soFile);
} else {
load_so_file_standard(game_data_dir, soFile);
}
}
void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length) {
// Cleanup custom linker resources when done (if used)
if (method == Config::InjectionMethod::CUSTOM_LINKER) {
// Keep libraries loaded, don't cleanup
LOGI("Custom linker injection completed, libraries remain loaded");
}
}
void hack_prepare(const char *game_data_dir, const char *package_name, void *data, size_t length, JavaVM *vm) {
LOGI("hack_prepare called for package: %s, dir: %s", package_name, game_data_dir);
std::thread hack_thread(hack_thread_func, game_data_dir, package_name);
std::thread hack_thread(hack_thread_func, game_data_dir, package_name, vm);
hack_thread.detach();
}

View File

@@ -42,9 +42,15 @@ public:
void postAppSpecialize(const AppSpecializeArgs *) override {
if (enable_hack) {
// Then start hack thread
std::thread hack_thread(hack_prepare, _data_dir, _package_name, data, length);
// Get JavaVM
JavaVM *vm = nullptr;
if (env->GetJavaVM(&vm) == JNI_OK) {
// Then start hack thread with JavaVM
std::thread hack_thread(hack_prepare, _data_dir, _package_name, data, length, vm);
hack_thread.detach();
} else {
LOGE("Failed to get JavaVM");
}
}
}

View File

@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.18.1)
project(ElfLoader)
set(CMAKE_CXX_STANDARD 17)
include_directories(include)
set(SOURCES
mylinker.cpp
elf_loader.cpp
elf_reader.cpp
memory_manager.cpp
relocator.cpp
soinfo_manager.cpp
utils.cpp
)
find_library(log-lib log)
# Build as shared library
add_library(mylinker SHARED ${SOURCES})
target_link_libraries(mylinker ${log-lib})
# Only build executable for standalone testing
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
add_executable(elf_loader_test main.cpp elf_loader.cpp elf_reader.cpp memory_manager.cpp relocator.cpp soinfo_manager.cpp utils.cpp)
target_link_libraries(elf_loader_test ${log-lib})
endif()

View File

@@ -0,0 +1,187 @@
#include "elf_loader.h"
ElfLoader::ElfLoader() : loaded_si_(nullptr) {
reader_ = std::make_unique<ElfReader>();
memory_manager_ = std::make_unique<MemoryManager>();
soinfo_manager_ = std::make_unique<SoinfoManager>();
relocator_ = std::make_unique<Relocator>();
}
ElfLoader::~ElfLoader() {
}
bool ElfLoader::LoadLibrary(const char* path) {
LOGI("Loading library: %s", path);
if (!reader_->Open(path)) {
LOGE("Failed to open %s", path);
return false;
}
if (!reader_->Read()) {
LOGE("Failed to read ELF file");
return false;
}
if (!memory_manager_->ReserveAddressSpace(reader_->GetProgramHeaders(),
reader_->GetProgramHeaderCount())) {
LOGE("Failed to reserve address space");
return false;
}
if (!memory_manager_->LoadSegments(reader_->GetProgramHeaders(),
reader_->GetProgramHeaderCount(),
reader_->GetMappedAddr(),
reader_->GetFileSize())) {
LOGE("Failed to load segments");
return false;
}
if (!memory_manager_->FindPhdr(reader_->GetProgramHeaders(),
reader_->GetProgramHeaderCount())) {
LOGE("Failed to find program headers");
return false;
}
const char* basename = strrchr(path, '/');
basename = basename ? basename + 1 : path;
loaded_si_ = soinfo_manager_->GetOrCreateSoinfo(basename);
if (!soinfo_manager_->UpdateSoinfo(loaded_si_, memory_manager_.get(), reader_.get())) {
LOGE("Failed to update soinfo");
return false;
}
if (!soinfo_manager_->PrelinkImage(loaded_si_)) {
LOGE("Failed to prelink image");
return false;
}
if (!memory_manager_->ProtectSegments(reader_->GetProgramHeaders(),
reader_->GetProgramHeaderCount())) {
LOGE("Failed to protect segments");
return false;
}
if (!relocator_->LinkImage(loaded_si_)) {
LOGE("Failed to link image");
return false;
}
reader_->Close();
LOGI("Successfully loaded %s", path);
return true;
}
void ElfLoader::CallConstructors() {
if (loaded_si_ == nullptr) {
return;
}
LOGD("Constructors already called during linking");
}
void* ElfLoader::GetSymbol(const char* name) {
if (loaded_si_ == nullptr) {
LOGE("loaded_si_ is null");
return nullptr;
}
if (name == nullptr) {
LOGE("Symbol name is null");
return nullptr;
}
LOGD("Looking for symbol: %s", name);
LOGD("soinfo state: symtab=%p, strtab=%p, gnu_bucket=%p, bucket=%p",
loaded_si_->symtab, loaded_si_->strtab, loaded_si_->gnu_bucket, loaded_si_->bucket);
if (loaded_si_->symtab != nullptr) {
if (loaded_si_->gnu_bucket != nullptr) {
LOGD("Trying GNU hash lookup for %s", name);
uint32_t hash = relocator_->gnu_hash(name);
LOGD("GNU hash for %s: 0x%x", name, hash);
ElfW(Sym)* sym = relocator_->gnu_lookup(hash, name, loaded_si_);
if (sym != nullptr && sym->st_shndx != SHN_UNDEF) {
ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias;
LOGD("Found symbol %s via GNU hash: st_value=0x%llx, load_bias=0x%llx, final_addr=0x%llx",
name, (unsigned long long)sym->st_value, (unsigned long long)loaded_si_->load_bias, (unsigned long long)addr);
if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) {
return reinterpret_cast<void*>(addr);
} else {
LOGE("Symbol %s address 0x%llx out of range [0x%llx, 0x%llx)",
name, (unsigned long long)addr, (unsigned long long)loaded_si_->base,
(unsigned long long)(loaded_si_->base + loaded_si_->size));
}
} else {
LOGD("Symbol %s not found via GNU hash", name);
}
}
if (loaded_si_->bucket != nullptr) {
LOGD("Trying ELF hash lookup for %s", name);
unsigned hash = relocator_->elf_hash(name);
LOGD("ELF hash for %s: 0x%x", name, hash);
ElfW(Sym)* sym = relocator_->elf_lookup(hash, name, loaded_si_);
if (sym != nullptr && sym->st_shndx != SHN_UNDEF) {
ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias;
LOGD("Found symbol %s via ELF hash: st_value=0x%llx, load_bias=0x%llx, final_addr=0x%llx",
name, (unsigned long long)sym->st_value, (unsigned long long)loaded_si_->load_bias, (unsigned long long)addr);
if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) {
return reinterpret_cast<void*>(addr);
} else {
LOGE("Symbol %s address 0x%llx out of range [0x%llx, 0x%llx)",
name, (unsigned long long)addr, (unsigned long long)loaded_si_->base,
(unsigned long long)(loaded_si_->base + loaded_si_->size));
}
} else {
LOGD("Symbol %s not found via ELF hash", name);
}
}
if (loaded_si_->gnu_bucket == nullptr && loaded_si_->bucket == nullptr) {
LOGD("No hash tables available, trying linear search");
if (loaded_si_->strtab != nullptr) {
size_t sym_count = 0;
if (loaded_si_->nchain > 0) {
sym_count = loaded_si_->nchain;
} else {
LOGD("Cannot determine symbol table size");
return nullptr;
}
LOGD("Trying linear search with %zu symbols", sym_count);
for (size_t i = 0; i < sym_count; ++i) {
ElfW(Sym)* sym = &loaded_si_->symtab[i];
if (sym->st_name != 0 && sym->st_shndx != SHN_UNDEF) {
const char* sym_name = loaded_si_->strtab + sym->st_name;
if (strcmp(sym_name, name) == 0) {
ElfW(Addr) addr = sym->st_value + loaded_si_->load_bias;
LOGD("Found symbol %s via linear search: st_value=0x%llx, load_bias=0x%llx, final_addr=0x%llx",
name, (unsigned long long)sym->st_value, (unsigned long long)loaded_si_->load_bias, (unsigned long long)addr);
if (addr >= loaded_si_->base && addr < loaded_si_->base + loaded_si_->size) {
return reinterpret_cast<void*>(addr);
} else {
LOGE("Symbol %s address 0x%llx out of range [0x%llx, 0x%llx)",
name, (unsigned long long)addr, (unsigned long long)loaded_si_->base, (unsigned long long)(loaded_si_->base + loaded_si_->size));
}
}
}
}
LOGD("Symbol %s not found via linear search", name);
}
}
} else {
LOGE("Symbol table is null");
}
LOGE("Symbol %s not found in any method", name);
return nullptr;
}

View File

@@ -0,0 +1,149 @@
#include "elf_reader.h"
#include <sys/types.h>
ElfReader::ElfReader() : fd_(-1), file_size_(0), file_offset_(0),
mapped_file_(nullptr), phdr_table_(nullptr), phdr_num_(0) {
memset(&header_, 0, sizeof(header_));
}
ElfReader::~ElfReader() {
Close();
}
bool ElfReader::Open(const char* path) {
path_ = path;
struct stat sb;
fd_ = open(path, O_RDONLY | O_CLOEXEC);
if (fd_ < 0) {
LOGE("Cannot open %s: %s", path, strerror(errno));
return false;
}
if (fstat(fd_, &sb) < 0) {
LOGE("Cannot stat %s: %s", path, strerror(errno));
close(fd_);
fd_ = -1;
return false;
}
file_size_ = sb.st_size;
// 映射整个文件到内存
mapped_file_ = mmap(nullptr, file_size_, PROT_READ, MAP_PRIVATE, fd_, 0);
if (mapped_file_ == MAP_FAILED) {
LOGE("Cannot mmap %s: %s", path, strerror(errno));
close(fd_);
fd_ = -1;
return false;
}
return true;
}
bool ElfReader::Read() {
if (!ReadElfHeader()) {
return false;
}
if (!VerifyElfHeader()) {
return false;
}
if (!ReadProgramHeaders()) {
return false;
}
return true;
}
void ElfReader::Close() {
if (mapped_file_ != nullptr && mapped_file_ != MAP_FAILED) {
munmap(mapped_file_, file_size_);
mapped_file_ = nullptr;
}
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
if (phdr_table_ != nullptr) {
free(phdr_table_);
phdr_table_ = nullptr;
}
}
bool ElfReader::ReadElfHeader() {
if (file_size_ < sizeof(ElfW(Ehdr))) {
LOGE("File too small for ELF header");
return false;
}
memcpy(&header_, mapped_file_, sizeof(header_));
return true;
}
bool ElfReader::VerifyElfHeader() {
if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
LOGE("Invalid ELF magic");
return false;
}
if (header_.e_ident[EI_CLASS] != ELFCLASS64) {
LOGE("Not a 64-bit ELF file");
return false;
}
if (header_.e_machine != EM_AARCH64) {
LOGE("Not an ARM64 ELF file");
return false;
}
if (header_.e_version != EV_CURRENT) {
LOGE("Invalid ELF version");
return false;
}
if (header_.e_type != ET_DYN) {
LOGE("Not a shared object");
return false;
}
LOGD("ELF Header: type=%d, machine=%d, entry=0x%llx, phoff=0x%llx, phnum=%d",
header_.e_type, header_.e_machine, (unsigned long long)header_.e_entry,
(unsigned long long)header_.e_phoff, header_.e_phnum);
return true;
}
bool ElfReader::ReadProgramHeaders() {
phdr_num_ = header_.e_phnum;
if (phdr_num_ == 0) {
LOGE("No program headers");
return false;
}
if (header_.e_phentsize != sizeof(ElfW(Phdr))) {
LOGE("Invalid program header size");
return false;
}
size_t size = phdr_num_ * sizeof(ElfW(Phdr));
if (header_.e_phoff + size > file_size_) {
LOGE("Program headers out of file bounds");
return false;
}
phdr_table_ = static_cast<ElfW(Phdr)*>(malloc(size));
if (phdr_table_ == nullptr) {
LOGE("Cannot allocate memory for program headers");
return false;
}
memcpy(phdr_table_, static_cast<char*>(mapped_file_) + header_.e_phoff, size);
return true;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <android/log.h>
#include <elf.h>
#include <link.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#define LOG_TAG "CustomLinker"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#if defined(__LP64__)
#define ELFW(what) ELF64_ ## what
#else
#define ELFW(what) ELF32_ ## what
#endif
#define PAGE_SIZE 4096
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define PAGE_START(addr) ((addr) & PAGE_MASK)
#define PAGE_END(addr) PAGE_START((addr) + PAGE_SIZE - 1)
#define PAGE_OFFSET(addr) ((addr) & (PAGE_SIZE - 1))
// 权限标志转换
#define PFLAGS_TO_PROT(x) (((x) & PF_R) ? PROT_READ : 0) | \
(((x) & PF_W) ? PROT_WRITE : 0) | \
(((x) & PF_X) ? PROT_EXEC : 0)
struct soinfo;
class ElfReader;
class MemoryManager;
class Relocator;

View File

@@ -0,0 +1,27 @@
#pragma once
#include "common.h"
#include "elf_reader.h"
#include "memory_manager.h"
#include "soinfo_manager.h"
#include "relocator.h"
class ElfLoader {
public:
ElfLoader();
~ElfLoader();
bool LoadLibrary(const char* path);
void CallConstructors();
void* GetSymbol(const char* name);
private:
std::unique_ptr<ElfReader> reader_;
std::unique_ptr<MemoryManager> memory_manager_;
std::unique_ptr<SoinfoManager> soinfo_manager_;
std::unique_ptr<Relocator> relocator_;
soinfo* loaded_si_;
};

View File

@@ -0,0 +1,38 @@
#pragma once
#include "common.h"
class ElfReader {
public:
ElfReader();
~ElfReader();
bool Open(const char* path);
bool Read();
void Close();
const ElfW(Ehdr)* GetHeader() const { return &header_; }
const ElfW(Phdr)* GetProgramHeaders() const { return phdr_table_; }
size_t GetProgramHeaderCount() const { return phdr_num_; }
const char* GetPath() const { return path_.c_str(); }
int GetFd() const { return fd_; }
size_t GetFileSize() const { return file_size_; }
void* GetMappedAddr() const { return mapped_file_; }
private:
bool ReadElfHeader();
bool ReadProgramHeaders();
bool VerifyElfHeader();
std::string path_;
int fd_;
size_t file_size_;
off64_t file_offset_;
void* mapped_file_;
ElfW(Ehdr) header_;
ElfW(Phdr)* phdr_table_;
size_t phdr_num_;
};

View File

@@ -0,0 +1,34 @@
#pragma once
#include "common.h"
class MemoryManager {
public:
MemoryManager();
~MemoryManager();
bool ReserveAddressSpace(const ElfW(Phdr)* phdr_table, size_t phdr_num);
bool LoadSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num,
void* mapped_file, size_t file_size);
bool FindPhdr(const ElfW(Phdr)* phdr_table, size_t phdr_num);
bool ProtectSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num);
void* GetLoadStart() const { return load_start_; }
size_t GetLoadSize() const { return load_size_; }
ElfW(Addr) GetLoadBias() const { return load_bias_; }
const ElfW(Phdr)* GetLoadedPhdr() const { return loaded_phdr_; }
private:
bool CheckPhdr(ElfW(Addr) loaded, const ElfW(Phdr)* phdr_table, size_t phdr_num);
size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table,
size_t phdr_count,
ElfW(Addr)* min_vaddr);
void* load_start_;
size_t load_size_;
ElfW(Addr) load_bias_;
const ElfW(Phdr)* loaded_phdr_;
};

View File

@@ -0,0 +1,17 @@
#pragma once
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("default"))) bool mylinker_load_library(const char* library_path, JavaVM* vm);
__attribute__((visibility("default"))) void* mylinker_get_symbol(const char* library_path, const char* symbol_name);
__attribute__((visibility("default"))) void mylinker_cleanup();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,25 @@
#pragma once
#include "common.h"
#include "soinfo_manager.h"
class Relocator {
public:
Relocator();
~Relocator();
bool RelocateImage(soinfo* si);
bool LinkImage(soinfo* si);
uint32_t gnu_hash(const char* name);
unsigned elf_hash(const char* name);
ElfW(Sym)* gnu_lookup(uint32_t hash, const char* name, soinfo* si);
ElfW(Sym)* elf_lookup(unsigned hash, const char* name, soinfo* si);
private:
bool ProcessRelaRelocation(soinfo* si, const ElfW(Rela)* rela);
ElfW(Addr) FindSymbolAddress(const char* name, soinfo* si);
};

View File

@@ -0,0 +1,75 @@
#pragma once
#include "common.h"
// soinfo结构体定义(简化版)
struct soinfo {
const char* name;
ElfW(Addr) base;
size_t size;
ElfW(Addr) load_bias;
const ElfW(Phdr)* phdr;
size_t phnum;
ElfW(Addr) entry;
// Dynamic段信息
ElfW(Dyn)* dynamic;
size_t dynamic_count;
// 符号表相关
const char* strtab;
ElfW(Sym)* symtab;
size_t nbucket;
size_t nchain;
uint32_t* bucket;
uint32_t* chain;
// 重定位相关
ElfW(Rela)* plt_rela;
size_t plt_rela_count;
ElfW(Rela)* rela;
size_t rela_count;
// GNU hash
size_t gnu_nbucket;
uint32_t* gnu_bucket;
uint32_t* gnu_chain;
uint32_t gnu_maskwords;
uint32_t gnu_shift2;
ElfW(Addr)* gnu_bloom_filter;
// 初始化函数
void (*init_func)();
void (**init_array)();
size_t init_array_count;
void (**fini_array)();
size_t fini_array_count;
// 依赖库
std::vector<std::string> needed_libs;
uint32_t flags;
};
class SoinfoManager {
public:
SoinfoManager();
~SoinfoManager();
soinfo* GetOrCreateSoinfo(const char* name);
bool UpdateSoinfo(soinfo* si, MemoryManager* mm, ElfReader* reader);
bool PrelinkImage(soinfo* si);
soinfo* FindSoinfo(const char* name);
soinfo* GetCurrentSoinfo();
private:
bool ParseDynamic(soinfo* si);
void ApplyRelaSections(soinfo* si);
std::unordered_map<std::string, std::unique_ptr<soinfo>> soinfo_map_;
};

View File

@@ -0,0 +1,21 @@
#pragma once
#include "common.h"
namespace Utils {
bool safe_add(off64_t* out, off64_t a, size_t b);
soinfo* get_soinfo(const char* so_name);
void* getMapData(int fd, off64_t base_offset, size_t elf_offset, size_t size);
ElfW(Addr) get_export_func(const char* path, const char* func_name);
inline size_t page_start(size_t addr) {
return addr & ~(PAGE_SIZE - 1);
}
inline size_t page_offset(size_t addr) {
return addr & (PAGE_SIZE - 1);
}
}

View File

@@ -0,0 +1,49 @@
#include "elf_loader.h"
int (*yuuki_test_func) (int, int) = nullptr;
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s <so_file_path>\n", argv[0]);
return 1;
}
LOGI("Starting custom linker for: %s", argv[1]);
// 检查文件是否存在
if (access(argv[1], F_OK) != 0) {
LOGE("File does not exist: %s", argv[1]);
return 1;
}
if (access(argv[1], R_OK) != 0) {
LOGE("File is not readable: %s", argv[1]);
return 1;
}
ElfLoader loader;
if (loader.LoadLibrary(argv[1])) {
printf("Successfully loaded %s\n", argv[1]);
void* test_func = loader.GetSymbol("yuuki_test");
if (test_func) {
printf("Found yuuki_test function at %p\n", test_func);
yuuki_test_func = (int (*)(int, int)) test_func;
// 测试函数调用
printf("Testing function call: 1 + 1 = %d\n", yuuki_test_func(1, 1));
printf("Testing function call: 5 + 3 = %d\n", yuuki_test_func(5, 3));
} else {
printf("Failed to find yuuki_test function\n");
}
return 0;
} else {
printf("Failed to load %s\n", argv[1]);
return 1;
}
}
// logcat | grep "CustomLinker"
// logcat | grep "TEST_SO"
// ./data/local/tmp/elf_loader /storage/emulated/0/yuuki/test.so

View File

@@ -0,0 +1,241 @@
#include "memory_manager.h"
MemoryManager::MemoryManager() : load_start_(nullptr), load_size_(0),
load_bias_(0), loaded_phdr_(nullptr) {
}
MemoryManager::~MemoryManager() {
}
bool MemoryManager::ReserveAddressSpace(const ElfW(Phdr)* phdr_table, size_t phdr_num) {
ElfW(Addr) min_vaddr;
load_size_ = phdr_table_get_load_size(phdr_table, phdr_num, &min_vaddr);
if (load_size_ == 0) {
LOGE("No loadable segments");
return false;
}
LOGD("Load size: 0x%zx, min_vaddr: 0x%llx", load_size_, (unsigned long long)min_vaddr);
void* start = mmap(nullptr, load_size_, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (start == MAP_FAILED) {
LOGE("Cannot reserve %zu bytes: %s", load_size_, strerror(errno));
return false;
}
load_start_ = start;
load_bias_ = reinterpret_cast<ElfW(Addr)>(start) - min_vaddr;
LOGD("Reserved address space at %p, bias: 0x%llx", start, (unsigned long long)load_bias_);
return true;
}
bool MemoryManager::LoadSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num,
void* mapped_file, size_t file_size) {
LOGD("Starting LoadSegments: phdr_num=%zu, file_size=%zu", phdr_num, file_size);
for (size_t i = 0; i < phdr_num; ++i) {
const ElfW(Phdr)* phdr = &phdr_table[i];
if (phdr->p_type != PT_LOAD) {
continue;
}
LOGD("Processing LOAD segment %zu: vaddr=0x%llx, memsz=0x%llx, filesz=0x%llx, offset=0x%llx",
i, (unsigned long long)phdr->p_vaddr, (unsigned long long)phdr->p_memsz,
(unsigned long long)phdr->p_filesz, (unsigned long long)phdr->p_offset);
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;
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);
if (file_end > file_size) {
LOGE("Invalid file size: file_end=0x%llx > file_size=0x%zx", (unsigned long long)file_end, file_size);
return false;
}
if (phdr->p_filesz > 0) {
void* seg_addr = reinterpret_cast<void*>(seg_page_start);
size_t seg_size = seg_page_end - seg_page_start;
if (mprotect(seg_addr, seg_size, PROT_READ | PROT_WRITE) < 0) {
LOGE("Cannot mprotect for loading: %s", strerror(errno));
return false;
}
void* src = static_cast<char*>(mapped_file) + phdr->p_offset;
void* dst = reinterpret_cast<void*>(seg_start);
LOGD("Copying segment %zu: src=%p (offset=0x%llx), dst=%p, size=0x%llx",
i, src, (unsigned long long)phdr->p_offset, dst, (unsigned long long)phdr->p_filesz);
if (static_cast<char*>(src) + phdr->p_filesz > static_cast<char*>(mapped_file) + file_size) {
LOGE("Source copy would exceed file bounds");
return false;
}
if (reinterpret_cast<ElfW(Addr)>(dst) + phdr->p_filesz > seg_page_end) {
LOGE("Destination copy would exceed segment bounds");
return false;
}
memcpy(dst, src, phdr->p_filesz);
LOGD("Successfully copied segment %zu", i);
}
if (phdr->p_memsz > phdr->p_filesz) {
ElfW(Addr) bss_start = seg_start + phdr->p_filesz;
ElfW(Addr) bss_end = seg_start + phdr->p_memsz;
size_t bss_size = bss_end - bss_start;
LOGD("Zeroing BSS: start=0x%llx, size=0x%zx", (unsigned long long)bss_start, bss_size);
memset(reinterpret_cast<void*>(bss_start), 0, bss_size);
}
ElfW(Addr) aligned_file_end = PAGE_END(seg_file_end);
if (seg_page_end > aligned_file_end) {
size_t zeromap_size = seg_page_end - aligned_file_end;
void* zeromap = mmap(reinterpret_cast<void*>(aligned_file_end),
zeromap_size,
PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
if (zeromap == MAP_FAILED) {
LOGE("Cannot zero fill gap: %s", strerror(errno));
return false;
}
LOGD("Zero-filled gap: addr=%p, size=0x%zx", zeromap, zeromap_size);
}
}
LOGD("LoadSegments complete");
return true;
}
bool MemoryManager::FindPhdr(const ElfW(Phdr)* phdr_table, size_t phdr_num) {
const ElfW(Phdr)* phdr_limit = phdr_table + phdr_num;
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) {
if (phdr->p_type == PT_PHDR) {
return CheckPhdr(load_bias_ + phdr->p_vaddr, phdr_table, phdr_num);
}
}
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) {
if (phdr->p_type == PT_LOAD) {
if (phdr->p_offset == 0) {
ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr;
const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr);
ElfW(Addr) offset = ehdr->e_phoff;
return CheckPhdr(reinterpret_cast<ElfW(Addr)>(ehdr) + offset, phdr_table, phdr_num);
}
break;
}
}
LOGD("Using original phdr_table as loaded_phdr");
loaded_phdr_ = phdr_table;
return true;
}
bool MemoryManager::ProtectSegments(const ElfW(Phdr)* phdr_table, size_t phdr_num) {
for (size_t i = 0; i < phdr_num; ++i) {
const ElfW(Phdr)* phdr = &phdr_table[i];
if (phdr->p_type != PT_LOAD) {
continue;
}
ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
ElfW(Addr) seg_page_start = PAGE_START(seg_start);
ElfW(Addr) seg_page_end = PAGE_END(seg_start + phdr->p_memsz);
int prot = PFLAGS_TO_PROT(phdr->p_flags);
if (mprotect(reinterpret_cast<void*>(seg_page_start),
seg_page_end - seg_page_start, prot) < 0) {
LOGE("Cannot protect segment %zu: %s", i, strerror(errno));
return false;
}
LOGD("Protected segment %zu: 0x%llx-0x%llx, prot: %d",
i, (unsigned long long)seg_page_start, (unsigned long long)seg_page_end, prot);
}
return true;
}
bool MemoryManager::CheckPhdr(ElfW(Addr) loaded, const ElfW(Phdr)* phdr_table, size_t phdr_num) {
const ElfW(Phdr)* phdr_limit = phdr_table + phdr_num;
ElfW(Addr) loaded_end = loaded + (phdr_num * sizeof(ElfW(Phdr)));
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_limit; ++phdr) {
if (phdr->p_type != PT_LOAD) {
continue;
}
ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
ElfW(Addr) seg_end = phdr->p_filesz + seg_start;
if (seg_start <= loaded && loaded_end <= seg_end) {
loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded);
return true;
}
}
LOGE("Loaded phdr %p not in loadable segment", reinterpret_cast<void*>(loaded));
return false;
}
size_t MemoryManager::phdr_table_get_load_size(const ElfW(Phdr)* phdr_table,
size_t phdr_count,
ElfW(Addr)* min_vaddr) {
ElfW(Addr) min_addr = UINTPTR_MAX;
ElfW(Addr) max_addr = 0;
bool found_pt_load = false;
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_addr) {
min_addr = phdr->p_vaddr;
}
if (phdr->p_vaddr + phdr->p_memsz > max_addr) {
max_addr = phdr->p_vaddr + phdr->p_memsz;
}
}
if (!found_pt_load) {
return 0;
}
min_addr = PAGE_START(min_addr);
max_addr = PAGE_END(max_addr);
if (min_vaddr != nullptr) {
*min_vaddr = min_addr;
}
return max_addr - min_addr;
}

View File

@@ -0,0 +1,61 @@
#include "mylinker.h"
#include "elf_loader.h"
#include "common.h"
#include <unordered_map>
#include <memory>
#include <string>
static std::unordered_map<std::string, std::unique_ptr<ElfLoader>> loaded_libraries;
bool mylinker_load_library(const char* library_path, JavaVM* vm) {
if (!library_path) {
LOGE("Invalid library path");
return false;
}
std::string path(library_path);
if (loaded_libraries.find(path) != loaded_libraries.end()) {
LOGI("Library already loaded: %s", library_path);
return true;
}
auto loader = std::make_unique<ElfLoader>();
if (!loader->LoadLibrary(library_path)) {
LOGE("Failed to load library: %s", library_path);
return false;
}
JNIEnv* env = nullptr;
if (vm && vm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_OK) {
typedef jint (*JNI_OnLoad_t)(JavaVM*, void*);
auto jni_onload = reinterpret_cast<JNI_OnLoad_t>(loader->GetSymbol("JNI_OnLoad"));
if (jni_onload) {
LOGI("Calling JNI_OnLoad");
jni_onload(vm, nullptr);
}
}
loaded_libraries[path] = std::move(loader);
LOGI("Successfully loaded library: %s", library_path);
return true;
}
void* mylinker_get_symbol(const char* library_path, const char* symbol_name) {
if (!library_path || !symbol_name) {
return nullptr;
}
auto it = loaded_libraries.find(library_path);
if (it == loaded_libraries.end()) {
LOGE("Library not loaded: %s", library_path);
return nullptr;
}
return it->second->GetSymbol(symbol_name);
}
void mylinker_cleanup() {
loaded_libraries.clear();
LOGI("Cleaned up all loaded libraries");
}

View File

@@ -0,0 +1,342 @@
#include "relocator.h"
// Only define if not already defined
#ifndef R_AARCH64_NONE
#define R_AARCH64_NONE 0
#endif
#ifndef R_AARCH64_ABS64
#define R_AARCH64_ABS64 257
#endif
#ifndef R_AARCH64_GLOB_DAT
#define R_AARCH64_GLOB_DAT 1025
#endif
#ifndef R_AARCH64_JUMP_SLOT
#define R_AARCH64_JUMP_SLOT 1026
#endif
#ifndef R_AARCH64_RELATIVE
#define R_AARCH64_RELATIVE 1027
#endif
#ifndef R_AARCH64_TLS_TPREL64
#define R_AARCH64_TLS_TPREL64 1030
#endif
#ifndef R_AARCH64_TLS_DTPREL32
#define R_AARCH64_TLS_DTPREL32 1031
#endif
#ifndef R_AARCH64_IRELATIVE
#define R_AARCH64_IRELATIVE 1032
#endif
Relocator::Relocator() {
}
Relocator::~Relocator() {
}
bool Relocator::RelocateImage(soinfo* si) {
LOGD("Starting relocation for %s", si->name);
if (!si) {
LOGE("soinfo is null");
return false;
}
if (si->rela != nullptr && si->rela_count > 0) {
LOGD("Processing %zu RELA relocations", si->rela_count);
if (si->rela_count > 100000) {
LOGE("RELA count too large: %zu", si->rela_count);
return false;
}
for (size_t i = 0; i < si->rela_count; ++i) {
if (!ProcessRelaRelocation(si, &si->rela[i])) {
LOGE("Failed to process RELA relocation %zu", i);
// 继续处理其他重定位,不要因为一个失败就退出
// return false;
}
}
} else {
LOGD("No RELA relocations to process");
}
if (si->plt_rela != nullptr && si->plt_rela_count > 0) {
LOGD("Processing %zu PLT RELA relocations", si->plt_rela_count);
if (si->plt_rela_count > 10000) {
LOGE("PLT RELA count too large: %zu", si->plt_rela_count);
return false;
}
for (size_t i = 0; i < si->plt_rela_count; ++i) {
if (!ProcessRelaRelocation(si, &si->plt_rela[i])) {
LOGE("Failed to process PLT RELA relocation %zu", i);
// 继续处理其他重定位
// return false;
}
}
} else {
LOGD("No PLT RELA relocations to process");
}
LOGD("Relocation complete for %s", si->name);
return true;
}
bool Relocator::LinkImage(soinfo* si) {
if (!si) {
LOGE("soinfo is null in LinkImage");
return false;
}
if (!RelocateImage(si)) {
LOGE("Failed to relocate image");
return false;
}
if (si->init_func != nullptr) {
LOGD("Calling init function at %p", si->init_func);
si->init_func();
}
if (si->init_array != nullptr && si->init_array_count > 0) {
LOGD("Calling %zu init_array functions", si->init_array_count);
if (si->init_array_count > 1000) {
LOGE("init_array_count too large: %zu", si->init_array_count);
return false;
}
for (size_t i = 0; i < si->init_array_count; ++i) {
void (*func)() = si->init_array[i];
if (func != nullptr) {
LOGD("Calling init_array[%zu] at %p", i, func);
func();
}
}
}
return true;
}
bool Relocator::ProcessRelaRelocation(soinfo* si, const ElfW(Rela)* rela) {
if (!si || !rela) {
LOGE("Invalid parameters in ProcessRelaRelocation");
return false;
}
ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rela->r_offset + si->load_bias);
ElfW(Word) type = ELFW(R_TYPE)(rela->r_info);
ElfW(Word) sym = ELFW(R_SYM)(rela->r_info);
LOGD("Processing relocation: offset=0x%llx, type=%d, sym=%d, addend=0x%llx",
(unsigned long long)rela->r_offset, type, sym, (long long)rela->r_addend);
if (reloc < si->base || reloc >= si->base + si->size) {
LOGE("Relocation address 0x%llx out of range [0x%llx, 0x%llx)",
(unsigned long long)reloc, (unsigned long long)si->base, (unsigned long long)(si->base + si->size));
return false;
}
ElfW(Addr) sym_addr = 0;
const char* sym_name = nullptr;
if (sym != 0) {
if (!si->symtab) {
LOGE("Symbol table is null");
return false;
}
const ElfW(Sym)* s = &si->symtab[sym];
if (si->strtab && s->st_name != 0) {
sym_name = si->strtab + s->st_name;
LOGD("Symbol name: %s", sym_name);
}
if (s->st_shndx != SHN_UNDEF) {
sym_addr = s->st_value + si->load_bias;
LOGD("Local symbol: addr=0x%llx", (unsigned long long)sym_addr);
} else if (sym_name) {
sym_addr = FindSymbolAddress(sym_name, si);
if (sym_addr == 0) {
LOGD("Cannot find symbol: %s (may be optional)", sym_name);
}
}
}
void* page_start = reinterpret_cast<void*>(PAGE_START(reloc));
size_t page_size = PAGE_SIZE;
int old_prot = PROT_READ | PROT_WRITE;
if (mprotect(page_start, page_size, old_prot) != 0) {
LOGD("mprotect failed for relocation, trying anyway: %s", strerror(errno));
}
switch (type) {
case R_AARCH64_NONE:
LOGD("R_AARCH64_NONE");
break;
case R_AARCH64_ABS64:
LOGD("R_AARCH64_ABS64: writing 0x%llx to 0x%llx",
(unsigned long long)(sym_addr + rela->r_addend), (unsigned long long)reloc);
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
break;
case R_AARCH64_GLOB_DAT:
LOGD("R_AARCH64_GLOB_DAT: writing 0x%llx to 0x%llx",
(unsigned long long)(sym_addr + rela->r_addend), (unsigned long long)reloc);
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
break;
case R_AARCH64_JUMP_SLOT:
LOGD("R_AARCH64_JUMP_SLOT: writing 0x%llx to 0x%llx",
(unsigned long long)(sym_addr + rela->r_addend), (unsigned long long)reloc);
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
break;
case R_AARCH64_RELATIVE:
LOGD("R_AARCH64_RELATIVE: writing 0x%llx to 0x%llx",
(unsigned long long)(si->load_bias + rela->r_addend), (unsigned long long)reloc);
*reinterpret_cast<ElfW(Addr)*>(reloc) = si->load_bias + rela->r_addend;
break;
case R_AARCH64_IRELATIVE:
{
ElfW(Addr) resolver = si->load_bias + rela->r_addend;
LOGD("R_AARCH64_IRELATIVE: resolver at 0x%llx", (unsigned long long)resolver);
if (resolver < si->base || resolver >= si->base + si->size) {
LOGE("Invalid resolver address: 0x%llx", (unsigned long long)resolver);
return false;
}
ElfW(Addr) resolved = ((ElfW(Addr) (*)())resolver)();
*reinterpret_cast<ElfW(Addr)*>(reloc) = resolved;
LOGD("R_AARCH64_IRELATIVE: resolved to 0x%llx", (unsigned long long)resolved);
}
break;
default:
LOGD("Unknown relocation type %d, skipping", type);
break;
}
return true;
}
ElfW(Addr) Relocator::FindSymbolAddress(const char* name, soinfo* si) {
if (!name || !si) {
return 0;
}
if (si->symtab != nullptr) {
if (si->gnu_bucket != nullptr) {
uint32_t hash = gnu_hash(name);
ElfW(Sym)* sym = gnu_lookup(hash, name, si);
if (sym != nullptr && sym->st_shndx != SHN_UNDEF) {
ElfW(Addr) addr = sym->st_value + si->load_bias;
LOGD("Found symbol %s in current SO at 0x%llx", name, (unsigned long long)addr);
return addr;
}
}
if (si->bucket != nullptr) {
unsigned hash = elf_hash(name);
ElfW(Sym)* sym = elf_lookup(hash, name, si);
if (sym != nullptr && sym->st_shndx != SHN_UNDEF) {
ElfW(Addr) addr = sym->st_value + si->load_bias;
LOGD("Found symbol %s in current SO at 0x%llx", name, (unsigned long long)addr);
return addr;
}
}
}
for (const auto& lib : si->needed_libs) {
void* handle = dlopen(lib.c_str(), RTLD_NOW | RTLD_NOLOAD);
if (handle != nullptr) {
void* addr = dlsym(handle, name);
if (addr != nullptr) {
LOGD("Found symbol %s in %s at %p", name, lib.c_str(), addr);
dlclose(handle);
return reinterpret_cast<ElfW(Addr)>(addr);
}
dlclose(handle);
}
}
void* addr = dlsym(RTLD_DEFAULT, name);
if (addr != nullptr) {
LOGD("Found symbol %s globally at %p", name, addr);
return reinterpret_cast<ElfW(Addr)>(addr);
}
LOGD("Symbol %s not found", name);
return 0;
}
ElfW(Sym)* Relocator::gnu_lookup(uint32_t hash, const char* name, soinfo* si) {
if (!si->gnu_bucket || !si->gnu_chain || !si->symtab || !si->strtab) {
return nullptr;
}
uint32_t h2 = hash >> si->gnu_shift2;
uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8;
uint32_t word_num = (hash / bloom_mask_bits) & si->gnu_maskwords;
ElfW(Addr) bloom_word = si->gnu_bloom_filter[word_num];
if ((1 & (bloom_word >> (hash % bloom_mask_bits)) &
(bloom_word >> (h2 % bloom_mask_bits))) == 0) {
return nullptr;
}
uint32_t n = si->gnu_bucket[hash % si->gnu_nbucket];
if (n == 0) {
return nullptr;
}
do {
ElfW(Sym)* s = si->symtab + n;
if (((si->gnu_chain[n] ^ hash) >> 1) == 0 &&
strcmp(si->strtab + s->st_name, name) == 0) {
return s;
}
} while ((si->gnu_chain[n++] & 1) == 0);
return nullptr;
}
ElfW(Sym)* Relocator::elf_lookup(unsigned hash, const char* name, soinfo* si) {
if (!si->bucket || !si->chain || !si->symtab || !si->strtab) {
return nullptr;
}
for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
ElfW(Sym)* s = si->symtab + n;
if (s->st_name != 0 && strcmp(si->strtab + s->st_name, name) == 0) {
return s;
}
}
return nullptr;
}
uint32_t Relocator::gnu_hash(const char* name) {
uint32_t h = 5381;
for (const uint8_t* c = reinterpret_cast<const uint8_t*>(name); *c != '\0'; c++) {
h += (h << 5) + *c;
}
return h;
}
unsigned Relocator::elf_hash(const char* name) {
unsigned h = 0, g;
for (const unsigned char* p = reinterpret_cast<const unsigned char*>(name); *p; p++) {
h = (h << 4) + *p;
g = h & 0xf0000000;
h ^= g;
h ^= g >> 24;
}
return h;
}

View File

@@ -0,0 +1,312 @@
#include "soinfo_manager.h"
#include "utils.h"
#include "memory_manager.h"
#include "elf_reader.h"
SoinfoManager::SoinfoManager() {
}
SoinfoManager::~SoinfoManager() {
}
soinfo* SoinfoManager::GetOrCreateSoinfo(const char* name) {
auto it = soinfo_map_.find(name);
if (it != soinfo_map_.end()) {
return it->second.get();
}
auto si = std::make_unique<soinfo>();
memset(si.get(), 0, sizeof(soinfo));
si->name = strdup(name);
soinfo* result = si.get();
soinfo_map_[name] = std::move(si);
return result;
}
bool SoinfoManager::UpdateSoinfo(soinfo* si, MemoryManager* mm, ElfReader* reader) {
if (!si || !mm || !reader) {
return false;
}
si->base = reinterpret_cast<ElfW(Addr)>(mm->GetLoadStart());
si->size = mm->GetLoadSize();
si->load_bias = mm->GetLoadBias();
const ElfW(Phdr)* loaded_phdr = mm->GetLoadedPhdr();
if (loaded_phdr != nullptr) {
si->phdr = loaded_phdr;
} else {
si->phdr = reader->GetProgramHeaders();
LOGD("Using original program headers");
}
si->phnum = reader->GetProgramHeaderCount();
const ElfW(Ehdr)* header = reader->GetHeader();
si->entry = si->load_bias + header->e_entry;
LOGD("Updated soinfo: base=0x%llx, size=0x%zx, bias=0x%llx, entry=0x%llx, phdr=%p",
(unsigned long long)si->base, si->size, (unsigned long long)si->load_bias,
(unsigned long long)si->entry, si->phdr);
return true;
}
bool SoinfoManager::PrelinkImage(soinfo* si) {
LOGD("Starting PrelinkImage for %s", si->name);
if (!ParseDynamic(si)) {
LOGE("Failed to parse dynamic section");
return false;
}
if (si->strtab != nullptr && si->dynamic != nullptr) {
for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_NEEDED && si->needed_libs.empty()) {
const char* needed = si->strtab + d->d_un.d_val;
si->needed_libs.push_back(needed);
LOGD("Processing deferred DT_NEEDED: %s", needed);
}
}
}
ApplyRelaSections(si);
LOGD("PrelinkImage complete for %s", si->name);
return true;
}
soinfo* SoinfoManager::FindSoinfo(const char* name) {
auto it = soinfo_map_.find(name);
if (it != soinfo_map_.end()) {
return it->second.get();
}
return nullptr;
}
soinfo* SoinfoManager::GetCurrentSoinfo() {
return Utils::get_soinfo("libcustom_linker.so");
}
bool SoinfoManager::ParseDynamic(soinfo* si) {
if (!si || !si->phdr) {
LOGE("Invalid soinfo or phdr is null");
return false;
}
LOGD("Starting ParseDynamic: phdr=%p, phnum=%zu", si->phdr, si->phnum);
const ElfW(Phdr)* phdr_limit = si->phdr + si->phnum;
bool found_dynamic = false;
for (const ElfW(Phdr)* phdr = si->phdr; phdr < phdr_limit; ++phdr) {
if (phdr->p_type == PT_DYNAMIC) {
si->dynamic = reinterpret_cast<ElfW(Dyn)*>(si->load_bias + phdr->p_vaddr);
si->dynamic_count = phdr->p_memsz / sizeof(ElfW(Dyn));
found_dynamic = true;
LOGD("Found PT_DYNAMIC at vaddr=0x%llx, memsz=0x%llx",
(unsigned long long)phdr->p_vaddr, (unsigned long long)phdr->p_memsz);
break;
}
}
if (!found_dynamic || !si->dynamic) {
LOGE("No PT_DYNAMIC segment found");
return false;
}
LOGD("Dynamic section at %p, count=%zu", si->dynamic, si->dynamic_count);
if (si->dynamic_count == 0 || si->dynamic_count > 1000) {
LOGE("Invalid dynamic count: %zu", si->dynamic_count);
return false;
}
size_t dyn_count = 0;
for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL && dyn_count < si->dynamic_count; ++d, ++dyn_count) {
LOGD("Processing dynamic entry %zu: tag=0x%llx, val/ptr=0x%llx",
dyn_count, (unsigned long long)d->d_tag, (unsigned long long)d->d_un.d_val);
switch (d->d_tag) {
case DT_SYMTAB:
si->symtab = reinterpret_cast<ElfW(Sym)*>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_SYMTAB: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->symtab);
break;
case DT_STRTAB:
si->strtab = reinterpret_cast<const char*>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_STRTAB: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->strtab);
break;
case DT_STRSZ:
LOGD("DT_STRSZ: %lu", (unsigned long)d->d_un.d_val);
break;
case DT_HASH: {
uint32_t* hash = reinterpret_cast<uint32_t*>(si->load_bias + d->d_un.d_ptr);
si->nbucket = hash[0];
si->nchain = hash[1];
si->bucket = hash + 2;
si->chain = si->bucket + si->nbucket;
LOGD("DT_HASH: raw_ptr=0x%llx, nbucket=%zu, nchain=%zu",
(unsigned long long)d->d_un.d_ptr, si->nbucket, si->nchain);
break;
}
case DT_GNU_HASH: {
uint32_t* hash = reinterpret_cast<uint32_t*>(si->load_bias + d->d_un.d_ptr);
si->gnu_nbucket = hash[0];
uint32_t symbias = hash[1];
si->gnu_maskwords = hash[2];
si->gnu_shift2 = hash[3];
si->gnu_bloom_filter = reinterpret_cast<ElfW(Addr)*>(hash + 4);
si->gnu_bucket = reinterpret_cast<uint32_t*>(si->gnu_bloom_filter + si->gnu_maskwords);
si->gnu_chain = si->gnu_bucket + si->gnu_nbucket - symbias;
LOGD("DT_GNU_HASH: raw_ptr=0x%llx, nbucket=%zu, symbias=%u",
(unsigned long long)d->d_un.d_ptr, si->gnu_nbucket, symbias);
break;
}
case DT_JMPREL:
si->plt_rela = reinterpret_cast<ElfW(Rela)*>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_JMPREL: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->plt_rela);
break;
case DT_PLTRELSZ:
si->plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
LOGD("DT_PLTRELSZ: raw_val=%lu, count=%zu",
(unsigned long)d->d_un.d_val, si->plt_rela_count);
break;
case DT_PLTREL:
LOGD("DT_PLTREL: %lu", (unsigned long)d->d_un.d_val);
break;
case DT_RELA:
si->rela = reinterpret_cast<ElfW(Rela)*>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_RELA: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->rela);
break;
case DT_RELASZ:
si->rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
LOGD("DT_RELASZ: raw_val=%lu, count=%zu",
(unsigned long)d->d_un.d_val, si->rela_count);
break;
case DT_RELAENT:
LOGD("DT_RELAENT: %lu", (unsigned long)d->d_un.d_val);
break;
case DT_INIT:
si->init_func = reinterpret_cast<void (*)()>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_INIT: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->init_func);
break;
case DT_INIT_ARRAY:
si->init_array = reinterpret_cast<void (**)()>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_INIT_ARRAY: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->init_array);
break;
case DT_INIT_ARRAYSZ:
si->init_array_count = d->d_un.d_val / sizeof(void*);
LOGD("DT_INIT_ARRAYSZ: raw_val=%lu, count=%zu",
(unsigned long)d->d_un.d_val, si->init_array_count);
break;
case DT_FINI:
LOGD("DT_FINI: 0x%llx", (unsigned long long)d->d_un.d_ptr);
break;
case DT_FINI_ARRAY:
si->fini_array = reinterpret_cast<void (**)()>(si->load_bias + d->d_un.d_ptr);
LOGD("DT_FINI_ARRAY: raw_ptr=0x%llx, final_addr=%p",
(unsigned long long)d->d_un.d_ptr, si->fini_array);
break;
case DT_FINI_ARRAYSZ:
si->fini_array_count = d->d_un.d_val / sizeof(void*);
LOGD("DT_FINI_ARRAYSZ: raw_val=%lu, count=%zu",
(unsigned long)d->d_un.d_val, si->fini_array_count);
break;
case DT_FLAGS:
si->flags = d->d_un.d_val;
LOGD("DT_FLAGS: 0x%x", si->flags);
break;
case DT_FLAGS_1:
LOGD("DT_FLAGS_1: 0x%llx", (unsigned long long)d->d_un.d_val);
break;
case DT_SONAME:
LOGD("DT_SONAME: offset=%lu", (unsigned long)d->d_un.d_val);
break;
case DT_RUNPATH:
LOGD("DT_RUNPATH: offset=%lu", (unsigned long)d->d_un.d_val);
break;
case DT_NEEDED:
// 跳过,稍后处理
LOGD("DT_NEEDED: offset=%lu (deferred)", (unsigned long)d->d_un.d_val);
break;
default:
LOGD("Unknown dynamic tag: 0x%llx, value=0x%llx",
(unsigned long long)d->d_tag, (unsigned long long)d->d_un.d_val);
break;
}
// 添加安全检查,防止无限循环
if (dyn_count > si->dynamic_count) {
LOGE("Dynamic parsing exceeded expected count");
break;
}
}
if (si->symtab == nullptr) {
LOGD("Warning: DT_SYMTAB not found or is null");
}
if (si->strtab == nullptr) {
LOGD("Warning: DT_STRTAB not found or is null");
}
if (si->strtab != nullptr) {
dyn_count = 0;
for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL && dyn_count < si->dynamic_count; ++d, ++dyn_count) {
if (d->d_tag == DT_NEEDED) {
if (d->d_un.d_val < 65536) {
const char* needed = si->strtab + d->d_un.d_val;
if (strlen(needed) > 0 && strlen(needed) < 256) {
si->needed_libs.push_back(needed);
LOGD("DT_NEEDED: %s", needed);
} else {
LOGD("DT_NEEDED: invalid string at offset %lu", (unsigned long)d->d_un.d_val);
}
} else {
LOGD("DT_NEEDED: offset too large: %lu", (unsigned long)d->d_un.d_val);
}
}
}
}
LOGD("Dynamic parsing complete: symtab=%p, strtab=%p, needed_libs=%zu",
si->symtab, si->strtab, si->needed_libs.size());
return true;
}
void SoinfoManager::ApplyRelaSections(soinfo* si) {
LOGD("RELA sections: rela_count=%zu, plt_rela_count=%zu",
si->rela_count, si->plt_rela_count);
}

View File

@@ -0,0 +1,157 @@
#include "utils.h"
namespace Utils {
bool safe_add(off64_t* out, off64_t a, size_t b) {
if (a < 0 || __builtin_add_overflow(a, b, out)) {
return false;
}
return true;
}
soinfo* get_soinfo(const char* so_name) {
typedef soinfo* (*FunctionPtr)(ElfW(Addr));
char line[1024];
ElfW(Addr) linker_base = 0;
ElfW(Addr) so_addr = 0;
FILE* fp = fopen("/proc/self/maps", "r");
if (!fp) {
LOGE("Cannot open /proc/self/maps");
return nullptr;
}
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "linker64") && !linker_base) {
char* addr = strtok(line, "-");
linker_base = strtoull(addr, nullptr, 16);
} else if (strstr(line, so_name) && !so_addr) {
char* addr = strtok(line, "-");
so_addr = strtoull(addr, nullptr, 16);
}
if (linker_base && so_addr) {
break;
}
}
fclose(fp);
if (!linker_base || !so_addr) {
LOGE("Cannot find addresses");
return nullptr;
}
ElfW(Addr) func_offset = get_export_func("/system/bin/linker64", "find_containing_library");
if (!func_offset) {
LOGE("Cannot find find_containing_library");
return nullptr;
}
ElfW(Addr) find_containing_library_addr = linker_base + func_offset;
FunctionPtr find_containing_library = reinterpret_cast<FunctionPtr>(find_containing_library_addr);
return find_containing_library(so_addr);
}
void* getMapData(int fd, off64_t base_offset, size_t elf_offset, size_t size) {
off64_t offset;
if (!safe_add(&offset, base_offset, elf_offset)) {
return nullptr;
}
off64_t page_min = page_start(offset);
off64_t end_offset;
if (!safe_add(&end_offset, offset, size)) {
return nullptr;
}
if (!safe_add(&end_offset, end_offset, page_offset(offset))) {
return nullptr;
}
size_t map_size = static_cast<size_t>(end_offset - page_min);
uint8_t* map_start = static_cast<uint8_t*>(
mmap(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min));
if (map_start == MAP_FAILED) {
return nullptr;
}
return map_start + page_offset(offset);
}
ElfW(Addr) get_export_func(const char* path, const char* func_name) {
struct stat sb;
int fd = open(path, O_RDONLY);
if (fd < 0) {
return 0;
}
if (fstat(fd, &sb) < 0) {
close(fd);
return 0;
}
void* base = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (base == MAP_FAILED) {
close(fd);
return 0;
}
ElfW(Ehdr) header;
memcpy(&header, base, sizeof(header));
size_t shdr_size = header.e_shnum * sizeof(ElfW(Shdr));
ElfW(Shdr)* shdr_table = static_cast<ElfW(Shdr)*>(malloc(shdr_size));
memcpy(shdr_table, static_cast<char*>(base) + header.e_shoff, shdr_size);
char* shstrtab = static_cast<char*>(base) + shdr_table[header.e_shstrndx].sh_offset;
void* symtab = nullptr;
char* strtab = nullptr;
uint32_t symtab_size = 0;
for (size_t i = 0; i < header.e_shnum; ++i) {
const ElfW(Shdr)* shdr = &shdr_table[i];
char* section_name = shstrtab + shdr->sh_name;
if (strcmp(section_name, ".symtab") == 0) {
symtab = static_cast<char*>(base) + shdr->sh_offset;
symtab_size = shdr->sh_size;
}
if (strcmp(section_name, ".strtab") == 0) {
strtab = static_cast<char*>(base) + shdr->sh_offset;
}
if (strtab && symtab) {
break;
}
}
ElfW(Addr) result = 0;
if (symtab && strtab) {
ElfW(Sym)* sym_table = static_cast<ElfW(Sym)*>(symtab);
int sym_num = symtab_size / sizeof(ElfW(Sym));
for (int i = 0; i < sym_num; i++) {
const ElfW(Sym)* sym = &sym_table[i];
char* sym_name = strtab + sym->st_name;
if (strstr(sym_name, func_name)) {
result = sym->st_value;
break;
}
}
}
free(shdr_table);
munmap(base, sb.st_size);
close(fd);
return result;
}
}