引言

从 Android 7.0 开始,系统将阻止应用动态链接非公开 NDK 库 ,直接影响就是在 7.0 以后 dlopen 和dlsym 函数将受到限制。如果直接调用,logcat 可能会生成一个警告或运行时错误。

void *handler = dlopen("/apex/com.android.art/lib/libart.so", RTLD_LAZY);
    if (nullptr != handler) {
        LOGD("handler: %u", (uintptr_t) handler);
    } else {
        LOGD("can't find libart.so");
    }
}
E/linker: library "/apex/com.android.art/lib/libart.so" 
("/apex/com.android.art/lib/libart.so") needed or dlopened by 
"/data/app/~~3RWpnGAvU22Gu6D55CM6wQ==/com.youngtr.jnievner-NnG3_CcHIYdUNtIcmKG5zA==/base.apk!/lib/armeabi-v7a/libjnievner.so" 
is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/~~3RWpnGAvU22Gu6D55CM6wQ==/com.youngtr.jnievner-NnG3_CcHIYdUNtIcmKG5zA==/lib/arm:/data/app/~~3RWpnGAvU22Gu6D55CM6wQ==/com.youngtr.jnievner-NnG3_CcHIYdUNtIcmKG5zA==/base.apk!/lib/armeabi-v7a", permitted_paths="/data:/mnt/expand:/data/data/com.youngtr.jnievner"]

获取方式

这里主要介绍通过 dl_iterate_phdr 获取符号地址的方式

#include <link.h>

int dl_iterate_phdr(
         int (*callback)(struct dl_phdr_info *info,
                         size_t size, void *data),
         void *data);

dl_iterate_phdr 函数允许应用程序在运行时查询它已经加载的共享对象,以及它们的加载顺序。

dl_iterate_phdr 函数遍历应用程序的共享对象列表并为每个对象回调一次 callback ,直到所有共享对象都已处理或 callback 返回非零值。

#include <link.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
   char *type;
   int p_type;

   printf("Name: \\"%s\\" (%d segments)\\n", info->dlpi_name,
              info->dlpi_phnum);

   for (int j = 0; j < info->dlpi_phnum; j++) {
       p_type = info->dlpi_phdr[j].p_type;
       type =  (p_type == PT_LOAD) ? "PT_LOAD" :
               (p_type == PT_DYNAMIC) ? "PT_DYNAMIC" :
               (p_type == PT_INTERP) ? "PT_INTERP" :
               (p_type == PT_NOTE) ? "PT_NOTE" :
               (p_type == PT_INTERP) ? "PT_INTERP" :
               (p_type == PT_PHDR) ? "PT_PHDR" :
               (p_type == PT_TLS) ? "PT_TLS" :
               (p_type == PT_GNU_EH_FRAME) ? "PT_GNU_EH_FRAME" :
               (p_type == PT_GNU_STACK) ? "PT_GNU_STACK" :
               (p_type == PT_GNU_RELRO) ? "PT_GNU_RELRO" : NULL;

       printf("    %2d: [%14p; memsz:%7jx] flags: %#jx; ", j,
               (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr),
               (uintmax_t) info->dlpi_phdr[j].p_memsz,
               (uintmax_t) info->dlpi_phdr[j].p_flags);
       if (type != NULL)
           printf("%s\\n", type);
       else
           printf("[other (%#x)]\\n", p_type);
   }

   return 0;
}

int
main(int argc, char *argv[])
{
   dl_iterate_phdr(callback, NULL);

   exit(EXIT_SUCCESS);
}

PHT

typedef struct elf64_phdr {
	Elf64_Word p_type;
	Elf64_Word p_flags;
	Elf64_Off p_offset;	/* Segment file offset */
	Elf64_Addr p_vaddr;	/* Segment virtual address */
	Elf64_Addr p_paddr;	/* Segment physical address */
	Elf64_Xword p_filesz;	/* Segment size in file */
	Elf64_Xword p_memsz;	/* Segment size in memory */
	Elf64_Xword p_align;	/* Segment alignment, file & memory */
} Elf64_Phdr;
字段 占位 示意
p_type 4 类型,关注“LOAD”类型
p_flags 4 权限属性,R W X
p_offset 8 在文件中偏移
p_vaddr 8 “Segment” 的第一个字节在进程虚拟地址空间的起始位置。
p_paddr 8 “Segment” 物理装载地址,一般情况下跟 p_vaddr 一样
p_filesz 8 ”Segment“ 在 ELF 文件中所占空间的长度
p_memsz 8 “Segment” 在进程虚拟地址空间所占的长度
p_align 8 “Segment” 的对齐属性

在操作系统里,VMA 用来映射可执行文件中的各个 “Segment”,也使用 VMA 来对进程的地址空间进行管理。

Linux 中,可以通过查看 “/proc” 来查看进程的虚拟看空间分别:

00400000-00401000 r--p 00000000 08:02 6949080                            ./sectionmapping.elf
00401000-00495000 r-xp 00001000 08:02 6949080                            ./sectionmapping.elf
00495000-004bc000 r--p 00095000 08:02 6949080                            ./sectionmapping.elf
004bd000-004c0000 r--p 000bc000 08:02 6949080                            ./sectionmapping.elf
004c0000-004c3000 rw-p 000bf000 08:02 6949080                            ./sectionmapping.elf
004c3000-004c4000 rw-p 00000000 00:00 0
00e5c000-00e7f000 rw-p 00000000 00:00 0                                  [heap]
7ffee7ab1000-7ffee7ad2000 rw-p 00000000 00:00 0                          [stack]
7ffee7b29000-7ffee7b2d000 r--p 00000000 00:00 0                          [vvar]
7ffee7b2d000-7ffee7b2f000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]