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