diff --git a/elf/rtld.c b/elf/rtld.c
index 4abc6dd4..5b2e0583 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -861,6 +861,329 @@ handle_ld_preload (const char *preloadlist, struct link_map *main_map)
   return npreloads;
}
+
+// zk---
+struct elf_info {
+        Elf64_Ehdr *hdr;
+        Elf64_Shdr *sechdrs;
+        Elf64_Shdr *strhdr;
+        Elf64_Shdr *symsec;
+        char *secstrings;
+        char *strtab;
+        void *text_vhdr;
+        void *rodata_vhdr;
+       struct link_map *lmap;
+};
+
+#define BYTES_NOP1      0x90
+
+#define CALL_INSN_SIZE          5
+#define CALL_INSN_OPCODE        0xE8
+
+#define JMP32_INSN_SIZE         5
+#define JMP32_INSN_OPCODE       0xE9
+
+#define POKE_MAX_OPCODE_SIZE    10
+
+union text_poke_insn {
+        unsigned char text[POKE_MAX_OPCODE_SIZE];
+        struct {
+                unsigned char opcode;
+                int disp;
+        } __attribute__ ((packed));
+};
+
+static int text_gen_insn(const void *loc, const void *dest)
+{
+        union text_poke_insn *insn;
+
+        // ff 15 00 00 00 00       callq  *0x00(%rip)
+        if ((*(unsigned char *)(loc - 2) == 0xff) && (*(unsigned char *)(loc - 1) == 0x15)) {
+                insn = (union text_poke_insn *)(loc - 2);
+                insn->opcode = CALL_INSN_OPCODE;
+                insn->disp = (long)dest - (long)(loc - 2 + CALL_INSN_SIZE);
+                insn->text[5] = BYTES_NOP1;
+                return 0;
+        }
+       return -1;
+}
+
+static int rewrite_section_headers(struct elf_info *info)
+{
+        unsigned int i;
+        Elf64_Ehdr *hdr = info->hdr;
+        Elf64_Shdr *sechdrs = info->sechdrs;
+
+        /* This should always be true, but let's be sure. */
+        sechdrs[0].sh_addr = 0;
+
+        for (i = 1; i < hdr->e_shnum; i++) {
+                Elf64_Shdr *shdr = &sechdrs[i];
+                shdr->sh_addr = (size_t) hdr + shdr->sh_offset;
+        }
+
+        return 0;
+}
+
+static int resolve_symbol(struct link_map *l, const char *name, Elf64_Sym *sym)
+{
+       const ElfW(Sym) *ref = sym;
+       struct link_map *sym_map = NULL;
+        char buf[256] = {0};
+        for (int i = 0; ; i++) {
+                if (name[i] == '\0' || name[i] == '@')
+                        break;
+                buf[i] = name[i];
+        }
+        sym_map = _dl_lookup_symbol_x(buf, l, &ref, l->l_scope, 0, ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA, DL_LOOKUP_ADD_DEPENDENCY, NULL);
+       // TODO: use vhdr
+       if (ref != NULL) {
+               sym->st_value = (Elf64_Addr)sym_map->l_addr + ref->st_value;
+       }
+        _dl_debug_printf("symbol: 0x%lx %s\n", (long)sym->st_value, name);
+        return 0;
+}
+
+static int simplify_symbols(struct elf_info *info)
+{
+        char *strtab = info->strtab;
+        Elf64_Shdr *symsec = info->symsec;
+        Elf64_Sym *syms = (void *)symsec->sh_addr;
+        unsigned long secbase = 0;
+        unsigned int i;
+        int ret = 0;
+
+        // .symtab
+        for (i = 1; i < symsec->sh_size / sizeof(Elf64_Sym); i++) {
+                Elf64_Sym *sym = syms + i;
+                const char *name = strtab + sym->st_name;
+
+                switch (sym->st_shndx) {
+                case SHN_COMMON:
+                case SHN_ABS:
+                        /* Don't need to do anything */
+                        break;
+
+                case SHN_UNDEF:
+                        ret = resolve_symbol(info->lmap, name, sym);
+                        break;
+
+                default:
+                        if ((ELF64_ST_TYPE(sym->st_info) == STT_SECTION) || (ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE)) {
+                                break;
+                        }
+                        // local ELF FUNC
+                        if (ELF64_ST_TYPE(sym->st_info) == STT_FUNC) {
+                                secbase = (unsigned long)info->text_vhdr;
+                        }
+                        // TODO: rodata
+                        // local ELF OBJECT
+                        if (ELF64_ST_TYPE(sym->st_info) == STT_OBJECT) {
+                                secbase = (unsigned long)info->rodata_vhdr;
+                        }
+                        sym->st_value += secbase;
+                        _dl_debug_printf("symbol: 0x%lx %s 0x%x local\n", (long)sym->st_value, name, sym->st_info);
+
+                        break;
+                }
+        }
+
+        return ret;
+}
+
+static void relocate_rewrite_value(struct elf_info *info, Elf64_Rela *rel, void *loc)
+{
+        unsigned long val;
+
+        // GOT data offset to elf hdr
+        val = *(int *)loc - rel->r_addend + rel->r_offset;
+        val = (unsigned long)info->rodata_vhdr + val;
+        val = val - (unsigned long)loc + rel->r_addend;
+        memcpy(loc, &val, 4);
+}
+
+#if __has_attribute(__fallthrough__)
+# define fallthrough                    __attribute__((__fallthrough__))
+#else
+# define fallthrough                    do {} while (0)  /* fallthrough */
+#endif
+
+static int apply_relocate_add(Elf64_Shdr *shdr, struct elf_info *info)
+{
+        unsigned int i;
+        Elf64_Rela *rel_tab = (void *)shdr->sh_addr;
+        Elf64_Sym *sym;
+        void *loc;
+        int ret;
+
+        for (i = 0; i < shdr->sh_size / sizeof(Elf64_Rela); i++) {
+                Elf64_Rela *rel = rel_tab + i;
+                /* This is where to make the change */
+                loc = info->text_vhdr + rel->r_offset;
+                sym = (Elf64_Sym *)info->symsec->sh_addr + ELF64_R_SYM(rel->r_info);
+
+                _dl_debug_printf("type %x st_value %lx r_addend %lx loc %lx\n", (int)ELF64_R_TYPE(rel->r_info), sym->st_value,
+                       rel->r_addend, (unsigned long)loc);
+
+                switch (ELF64_R_TYPE(rel->r_info)) {
+                case R_X86_64_NONE:
+                case R_X86_64_PLT32:
+                        break;
+                case R_X86_64_GOTPCRELX:
+                        // ff 15 00 00 00 00       callq  *0x00(%rip)
+                        ret = text_gen_insn(loc, (const void *)sym->st_value);
+                        if (ret == 0)
+                                break;
+                        // 48 83 3d d2 fe 5f 00    cmpq   $0x0,0x5ffed2(%rip)
+                          relocate_rewrite_value(info, rel, loc);
+                        break;
+                case R_X86_64_PC32:
+                        // SHN_COMMON STT_FUNC no need reloc
+                        if (ELF64_ST_TYPE(sym->st_info) == STT_FUNC)
+                                break;
+                        // STT_OBJECT
+                        // TODO: direct mov, do not use lea
+                        fallthrough;
+                case R_X86_64_GOTPCREL:
+                case R_X86_64_REX_GOTPCRELX:
+                        // sym may not exist, change data offset
+                          relocate_rewrite_value(info, rel, loc);
+                        break;
+                default:
+                        _dl_debug_printf("invalid relocation target, type %x, loc %lx\n",
+                               (int)ELF64_R_TYPE(rel->r_info), (unsigned long)loc);
+                        return -1;
+                }
+        }
+        return 0;
+}
+
+static int apply_relocations(struct elf_info *info)
+{
+        Elf64_Shdr *sechdrs = info->sechdrs;
+        char *secstrings = info->secstrings;
+        unsigned int shnum = info->hdr->e_shnum;
+        unsigned int i;
+        int err = 0;
+
+        for (i = 1; i < shnum; i++) {
+                Elf64_Shdr *shdr = &sechdrs[i];
+
+                /* Not a valid relocation section? */
+                if (shdr->sh_info >= shnum)
+                        continue;
+
+                if (shdr->sh_type == SHT_RELA) {
+                        const char *name = secstrings + shdr->sh_name;
+                        if ((strcmp(name, ".rela.text") != 0) && (strcmp(name, ".rela.init") != 0))
+                                continue;
+                        _dl_debug_printf("relocation %s\n", name);
+                        err = apply_relocate_add(shdr, info);
+                }
+                if (err < 0)
+                        break;
+        }
+        return err;
+}
+
+static int read_elf_info(struct link_map *lmap, struct elf_info *info)
+{
+       int fd = -1;
+        int ret;
+        unsigned int i;
+        unsigned int index_str;
+        Elf64_Ehdr *hdr = NULL;
+        Elf64_Shdr *sechdrs = NULL;
+        Elf64_Shdr *strhdr;
+        void *buf;
+
+       _dl_debug_printf("\nzk--- relocation: %s, elf_info 0x%lx, link_map 0x%lx\n", DSO_FILENAME(lmap->l_name), (unsigned long)info, (unsigned long)lmap);
+       info->lmap = lmap;
+       fd = __open(DSO_FILENAME(lmap->l_name), O_RDONLY);
+       if (fd == -1) {
+                _dl_debug_printf("\nopen %s fail\n", DSO_FILENAME(lmap->l_name));
+                return -1;
+       }
+
+        ret = __lseek(fd, 0, SEEK_END);
+        buf = __mmap(0, ret, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0);
+        _dl_debug_printf("\nELF len %u, addr 0x%lx\n", ret, (unsigned long)buf);
+
+        hdr = (Elf64_Ehdr *) buf;
+        sechdrs = (void *)hdr + hdr->e_shoff;
+
+        // session header name string table
+        strhdr = &sechdrs[hdr->e_shstrndx];
+        info->secstrings = (void *)hdr + strhdr->sh_offset;
+
+        // .symtab
+        for (i = 1; i < hdr->e_shnum; i++) {
+                if (sechdrs[i].sh_type == SHT_SYMTAB) {
+                        info->symsec = &sechdrs[i];
+                        index_str = sechdrs[i].sh_link;
+                        info->strtab = (char *)hdr + sechdrs[index_str].sh_offset;
+                        break;
+                }
+        }
+
+        info->hdr = hdr;
+        info->sechdrs = sechdrs;
+        info->strhdr = strhdr;
+
+       close(fd);
+        return 0;
+}
+
+// zk---
+void dl_map_new_addr(struct link_map *main_map, ElfW(Addr) *user_entry)
+{
+       ElfW(Addr) text_layout, rodata_layout;
+       struct elf_info info_buf = {0};
+       struct elf_info *info = &info_buf;
+        int i;
+        Elf64_Phdr *elf_ppnt, *elf_phdata;
+       void *load_addr;
+
+       text_layout = (ElfW(Addr)) __mmap(0, 0x40000000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|(21 << MAP_HUGE_SHIFT), -1, 0);
+       rodata_layout = (ElfW(Addr)) __mmap(0, 0x40000000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|(21 << MAP_HUGE_SHIFT), -1, 0);
+
+       // cp text from ELF
+       read_elf_info(main_map, info);
+        load_addr = (void*)info->hdr;
+        elf_phdata = (Elf64_Phdr *)(load_addr + info->hdr->e_phoff);
+        for (i = 0, elf_ppnt = elf_phdata; i < info->hdr->e_phnum; i++, elf_ppnt++) {
+                if (elf_ppnt->p_type != PT_LOAD)
+                        continue;
+
+                // skip first LOAD segment
+                elf_ppnt++;
+                // text
+                info->text_vhdr = (void*)text_layout - elf_ppnt->p_offset;
+               _dl_debug_printf("\n old user_entry 0x%lx\n", (unsigned long)*user_entry);
+                *user_entry = (ElfW(Addr))info->text_vhdr + info->hdr->e_entry;
+               _dl_debug_printf("\n user_entry 0x%lx\n", (unsigned long)*user_entry);
+               _dl_debug_printf("\n text 0x%lx\n", (unsigned long)text_layout);
+                memcpy((void*)text_layout, load_addr + elf_ppnt->p_offset, elf_ppnt->p_filesz);
+                //main_map->l_text_end = text_layout + elf_ppnt->p_memsz;
+                // rodata
+                elf_ppnt++;
+                info->rodata_vhdr = (void*)rodata_layout - elf_ppnt->p_offset;
+               _dl_debug_printf("\n rodata dst 0x%lx src 0x%lx len 0x%lx\n", (unsigned long)rodata_layout, (unsigned long)main_map->l_addr + elf_ppnt->p_paddr, (unsigned long)elf_ppnt->p_memsz);
+                memcpy((void*)rodata_layout, (void*)main_map->l_addr + elf_ppnt->p_paddr, elf_ppnt->p_memsz);
+                // data
+                elf_ppnt++;
+                void *data_begin = (void*)rodata_layout + (elf_ppnt->p_paddr - (elf_ppnt - 1)->p_paddr);
+               _dl_debug_printf("\n data dst 0x%lx src 0x%lx len 0x%lx\n", (unsigned long)data_begin, (unsigned long)main_map->l_addr + elf_ppnt->p_paddr, (unsigned long)elf_ppnt->p_memsz);
+                memcpy(data_begin, (void*)main_map->l_addr + elf_ppnt->p_paddr, elf_ppnt->p_memsz);
+
+                break;
+        }
+
+        rewrite_section_headers(info);
+        simplify_symbols(info);
+        apply_relocations(info);
+}
+
static void
dl_main (const ElfW(Phdr) *phdr,
        ElfW(Word) phnum,
@@ -2296,6 +2619,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
   _dl_unload_cache ();
#endif
+// zk---
+dl_map_new_addr(main_map, user_entry);
+
   /* Once we return, _dl_sysdep_start will invoke
      the DT_INIT functions and then *USER_ENTRY.  */
}