diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index db6b3ecc..08769c9b 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -590,7 +590,8 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) // Update {DYNAMIC}.sh_offset by so_slide. Elf64_Shdr *shdr = shdri; for (unsigned j = 0; j < e_shnum; ++shdr, ++j) { - if (Elf64_Shdr::SHT_DYNAMIC == get_te32(&shdr->sh_type)) { + unsigned sh_type = get_te32(&shdr->sh_type); + if (Elf64_Shdr::SHT_DYNAMIC == sh_type) { upx_uint64_t offset = get_te64(&shdr->sh_offset); set_te64(&shdr->sh_offset, so_slide - asl_delta + offset ); fo->seek(j * sizeof(Elf64_Shdr) - asl_delta + xct_off, SEEK_SET); @@ -598,6 +599,13 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) fo->seek(0, SEEK_END); break; } + if (Elf64_Shdr::SHT_RELA == sh_type + && n_jmp_slot + && !strcmp(".rela.plt", get_te32(&shdr->sh_name) + shstrtab)) { + upx_uint64_t f_off = elf_get_offset_from_address(plt_off); + fo->seek(f_off, SEEK_SET); + fo->rewrite(&file_image[f_off - so_slide], n_jmp_slot * sizeof(void *)); + } } } else { // !opt->o_unix.android_shlib) @@ -2032,11 +2040,11 @@ PackLinuxElf64::canPack() } } else { // no Sections; use heuristics - uint64_t const strsz = elf_unsigned_dynamic(Elf64_Dyn::DT_STRSZ); - uint64_t const strtab = elf_unsigned_dynamic(Elf64_Dyn::DT_STRTAB); - uint64_t const relsz = elf_unsigned_dynamic(Elf64_Dyn::DT_RELSZ); - uint64_t const rel = elf_unsigned_dynamic(Elf64_Dyn::DT_REL); - uint64_t const init = elf_unsigned_dynamic(Elf64_Dyn::DT_INIT); + upx_uint64_t const strsz = elf_unsigned_dynamic(Elf64_Dyn::DT_STRSZ); + upx_uint64_t const strtab = elf_unsigned_dynamic(Elf64_Dyn::DT_STRTAB); + upx_uint64_t const relsz = elf_unsigned_dynamic(Elf64_Dyn::DT_RELSZ); + upx_uint64_t const rel = elf_unsigned_dynamic(Elf64_Dyn::DT_REL); + upx_uint64_t const init = elf_unsigned_dynamic(Elf64_Dyn::DT_INIT); if ((init == (relsz + rel ) && rel == (strsz + strtab)) || (init == (strsz + strtab) && strtab == (relsz + rel )) ) { @@ -2865,23 +2873,23 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) // Relocate PT_DYNAMIC (in 2nd PT_LOAD) Elf64_Dyn *dyn = const_cast(dynseg); for (; dyn->d_tag; ++dyn) { - uint64_t d_tag = get_te64(&dyn->d_tag); + upx_uint64_t d_tag = get_te64(&dyn->d_tag); if (Elf64_Dyn::DT_FINI == d_tag || Elf64_Dyn::DT_FINI_ARRAY == d_tag || Elf64_Dyn::DT_INIT_ARRAY == d_tag || Elf64_Dyn::DT_PREINIT_ARRAY == d_tag || Elf64_Dyn::DT_PLTGOT == d_tag) { - uint64_t d_val = get_te64(&dyn->d_val); + upx_uint64_t d_val = get_te64(&dyn->d_val); set_te64(&dyn->d_val, asl_delta + d_val); } } // Relocate dynsym (DT_SYMTAB) which is below xct_va Elf64_Sym *sym = const_cast(dynsym); - uint64_t off_dynsym = get_te64(&sec_dynsym->sh_offset); - uint64_t sz_dynsym = get_te64(&sec_dynsym->sh_size); + upx_uint64_t off_dynsym = get_te64(&sec_dynsym->sh_offset); + upx_uint64_t sz_dynsym = get_te64(&sec_dynsym->sh_size); for (int j = sz_dynsym / sizeof(Elf64_Sym); --j>=0; ++sym) { - uint64_t symval = get_te64(&sym->st_value); + upx_uint64_t symval = get_te64(&sym->st_value); unsigned symsec = get_te16(&sym->st_shndx); if (Elf64_Sym::SHN_UNDEF != symsec && Elf64_Sym::SHN_ABS != symsec @@ -2897,10 +2905,10 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) // Relocate Phdr virtual addresses, but not physical offsets and sizes /* Elf64_Phdr * */ phdr = phdri; for (int j = e_phnum; --j>=0; ++phdr) { - uint64_t offset = get_te64(&phdr->p_offset); + upx_uint64_t offset = get_te64(&phdr->p_offset); if (xct_off <= offset) { // above the extra page //set_te64(&phdr->p_offset, asl_delta + offset); // physical - uint64_t addr = get_te64(&phdr->p_paddr); + upx_uint64_t addr = get_te64(&phdr->p_paddr); set_te64(&phdr->p_paddr, asl_delta + addr); addr = get_te64(&phdr->p_vaddr); set_te64(&phdr->p_vaddr, asl_delta + addr); @@ -2911,29 +2919,53 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) // Relocate Shdr; and Rela, Rel (below xct_off) Elf64_Shdr *shdr = shdri; - uint64_t sz_shstrtab = get_te64(&sec_strndx->sh_size); + upx_uint64_t sz_shstrtab = get_te64(&sec_strndx->sh_size); for (int j = e_shnum; --j>=0; ++shdr) { unsigned sh_type = get_te32(&shdr->sh_type); - uint64_t sh_size = get_te64(&shdr->sh_size); - uint64_t sh_offset = get_te64(&shdr->sh_offset); - uint64_t sh_entsize = get_te64(&shdr->sh_entsize); + upx_uint64_t sh_size = get_te64(&shdr->sh_size); + upx_uint64_t sh_offset = get_te64(&shdr->sh_offset); + upx_uint64_t sh_entsize = get_te64(&shdr->sh_entsize); if (xct_off <= sh_offset) { set_te64(&shdr->sh_offset, asl_delta + sh_offset); - uint64_t addr = get_te64(&shdr->sh_addr); + upx_uint64_t addr = get_te64(&shdr->sh_addr); set_te64(&shdr->sh_addr, asl_delta + addr); } - if (Elf64_Shdr::SHT_RELA== sh_type) { + if (Elf64_Shdr::SHT_RELA == sh_type) { if (sizeof(Elf64_Rela) != sh_entsize) { char msg[50]; - snprintf(msg, sizeof(msg), "bad Rela.sh_entsize %lu", sh_entsize); + snprintf(msg, sizeof(msg), "bad Rela.sh_entsize %lu", (long)sh_entsize); throwCantPack(msg); } + n_jmp_slot = 0; + plt_off = ~0ull; Elf64_Rela *rela = (Elf64_Rela *)(sh_offset + file_image); for (int k = sh_size / sh_entsize; --k >= 0; ++rela) { - uint64_t r_offset = get_te64(&rela->r_offset); + upx_uint64_t r_addend = get_te64(&rela->r_addend); + upx_uint64_t r_offset = get_te64(&rela->r_offset); + upx_uint64_t r_info = get_te64(&rela->r_info); + unsigned r_type = ELF64_R_TYPE(r_info); if (xct_off <= r_offset) { set_te64(&rela->r_offset, asl_delta + r_offset); } + if (Elf64_Ehdr::EM_AARCH64 == e_machine) { + if (R_AARCH64_RELATIVE == r_type) { + if (xct_off <= r_addend) { + set_te64(&rela->r_addend, asl_delta + r_addend); + } + } + if (R_AARCH64_JUMP_SLOT == r_type) { + // .rela.plt contains offset of the "first time" target + if (plt_off > (asl_delta + r_offset)) { + plt_off = (asl_delta + r_offset); + } + upx_uint64_t d = elf_get_offset_from_address(asl_delta + r_offset); + upx_uint64_t w = get_te64(&file_image[d]); + if (xct_off <= w) { + set_te64(&file_image[d], asl_delta + w); + } + ++n_jmp_slot; + } + } } fo->seek(sh_offset, SEEK_SET); fo->rewrite(sh_offset + file_image, sh_size); @@ -2941,12 +2973,12 @@ void PackLinuxElf64::pack1(OutputFile *fo, Filter & /*ft*/) if (Elf64_Shdr::SHT_REL == sh_type) { if (sizeof(Elf64_Rel) != sh_entsize) { char msg[50]; - snprintf(msg, sizeof(msg), "bad Rel.sh_entsize %lu", sh_entsize); + snprintf(msg, sizeof(msg), "bad Rel.sh_entsize %lu", (long)sh_entsize); throwCantPack(msg); } Elf64_Rel *rel = (Elf64_Rel *)(sh_offset + file_image); for (int k = sh_size / sh_entsize; --k >= 0; ++rel) { - uint64_t r_offset = get_te64(&rel->r_offset); + upx_uint64_t r_offset = get_te64(&rel->r_offset); if (xct_off <= r_offset) { set_te64(&rel->r_offset, asl_delta + r_offset); } diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index 18da72c2..9883bbec 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -291,6 +291,8 @@ protected: upx_uint64_t so_slide; unsigned char *note_body; // concatenated contents of PT_NOTEs, if any unsigned note_size; // total size of PT_NOTEs + unsigned n_jmp_slot; + upx_uint64_t plt_off; upx_uint64_t page_mask; // AND clears the offset-within-page Elf64_Dyn const *dynseg; // from PT_DYNAMIC diff --git a/src/stub/src/arm64-linux.shlib-init.S b/src/stub/src/arm64-linux.shlib-init.S index eee983d7..c7072cc5 100644 --- a/src/stub/src/arm64-linux.shlib-init.S +++ b/src/stub/src/arm64-linux.shlib-init.S @@ -126,7 +126,7 @@ sp_frame = 24 * NBPW // .long offset(escape_hatch) // .long offset(xct_off) dst of f_exp _start: .globl _start -// brk #0 // debugging +// brk #0 // debugging slot f_argc // 0 slot f_argv // 1