我根据android5.0的linker源码,自己实现了一个加载64位so的功能,将加载so文件换成了加载so的字节数组,不过有一个问题
for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_NEEDED) {
const char* library_name = si->strtab + d->d_un.d_val;
DEBUG("%s needs %s", si->name, library_name);
soinfo* lsi = find_library(library_name, 0, NULL);
if (lsi == NULL) {
strlcpy(tmp_err_buf, linker_get_error_buffer(), sizeof(tmp_err_buf));
DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
library_name, si->name, tmp_err_buf);
return false;
}
si->add_child(lsi);
*pneeded++ = lsi;
}
}
我看网上有人将 soinfo lsi = find_library(library_name, 0, NULL)*; 修改为了dlopen,并且因为7.0后没有返回soinfo,它这里就没有做 si->add_child(lsi);处理
soinfo_relocate中:
if (sym != 0) {
sym_name = reinterpret_cast<const char*>(si->strtab + si->symtab[sym].st_name);
_ s = soinfo_do_lookup(si, sym_name, &lsi, needed);_
if (s == NULL) {
// We only allow an undefined symbol if this is a weak reference...
s = &si->symtab[sym];
if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, si->name);
return -1;
}
s = soinfo_do_lookup(si, sym_name, &lsi, needed); 这里,直接用dlsym返回地址
我参考的是这个地址:https://blog.csdn.net/u010559109/article/details/120460886
请问有没有试过内存加载so字节数组的。代码修改完成后,经常闪退,找不到原因
Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7058ce0760 in tid 8737 (pool-1-thread-1), pid 8686 (com.test.loader)
Android5.0源码:http://aospxref.com/android-5.0.2_r3/xref/bionic/linker/
附部分我结合linker5.0源码修改后的代码
bool ElfReader::ReadElfHeader() {
memcpy(&header_, elfData_, sizeof(header_));
return true;
}
void *mmap_result = mmap(NULL, phdr_size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (mmap_result == MAP_FAILED) {
DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno));
return false;
}
memcpy(mmap_result, elfData_ + page_min, phdr_size_);
phdr_mmap_ = mmap_result;
phdr_table_ = reinterpret_cast<ElfW(Phdr) *>(reinterpret_cast<char *>(mmap_result) +
page_offset);
mprotect(reinterpret_cast<void *>(seg_page_start), seg_page_end - seg_page_start,
PROT_WRITE);
if (file_length != 0) {
memcpy(reinterpret_cast<void *>(seg_page_start), elfData_ + file_page_start,
file_length);
}
if (sym != 0) {
bool isNeed = false;
sym_name = reinterpret_cast<const char *>(si->strtab + si->symtab[sym].st_name);
unsigned elf_hash = elfhash(sym_name);
//先从本si拿
ElfW(Sym) *s = soinfo_elf_lookup(si, elf_hash, sym_name);
if (s != NULL) {
sym_addr = static_cast<ElfW(Addr)>(s->st_value + si->load_bias);
} else {
//再从依赖里面拿
for (int i = 0; i < needed_count; i++) {
uintptr_t value = needed[i];
void *handle = reinterpret_cast<void *>(value);
sym_addr = reinterpret_cast<ElfW(Addr)>(dlsym(handle, sym_name));
if (sym_addr) {
isNeed = true;
break;
}
}
}
if (s == NULL && !isNeed) {
// We only allow an undefined symbol if this is a weak reference...
s = &si->symtab[sym];
if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name,
si->name);
return -1;
}
每行都加日志,看看哪里发生的踩内存
引用 皆我百晓生 小程序回复内容作答:
问题的原因可能是在加载so的字节数组时发生了内存访问错误。SIGSEGV信号表示访问了无效的内存地址。
有几个可能的原因需要排查:
检查加载的字节数组是否正确,确保没有发生内存溢出或内存错位的情况。
确保字节数组的指针和长度正确传递给了加载函数。
检查加载函数中和字节数组有关的操作,确保没有发生越界访问。
检查在加载和使用so的过程中是否有其他地方引起的内存错误。
对于问题的调试,建议使用调试器或日志记录来查看具体的错误发生位置和调用过程。这样可以更容易地确定导致闪退的原因,并进行修复。
援引讯飞星火:
根据您的描述,您在加载so字节数组时遇到了闪退的问题。这可能是由于内存访问越界导致的。为了解决这个问题,您可以尝试以下方法:
检查您的代码中是否有内存访问越界的情况。例如,确保您在访问数组、指针等数据结构时不会超出其边界。
使用调试工具(如Android Studio的调试器)来定位问题。通过设置断点和查看变量值,您可以更好地了解程序在运行过程中的状态,从而找到问题所在。
仔细检查您的代码逻辑,确保在处理字节数组时没有错误。例如,确保您正确地解析了字节数组,并将其转换为了正确的数据结构。
如果可能的话,尝试使用现有的库(如Android NDK)来加载so文件,而不是自己实现。这样可以避免许多潜在的问题,并提高代码的稳定性。
最后,确保您的设备或模拟器支持您正在使用的so文件。如果不支持,您可能需要更新设备的系统版本或使用其他设备进行测试。
【以下回答由 GPT 生成】
这个问题涉及到Android linker加载so字节数组的实现方式。在给出答案之前,我需要指出在Android系统中使用dlopen
和dlsym
来加载和获取so的地址是不被推荐的。
首先,关于soinfo* lsi = find_library(library_name, 0, NULL)
是否可以改为dlopen
,这个修改是不可行的。因为find_library
函数在linker中是用来查找so库,并返回对应的soinfo
结构体指针,而dlopen
函数是用来动态加载so库,并返回一个void*
类型的句柄,无法直接替代。
其次,关于s = soinfo_do_lookup(si, sym_name, &lsi, needed)
是否可以直接使用dlsym
,也是不可行的。因为soinfo_do_lookup
函数在linker中是用来根据符号名查找并返回对应的符号地址,而dlsym
函数是根据句柄和符号名来查找并返回对应的符号地址,无法直接替代。
所以,使用dlopen
和dlsym
来加载和获取so的地址是不正确的实现方式。
如果你想通过字节数组加载so库,我会推荐你使用以下步骤:
将so库的字节数组保存在一个文件中,比如"libtest.so"。可以通过将字节数组写入文件来实现。
在代码中使用dlopen
函数来加载so库。参考以下示例代码:
c void* handle = dlopen("/path/to/libtest.so", RTLD_NOW); if (handle == NULL) { const char* error = dlerror(); // 处理加载失败的情况 // ... }
在dlopen
中,你需要传入so库文件的路径,这里的路径可以是文件系统路径或者网络路径,根据你的实际需求进行适配。
dlsym
函数来获取so库中的符号地址。参考以下示例代码:c void (*myFunction)() = (void (*)())dlsym(handle, "myFunction"); if (myFunction == NULL) { const char* error = dlerror(); // 处理获取符号地址失败的情况 // ... }
在dlsym
中,你需要传入句柄和符号名,这里的符号名是在so库中定义的函数或者变量名。
注意,在使用dlopen
和dlsym
加载和获取so的地址之前,你需要在代码中包含<dlfcn.h>
头文件。
综上所述,正确的实现方式是使用dlopen
和dlsym
函数来加载和获取so的地址,但不能直接替换原有的find_library
和soinfo_do_lookup
函数的调用。如果你想通过字节数组加载so库,你可以将字节数组保存为文件,并使用dlopen
和dlsym
来加载和获取so的地址。