#include <unistd.h>
#include "ElfTool.h"

int ElfTool::get_module_address(const char *soname) {
    FILE *fp = fopen("/proc/self/maps", "r");
    char line[256];
    uint32_t result = 0;
    while ((fgets(line, sizeof(line), fp)) != NULL) {
        if (strstr(line, soname)) {
            LOGD("[+++] line:%s", line);
            std::string linestr(line);
            line[8] = '\0';
            break;
        }
    }
    result = strtoul(line, NULL, 16);
    fclose(fp);
    return result;
}

static void find_dynamic_table() {
    const char *soname = "/system/lib/libart.so";
    unsigned int module_base = ElfTool::get_module_address(soname);
    int fd = open(soname, O_RDONLY);
    Elf32_Ehdr *elf_header = (Elf32_Ehdr *) malloc(sizeof(Elf32_Ehdr));
    read(fd, elf_header, sizeof(Elf32_Ehdr));
    // programe header地址
    uint32_t ph_base = elf_header->e_phoff;
    uint32_t ph_num = elf_header->e_phnum;
    // 第4个program header就是dynamic
    uint32_t ph_dynamic_base = ph_base + 4 * sizeof(Elf32_Phdr);
    lseek(fd, ph_dynamic_base, SEEK_SET);
    Elf32_Phdr *dynamic_phdr = (Elf32_Phdr *) malloc(sizeof(Elf32_Phdr));
    read(fd, dynamic_phdr, sizeof(Elf32_Phdr));
    // dynamic program header 的第一个元素地址
    uint32_t offset = dynamic_phdr->p_offset + module_base;
    lseek(fd, offset, SEEK_SET);
    Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
    read(fd, dyn, sizeof(Elf32_Dyn));
}

int ElfTool::findSymbolByName(const char *soname, const char *symbol) {
    find_dynamic_table();
    int module_base = get_module_address(soname);
    int fd = open(soname, O_RDONLY);
    Elf32_Ehdr *elf_header = (Elf32_Ehdr *) malloc(sizeof(Elf32_Ehdr));
    read(fd, elf_header, sizeof(Elf32_Ehdr));
    // section header地址
    uint32_t sh_base = elf_header->e_shoff;
    // 字符串在section header中的索引
    uint32_t sh_strndx = elf_header->e_shstrndx;
    // 计算字符串地址
    uint32_t sh_str_base = sh_strndx * sizeof(Elf32_Shdr) + sh_base;

    // 指针移动到str section header位置
    lseek(fd, sh_str_base, SEEK_SET);
    // 读取字符串表,节头信息
    Elf32_Shdr *shstr_shdr = (Elf32_Shdr *) malloc(sizeof(Elf32_Shdr));
    read(fd, shstr_shdr, sizeof(Elf32_Shdr));
    // 分配内存存放截取名称字符串(symtab, strtab,shstrtab...)
    char *shstrtab = (char *) malloc(sizeof(char) * shstr_shdr->sh_size);
    // 根据节头信息移动到字符串所在位置
    lseek(fd, shstr_shdr->sh_offset, SEEK_SET);
    // 读入字符串
    read(fd, shstrtab, shstr_shdr->sh_size);

    // 动态符号节头
    Elf32_Shdr *sh_shdr = (Elf32_Shdr *) malloc(sizeof(Elf32_Shdr));
    Elf32_Shdr *dynsym_shdr = (Elf32_Shdr *) malloc(sizeof(Elf32_Shdr));
    Elf32_Shdr *dynstr_shdr = (Elf32_Shdr *) malloc(sizeof(Elf32_Shdr));
    Elf32_Shdr *strtab_shdr = (Elf32_Shdr *) malloc(sizeof(Elf32_Shdr));
    // 移动到第一个section header
    lseek(fd, sh_base, SEEK_SET);
    read(fd, sh_shdr, sizeof(Elf32_Shdr));
    char *section_name = NULL;
    for (int i = 0; i < elf_header->e_shnum; i++) {
        section_name = shstrtab + sh_shdr->sh_name;
        if (strcmp(section_name, ".dynsym") == 0) {
            memcpy(dynsym_shdr, sh_shdr, sizeof(Elf32_Shdr));
        } else if (strcmp(section_name, ".dynstr") == 0) {
            memcpy(dynstr_shdr, sh_shdr, sizeof(Elf32_Shdr));
        } else if(strcmp(section_name, ".strtab")) {
            memcpy(strtab_shdr, sh_shdr, sizeof(Elf32_Shdr));
        }
        // read之后，自动位置移动到下一个Elf32_Shdr位置
        read(fd, sh_shdr, sizeof(Elf32_Shdr));
    }
    // 动态符号表字符串
    char *dynstr = (char *) malloc(sizeof(char) * dynstr_shdr->sh_size);
    lseek(fd, dynstr_shdr->sh_offset, SEEK_SET);
    read(fd, dynstr, dynstr_shdr->sh_size);

    // 动态符号表
    Elf32_Sym *elfSym = (Elf32_Sym *) malloc(dynsym_shdr->sh_size);
    lseek(fd, dynsym_shdr->sh_offset, SEEK_SET);
    read(fd, elfSym, dynsym_shdr->sh_size);

    // 遍历动态符号表
    uint32_t sym_addr = 0;
    for (int i = 0; i < dynsym_shdr->sh_size / sizeof(Elf32_Sym); i++) {
        Elf32_Sym *sym = elfSym + i;
        const char *name = dynstr + sym->st_name;
        if(strcmp(name, symbol) == 0) {
            sym_addr =  module_base + sym->st_value;
            LOGD("FIND index is :%d", i);
            break;
        }
    }


    free(elf_header);
    free(shstr_shdr);
    free(shstrtab);
    free(dynsym_shdr);
    free(dynstr_shdr);
    free(dynstr);
    free(elfSym);
    return sym_addr;
}
