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. */
}