diff --git a/src/p_elf_enum.h b/src/p_elf_enum.h index 6769f8be..d9fef5a1 100644 --- a/src/p_elf_enum.h +++ b/src/p_elf_enum.h @@ -74,7 +74,9 @@ EM_PPC64 = 21, EM_ARM = 40, EM_X86_64 = 62, + EM_AMD64 = EM_X86_64, EM_AARCH64 = 183, + EM_ARM64 = EM_AARCH64, }; enum { // e_version @@ -260,6 +262,7 @@ R_ARM_GLOB_DAT = 21, R_386_GLOB_DAT = 6, + R_X86_64_64 = 1, R_AARCH64_ABS64 = 257, R_AARCH64_GLOB_DAT = 1025, diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp index 8caf278e..1a2a4252 100644 --- a/src/p_lx_elf.cpp +++ b/src/p_lx_elf.cpp @@ -442,8 +442,8 @@ PackLinuxElf32::elf_find_Phdr_for_va(upx_uint32_t addr, Elf32_Phdr const *phdr, return nullptr; } -Elf64_Phdr * -PackLinuxElf64::elf_find_Phdr_for_va(upx_uint64_t addr, Elf64_Phdr *phdr, unsigned phnum) +Elf64_Phdr const * +PackLinuxElf64::elf_find_Phdr_for_va(upx_uint64_t addr, Elf64_Phdr const *phdr, unsigned phnum) { for (unsigned j = 0; j < phnum; ++phdr) { if ((addr - get_te64(&phdr->p_vaddr)) < get_te64(&phdr->p_filesz)) { @@ -529,8 +529,10 @@ off_t PackLinuxElf32::pack3(OutputFile *fo, Filter &ft) unsigned const cpr_entry = (Elf32_Ehdr::EM_ARM==e_machine) + load_va + sz_pack2; // Thumb mode set_te32(&file_image[user_init_off], cpr_entry); // set the hook - Elf32_Dyn *dynp = (Elf32_Dyn *)elf_find_dynptr(Elf32_Dyn::DT_NULL); // for decompressor - set_te32(&dynp->d_val, (char *)user_init_rp - (char *)&file_image[0]); + if (user_init_rp) { // decompressor needs hint for DT_INIT_ARRAY + Elf32_Dyn *dynp = (Elf32_Dyn *)elf_find_dynptr(Elf32_Dyn::DT_NULL); + set_te32(&dynp->d_val, (char *)user_init_rp - (char *)&file_image[0]); + } Elf32_Phdr *const phdr0 = (Elf32_Phdr *)lowmem.subref( "bad e_phoff", e_phoff, e_phnum * sizeof(Elf32_Phdr)); @@ -684,12 +686,12 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) total_out = fpad4(fo, total_out); if (0==xct_off) { // not shared library - set_te64(&elfout.phdr[C_BASE].p_align, ((upx_uint64_t)0) - page_mask); + set_te64(&elfout.phdr[C_BASE].p_align, ((u64_t)0) - page_mask); elfout.phdr[C_BASE].p_paddr = elfout.phdr[C_BASE].p_vaddr; elfout.phdr[C_BASE].p_offset = 0; - upx_uint64_t abrk = getbrk(phdri, e_phnum); + u64_t abrk = getbrk(phdri, e_phnum); // vbase handles ET_EXEC. FIXME: pre-linking? - upx_uint64_t const vbase = get_te64(&elfout.phdr[C_BASE].p_vaddr); + u64_t const vbase = get_te64(&elfout.phdr[C_BASE].p_vaddr); set_te64(&elfout.phdr[C_BASE].p_filesz, 0x1000); // Linux kernel SIGSEGV if (0==.p_filesz) set_te64(&elfout.phdr[C_BASE].p_memsz, abrk - vbase); set_te64(&elfout.phdr[C_BASE].p_flags, Elf64_Phdr::PF_W|Elf64_Phdr::PF_R); @@ -699,9 +701,14 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) elfout.phdr[C_TEXT].p_paddr = elfout.phdr[C_TEXT].p_vaddr; set_te64(&elfout.ehdr.e_entry, abrk + get_te64(&elfout.ehdr.e_entry) - vbase); } - if (0!=xct_off) { // shared library - upx_uint64_t word = load_va + sz_pack2; - set_te64(&file_image[user_init_off], word); // set the hook + if (0!=xct_off) { // shared library + u64_t const cpr_entry = (Elf64_Ehdr::EM_ARM==e_machine) + load_va + sz_pack2; // Thumb mode + set_te64(&file_image[user_init_off], cpr_entry); // set the hook + + if (user_init_rp) { // decompressor needs hint for DT_INIT_ARRAY + Elf64_Dyn *dynp = (Elf64_Dyn *)elf_find_dynptr(Elf64_Dyn::DT_NULL); + set_te64(&dynp->d_val, (char *)user_init_rp - (char *)&file_image[0]); + } Elf64_Phdr *const phdr0 = (Elf64_Phdr *)lowmem.subref( "bad e_phoff", e_phoff, e_phnum * sizeof(Elf64_Phdr)); @@ -710,9 +717,9 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) so_slide = 0; for (unsigned j = 0; j < e_phnum; ++j, ++phdr) { // p_vaddr and p_paddr do not change! - upx_uint64_t const len = get_te64(&phdr->p_filesz); - upx_uint64_t const ioff = get_te64(&phdri[j].p_offset); // without asl_delta - upx_uint64_t align= get_te64(&phdr->p_align); + u64_t const len = get_te64(&phdr->p_filesz); + u64_t const ioff = get_te64(&phdri[j].p_offset); // without asl_delta + u64_t align= get_te64(&phdr->p_align); unsigned const type = get_te32(&phdr->p_type); if (Elf64_Phdr::PT_INTERP==type) { // Rotate to highest position, so it can be lopped @@ -735,12 +742,12 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) set_te64(&phdr->p_memsz, total_out - ioff); if (user_init_off < xct_off) { // MIPS puts PT_DYNAMIC here // Allow for DT_INIT in a new [stolen] slot - unsigned off2 = user_init_off - sizeof(word); + unsigned off2 = user_init_off - sizeof(u64_t); fo->seek(off2, SEEK_SET); - fo->rewrite(&file_image[off2], 2*sizeof(word)); + fo->rewrite(&file_image[off2], 2*sizeof(u64_t)); } } - else if (xct_off < ioff) { // Slide subsequent PT_LOAD. + else if (xct_off < ioff) { // Slide subsequent PT_LOAD. if ((1u<<12) < align && Elf64_Ehdr::EM_X86_64 == e_machine // FIXME: other $ARCH ? ) { @@ -749,7 +756,7 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) } off = fpadN(fo, (-1 + align) & (ioff - off)); if (!so_slide) { - so_slide = off - (asl_delta + ioff); + so_slide = off - ((is_asl ? asl_delta : 0) + ioff); //asl_slide_Shdrs(); } set_te64(&phdr->p_offset, off); @@ -757,29 +764,28 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) fo->write(&file_image[ioff], len); off += len; total_out = off; + + if ((user_init_off - ioff) < len) { + fo->seek(user_init_off + so_slide, SEEK_SET); + u64_t word = cpr_entry; + set_te64(&word, cpr_entry); + fo->rewrite(&word, sizeof(word)); + fo->seek(0, SEEK_END); + } } continue; // all done with this PT_LOAD } if (xct_off < ioff) { - set_te64(&phdr->p_offset, so_slide + asl_delta + ioff); + set_te64(&phdr->p_offset, so_slide + (is_asl ? asl_delta : 0) + ioff); } } // end each Phdr - if (opt->o_unix.android_shlib) { + if (sec_arm_attr || is_asl) { // must update Shdr.sh_offset for so_slide // Update {DYNAMIC}.sh_offset by so_slide. - Elf64_Shdr *shdr = (Elf64_Shdr *)lowmem.subref( - "bad e_shoff", xct_off - asl_delta, e_shnum * sizeof(Elf64_Shdr)); + Elf64_Shdr *shdr = (Elf64_Shdr *)lowmem.subref( // FIXME: use shdri ? + "bad e_shoff", xct_off - (is_asl ? asl_delta : 0), e_shnum * sizeof(Elf64_Shdr)); for (unsigned j = 0; j < e_shnum; ++shdr, ++j) { unsigned sh_type = get_te32(&shdr->sh_type); -#if 0 //{ ? old 2018-01-15 - if (Elf64_Shdr::SHT_DYNAMIC == sh_type) { - upx_uint64_t offset = get_te64(&shdr->sh_offset); - set_te64(&shdr->sh_offset, so_slide + offset); - fo->seek((j * sizeof(Elf64_Shdr)) + xct_off - asl_delta, SEEK_SET); - fo->rewrite(shdr, sizeof(*shdr)); - fo->seek(0, SEEK_END); - } -#endif //} unsigned sh_flags = get_te32(&shdr->sh_flags); unsigned sh_offset = get_te64(&shdr->sh_offset); // already asl_delta if (Elf64_Shdr::SHF_ALLOC & sh_flags @@ -790,32 +796,45 @@ off_t PackLinuxElf64::pack3(OutputFile *fo, Filter &ft) if (Elf64_Shdr::SHT_RELA == sh_type && n_jmp_slot // FIXME: does this apply to SHT_RELA ? && !strcmp(".rel.plt", get_te32(&shdr->sh_name) + shstrtab)) { - upx_uint64_t va = elf_unsigned_dynamic(Elf64_Dyn::DT_PLTGOT) - asl_delta; + u64_t va = elf_unsigned_dynamic(Elf64_Dyn::DT_PLTGOT) - (is_asl ? asl_delta : 0); // Now use the old Phdrs (phdri) Elf64_Phdr const *phva; phva = elf_find_Phdr_for_va(va, phdri, e_phnum); - upx_uint64_t old_off = (va - get_te64(&phva->p_vaddr)) + u64_t old_off = (va - get_te64(&phva->p_vaddr)) + get_te64(&phva->p_offset); // Now use the new Phdrs (phdr0) - va += asl_delta; + va += (is_asl ? asl_delta : 0); phva = elf_find_Phdr_for_va(va, phdr0, e_phnum); - upx_uint64_t new_off = (va - get_te64(&phva->p_vaddr)) + u64_t new_off = (va - get_te64(&phva->p_vaddr)) + get_te64(&phva->p_offset); - if (fo) { + if (fo && n_jmp_slot) { fo->seek(new_off, SEEK_SET); fo->rewrite(&file_image[old_off], n_jmp_slot * 8); } - } + } + if (j && shdr->sh_addr == 0 + && get_te32(&shdr->sh_offset) < xct_off) { + // Try to be nice by sliding; but still fails if compressed. + // So don't do it unless appending plain text of shstrtab. + unsigned sh_off = get_te32(&shdr->sh_offset); + if (xct_off < sh_off) { + set_te32(&shdr->sh_offset, sh_off + so_slide); + } + } } - if (fo) { - fo->seek((unsigned char *)shdro - (unsigned char *)(void *)lowmem, SEEK_SET); - fo->rewrite(shdro, e_shnum * sizeof(*shdr)); - fo->seek(0, SEEK_END); + // Maybe: append plain text of shstrtab strings? + fo->seek(total_out, SEEK_SET); + if (xct_off < e_shoff) { + set_te32(&((Elf32_Ehdr *)lowmem.getVoidPtr())->e_shoff, total_out); + if (fo) { + fo->write(shdri, e_shnum * sizeof(*shdr)); + total_out += e_shnum * sizeof(*shdr); + } } } - else { // !opt->o_unix.android_shlib) + else { // output has no Shdr ehdri.e_shnum = 0; ehdri.e_shoff = 0; ehdri.e_shstrndx = 0; @@ -909,6 +928,7 @@ PackLinuxElf64::PackLinuxElf64(InputFile *f) gashtab(nullptr), gashend(nullptr), dynsym(nullptr), jni_onload_sym(nullptr), sec_strndx(nullptr), sec_dynsym(nullptr), sec_dynstr(nullptr) + , sec_arm_attr(nullptr) { memset(&ehdri, 0, sizeof(ehdri)); n_jmp_slot = 0; @@ -2775,6 +2795,13 @@ bad: // Take one as a last resort. if ((Elf32_Dyn::DT_INIT==upx_dt_init || !upx_dt_init) && Elf32_Shdr::SHT_DYNAMIC == sh_type) { + unsigned sh_offset = get_te64(&shdr->sh_offset); + unsigned sh_size = get_te64(&shdr->sh_size); + if ((unsigned)file_size < sh_size + || (unsigned)file_size < sh_offset + || ((unsigned)file_size - sh_offset) < sh_size) { + throwCantPack("bad SHT_DYNAMIC"); + } unsigned const n = get_te32(&shdr->sh_size) / sizeof(Elf32_Dyn); Elf32_Dyn *dynp = (Elf32_Dyn *)&file_image[get_te32(&shdr->sh_offset)]; for (; Elf32_Dyn::DT_NULL != dynp->d_tag; ++dynp) { @@ -3024,9 +3051,9 @@ PackLinuxElf64::canPack() invert_pt_dynamic(dynseg, umin(get_te64(&phdr->p_filesz), file_size - offset)); } - else if (PT_LOAD64==get_te32(&phdr->p_type)) { + else if (is_LOAD64(phdr)) { if (!pload_x0 - && Elf32_Phdr::PF_X & get_te32(&phdr->p_flags) + && Elf64_Phdr::PF_X & get_te32(&phdr->p_flags) ) { pload_x0 = phdr; } @@ -3088,10 +3115,11 @@ PackLinuxElf64::canPack() xct_va = umin(xct_va, get_te64(&shdr->sh_addr)); } // Hook the first slot of DT_PREINIT_ARRAY or DT_INIT_ARRAY. - if (( Elf64_Dyn::DT_PREINIT_ARRAY==upx_dt_init - && Elf64_Shdr::SHT_PREINIT_ARRAY==sh_type) - || ( Elf64_Dyn::DT_INIT_ARRAY ==upx_dt_init - && Elf64_Shdr::SHT_INIT_ARRAY ==sh_type) ) { + if (!user_init_rp && ( + ( Elf64_Dyn::DT_PREINIT_ARRAY==upx_dt_init + && Elf64_Shdr::SHT_PREINIT_ARRAY==sh_type) + || ( Elf64_Dyn::DT_INIT_ARRAY ==upx_dt_init + && Elf64_Shdr::SHT_INIT_ARRAY ==sh_type) )) { unsigned user_init_ava = get_te32(&shdr->sh_addr); user_init_off = get_te64(&shdr->sh_offset); if ((u64_t)file_size <= user_init_off) { @@ -3101,8 +3129,8 @@ PackLinuxElf64::canPack() throwCantPack(msg); } // Check that &file_image[user_init_off] has - // *_RELATIVE relocation, and fetch user_init_va. - // If Elf64_Rela then the actual value is in Rela.r_addend. + // *_RELATIVE or *_ABS* relocation, and fetch user_init_va. + // If Elf_Rela then the actual value is in Rela.r_addend. int z_rel = dt_table[Elf64_Dyn::DT_RELA]; int z_rsz = dt_table[Elf64_Dyn::DT_RELASZ]; if (z_rel && z_rsz) { @@ -3125,6 +3153,7 @@ PackLinuxElf64::canPack() for (; rp < last; ++rp) { upx_uint64_t r_va = get_te64(&rp->r_offset); if (r_va == user_init_ava) { // found the Elf64_Rela + user_init_rp = rp; upx_uint64_t r_info = get_te64(&rp->r_info); unsigned r_type = ELF64_R_TYPE(r_info); if (Elf64_Ehdr::EM_AARCH64 == e_machine @@ -4997,6 +5026,9 @@ int PackLinuxElf32::pack2(OutputFile *fo, Filter &ft) x.offset = p_offset + hdr_u_len; x.size = p_filesz - hdr_u_len; + + Elf32_Phdr *phdr = k + (Elf32_Phdr *)(1+ (Elf32_Ehdr *)&lowmem[0]); + set_te32(&phdr->p_flags, Elf32_Phdr::PF_X | get_te32(&phdr->p_flags)); hdr_u_len = 0; // no longer the first time } // The remainder above xct_off in first compressible PT_LOAD @@ -5204,14 +5236,26 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) } else { // Just copy up to xct_off x.size = umin(x.size, xct_off - x.offset); - fi->seek(x.offset, SEEK_SET); - fi->readx(ibuf, x.size); - total_in += x.size; + if (0) { // DEBUG paranoia + fi->seek(x.offset, SEEK_SET); + fi->readx(ibuf, x.size); + total_in += x.size; + unsigned const *fip = (unsigned const *)file_image.getVoidPtr(); + unsigned const *ibp = (unsigned const *)ibuf.getVoidPtr(); + for (unsigned j = 0; j < x.size>>2; ++j) { + if (fip[j] != ibp[j]) { + printf("[%#x]: file_image %#x ibuf %#x\n", j, fip[j], ibp[j]); + } + } + } // FIXME: adler2 ? fo->seek(x.offset, SEEK_SET); - fo->write(ibuf, x.size); + fo->write(&file_image[x.offset], x.size); total_out += x.size; + // Kepp the input side in sync + total_in += x.size; + fi->seek(x.size + x.offset, SEEK_SET); } if (hdr_u_len) { // first time linfo.l_checksum = 0; // preliminary @@ -5241,6 +5285,8 @@ int PackLinuxElf64::pack2(OutputFile *fo, Filter &ft) x.offset = p_offset + hdr_u_len; x.size = p_filesz - hdr_u_len; + Elf64_Phdr *phdr = k + (Elf64_Phdr *)(1+ (Elf64_Ehdr *)&lowmem[0]); + set_te32(&phdr->p_flags, Elf64_Phdr::PF_X | get_te32(&phdr->p_flags)); hdr_u_len = 0; // no longer the first time } // The remainder above xct_off in first compressible PT_LOAD @@ -5431,29 +5477,8 @@ void PackLinuxElf32mipsel::defineSymbols(Filter const *ft) PackLinuxElf32::defineSymbols(ft); } -void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) +void PackLinuxElf32::forward_Shdrs(OutputFile *fo) { - if (!xct_off) { - overlay_offset = sz_elf_hdrs + sizeof(linfo); - } - - if (opt->o_unix.preserve_build_id) { - // calc e_shoff here and write shdrout, then o_shstrtab - //NOTE: these are pushed last to ensure nothing is stepped on - //for the UPX structure. - unsigned const len = fpad4(fo, total_out); - set_te32(&elfout.ehdr.e_shoff,len); - - int const ssize = sizeof(shdrout); - - shdrout.shdr[2].sh_offset = len+ssize; - shdrout.shdr[1].sh_offset = shdrout.shdr[2].sh_offset+shdrout.shdr[2].sh_size; - - fo->write(&shdrout, ssize); - - fo->write(o_shstrtab,shdrout.shdr[2].sh_size); - fo->write(buildid_data,shdrout.shdr[1].sh_size); - } if (saved_opt_android_shlib) { // Forward select _Shdr // Keep _Shdr for rtld data (below xct_off). // Discard _Shdr for compressed regions. @@ -5556,6 +5581,138 @@ void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) fo->write(eho, sizeof(*eho)); fo->seek(0, SEEK_END); } +} + +void PackLinuxElf64::forward_Shdrs(OutputFile *fo) +{ + if (saved_opt_android_shlib) { // Forward select _Shdr + // Keep _Shdr for rtld data (below xct_off). + // Discard _Shdr for compressed regions. + // Keep _Shdr for SHF_WRITE. + // Discard _Shdr with (0==sh_addr), except _Shdr[0] + // Keep ARM_ATTRIBUTES + Elf64_Ehdr *eho = (Elf64_Ehdr *)lowmem.getVoidPtr(); + MemBuffer mb_ask_for(e_shnum * sizeof(eho->e_shnum)); + memset(mb_ask_for, 0, mb_ask_for.getSize()); + unsigned short *const ask_for = (unsigned short *)mb_ask_for.getVoidPtr(); + + MemBuffer mb_shdro(e_shnum * sizeof(*shdri)); + Elf64_Shdr *sh_out0 = (Elf64_Shdr *)mb_shdro.getVoidPtr(); + Elf64_Shdr *sh_out = sh_out0; + Elf64_Shdr *sh_in = shdri; + + memset(sh_out, 0, sizeof(*sh_out)); // blank sh_out[0] + ++sh_in; ++sh_out; unsigned n_sh_out = 1; + + for (unsigned j = 1; j < e_shnum; ++j, ++sh_in) { + u64_t sh_offset = get_te64(&sh_in->sh_offset); + u64_t sh_flags = get_te64(&sh_in->sh_flags); + unsigned sh_info = get_te16(&sh_in->sh_info); + if (ask_for[j]) { // Some previous _Shdr requested me + // Tell them my new index + set_te16(&sh_out0[ask_for[j]].sh_info, n_sh_out); + } + ask_for[sh_info] = j; // Enter my request, if any + if ( (sh_offset && sh_offset < xct_off) + || (Elf64_Shdr::SHF_WRITE & sh_flags) + || (j == e_shstrndx) + || (sec_arm_attr == sh_in) + ) { + *sh_out = *sh_in; + if (sec_arm_attr == sh_in) { + set_te64(&sh_out->sh_offset, total_out); + fi->seek(get_te64(&sh_in->sh_offset), SEEK_SET); + u64_t len = get_te64(&sh_in->sh_size); + fi->read(ibuf, len); + fo->write(ibuf, len); + total_out += len; + } + if (j == e_shstrndx) { + set_te16(&eho->e_shstrndx, sh_out - (Elf64_Shdr *)mb_shdro.getVoidPtr()); + set_te64(&sh_out->sh_offset, total_out); + fi->seek(get_te64(&sh_in->sh_offset), SEEK_SET); + u64_t len = get_te64(&sh_in->sh_size); + fi->read(ibuf, len); + fo->write(ibuf, len); + total_out += len; + } + if (Elf64_Shdr::SHF_WRITE & sh_flags) { + set_te64(&sh_out->sh_offset, so_slide + get_te64(&sh_out->sh_offset)); + } + ++sh_out; ++n_sh_out; + } + } + total_out = fpad4(fo, total_out); + set_te64(&eho->e_shoff, total_out); + unsigned len = (char *)sh_out - (char *)mb_shdro.getVoidPtr(); + set_te16(&eho->e_shnum, len / sizeof(*sh_out)); + fo->write(mb_shdro, len); + total_out += len; + fo->seek(0, SEEK_SET); + fo->write(eho, sizeof(*eho)); + fo->seek(0, SEEK_END); + } + else if (sec_arm_attr) { + // Forward just ARM_ATTRIBUTES + Elf64_Shdr shdr_aa[3]; + u64_t const attr_len = get_te64(&sec_arm_attr->sh_size); + char const str_aa[] = "\x00" ".shstrtab\x00" ".ARM.attributes\x00"; + + memset(shdr_aa, 0, sizeof shdr_aa); + // shstrtab + set_te64(&shdr_aa[1].sh_name, 1); + set_te64(&shdr_aa[1].sh_type, Elf64_Shdr::SHT_STRTAB); + set_te64(&shdr_aa[1].sh_offset, total_out); + set_te64(&shdr_aa[1].sh_size, sizeof(str_aa)); + set_te64(&shdr_aa[1].sh_addralign, 1); + fo->write(str_aa, sizeof(str_aa)); total_out += sizeof(str_aa); + + // ARM_ATTRIBUTES + set_te64(&shdr_aa[2].sh_name, 11); + set_te64(&shdr_aa[2].sh_type, Elf64_Shdr::SHT_ARM_ATTRIBUTES); + set_te64(&shdr_aa[2].sh_offset, total_out); + set_te64(&shdr_aa[2].sh_size, attr_len); + set_te64(&shdr_aa[2].sh_addralign, 1); + fo->write(&file_image[get_te64(&sec_arm_attr->sh_offset)], attr_len); + total_out = fpad4(fo, total_out += attr_len); + + Elf64_Ehdr *eho = &elfout.ehdr; + set_te16(&eho->e_shnum, 3); + set_te64(&eho->e_shoff, total_out); + set_te16(&eho->e_shstrndx, 1); + fo->write(shdr_aa, sizeof(shdr_aa)); + total_out += sizeof(shdr_aa); + + fo->seek(0, SEEK_SET); + fo->write(eho, sizeof(*eho)); + fo->seek(0, SEEK_END); + } +} + +void PackLinuxElf32::pack4(OutputFile *fo, Filter &ft) +{ + if (!xct_off) { + overlay_offset = sz_elf_hdrs + sizeof(linfo); + } + + forward_Shdrs(fo); + if (opt->o_unix.preserve_build_id) { + // calc e_shoff here and write shdrout, then o_shstrtab + //NOTE: these are pushed last to ensure nothing is stepped on + //for the UPX structure. + unsigned const len = fpad4(fo, total_out); + set_te32(&elfout.ehdr.e_shoff,len); + + int const ssize = sizeof(shdrout); + + shdrout.shdr[2].sh_offset = len+ssize; + shdrout.shdr[1].sh_offset = shdrout.shdr[2].sh_offset+shdrout.shdr[2].sh_size; + + fo->write(&shdrout, ssize); + + fo->write(o_shstrtab,shdrout.shdr[2].sh_size); + fo->write(buildid_data,shdrout.shdr[1].sh_size); + } // Cannot pre-round .p_memsz. If .p_filesz < .p_memsz, then kernel // tries to make .bss, which requires PF_W. @@ -5939,18 +6096,29 @@ void PackLinuxElf64::un_shlib_1( fi->seek(0, SEEK_SET); fi->readx(ibuf, umin(blocksize, file_size)); + // Determine if the extra page with copy of _Shdrs was spliced in. + // This used to be the result of --anroid-shlib. + // But in 2023-02 the fowarding of ARM_ATTRIBUTES (by appending) + // takes care of this, so the 5th word before e_entry does not + // have the low bit 1, so is_asl should not be set. + // However, .so that were compressed before 2023-03 + // may be marked. e_shoff = get_te64(&ehdri.e_shoff); + if (e_shoff && e_shnum // +36: (sizeof(PackHeader) + sizeof(overlay_offset)) // after Shdrs for ARM_ATTRIBUTES - if (e_shoff && e_shnum - && ((e_shoff + sizeof(Elf64_Shdr) * e_shnum) + 36) < (upx_uint64_t)file_size) { // --android-shlib - is_asl = 1; - fi->seek(e_shoff, SEEK_SET); - mb_shdr.alloc( sizeof(Elf64_Shdr) * e_shnum); - shdri = (Elf64_Shdr *)mb_shdr.getVoidPtr(); - fi->readx(shdri, sizeof(Elf64_Shdr) * e_shnum); - yct_off = get_te64(&shdri->sh_offset); // for the output file (de-compresssed) - xct_off = asl_delta + yct_off; // for the input file (compressed) + && (((e_shoff + sizeof(Elf64_Shdr) * e_shnum) + 36) < (upx_uint64_t)file_size) + ) { // possible --android-shlib + unsigned x = get_te32(&file_image[get_te64(&ehdri.e_entry) - (1+ 4)*sizeof(int)]); + if (1 & x) { // the clincher + is_asl = 1; + fi->seek(e_shoff, SEEK_SET); + mb_shdr.alloc( sizeof(Elf64_Shdr) * e_shnum); + shdri = (Elf64_Shdr *)mb_shdr.getVoidPtr(); + fi->readx(shdri, sizeof(Elf64_Shdr) * e_shnum); + yct_off = get_te64(&shdri->sh_offset); // for the output file (de-compresssed) + xct_off = asl_delta + yct_off; // for the input file (compressed) + } } // Decompress first Extent. Old style covers [0, xct_off) @@ -6397,6 +6565,11 @@ void PackLinuxElf32::un_DT_INIT( else if (R_ARM_ABS32 == r_type) { word = 0; } + else { + char msg[40]; snprintf(msg, sizeof(msg), "unknown relocation: %#x", + r_type); + throwCantUnpack(msg); + } } else if (Elf32_Ehdr::EM_386 == e_machine) { if (R_386_RELATIVE == r_type) { @@ -6409,6 +6582,11 @@ void PackLinuxElf32::un_DT_INIT( else if (R_386_32 == r_type) { word = 0; } + else { + char msg[40]; snprintf(msg, sizeof(msg), "unknown relocation: %#x", + r_type); + throwCantUnpack(msg); + } } if (fo) { fo->seek(arr_off, SEEK_SET); @@ -6505,7 +6683,10 @@ void PackLinuxElf64::un_DT_INIT( case Elf64_Dyn::DT_RELA: { dt_rela = val; } break; case Elf64_Dyn::DT_JMPREL: { dt_jmprel = val; } break; case Elf64_Dyn::DT_PLTRELSZ: { dt_pltrelsz = val; - n_plt = 3+ (dt_pltrelsz / sizeof(Elf64_Rela)); // FIXME: "3+" + n_plt = dt_pltrelsz / sizeof(Elf32_Rel); + if (is_asl) { + n_plt += 3; // FIXME + } }; break; case Elf64_Dyn::DT_PLTGOT: { plt_va = dt_pltgot = val; (void)dt_pltgot;} @@ -6513,11 +6694,11 @@ void PackLinuxElf64::un_DT_INIT( case Elf64_Dyn::DT_PREINIT_ARRAY: case Elf64_Dyn::DT_INIT_ARRAY: case Elf64_Dyn::DT_FINI_ARRAY: - case Elf64_Dyn::DT_FINI: { + case Elf64_Dyn::DT_FINI: if (is_asl) { set_te64(&dyn->d_val, val - asl_delta); }; break; } // end switch() on tag when is_asl - if (upx_dt_init == tag) { + if (upx_dt_init == tag) { // the easy case if (Elf64_Dyn::DT_INIT == tag) { set_te64(&dyn->d_val, old_dtinit); if (!old_dtinit) { // compressor took the slot @@ -6525,28 +6706,67 @@ void PackLinuxElf64::un_DT_INIT( dyn->d_val = 0; } } - else if (Elf64_Dyn::DT_INIT_ARRAY == tag + // Apparently the hard case is common for some Android IDEs. + else if (Elf32_Dyn::DT_INIT_ARRAY == tag || Elf64_Dyn::DT_PREINIT_ARRAY == tag) { - // The slot must have a R_*_RELATIVE relocation (is_shlib, - // after all), but ElfXX_Rela ignores the initial contents! - // So changing the value will get ignored. Do it anyway. - // FIXME: we must fix the Rela ? - Elf64_Phdr const *phdr = phdro; - for (unsigned j = 0; j < e_phnum; ++j, ++phdr) { - upx_uint64_t vaddr = get_te64(&phdr->p_vaddr); - upx_uint64_t filesz = get_te64(&phdr->p_filesz); - unsigned q = val - (is_asl ? asl_delta : 0) - vaddr; - if (q < filesz) { - upx_uint64_t offset = get_te64(&phdr->p_offset); - // Rela overwrites the target; assumed default is 0 - upx_uint64_t oldval = 0; //set_te64(&oldval, old_dtinit); - // Counter-act unRel64 if asl_delta - // FIXME? the in-memory copy? - if (fo) { - fo->seek(q + offset, SEEK_SET); - fo->write(&oldval, sizeof(oldval)); + // 'val' is the RVA of the first slot, which is the slot that + // the compressor changed to be the entry to the run-time stub. + Elf64_Rel *rp = (Elf64_Rel *)elf_find_dynamic(Elf64_Dyn::DT_NULL); + ((Elf64_Dyn *)elf_find_dynptr(Elf64_Dyn::DT_NULL))->d_val = 0; + if (rp) { + // Compressor saved the original *rp in dynsym[0] + Elf64_Rel *rp_unc = (Elf64_Rel *)&dynsym[0]; // pointer + rp->r_info = rp_unc->r_info; // restore original r_info; r_offset not touched + + unsigned e_entry = get_te64(&ehdri.e_entry); + unsigned init_rva = get_te64(&file_image[e_entry - 3*sizeof(unsigned)]); + unsigned arr_rva = get_te64(&rp_unc->r_offset); + Elf64_Phdr const *phdr = elf_find_Phdr_for_va(arr_rva, phdro, e_phnum); + unsigned arr_off = (arr_rva - get_te64(&phdr->p_vaddr)) + get_te64(&phdr->p_offset); + + rp_unc->r_offset = 0; rp_unc->r_info = 0; + if (fo) { + fo->seek(elf_unsigned_dynamic(Elf64_Dyn::DT_SYMTAB), SEEK_SET); + fo->rewrite(rp_unc, sizeof(Elf64_Rel)); // clear dynsym[0] + + fo->seek((char *)rp - (char *)&file_image[0], SEEK_SET); + fo->rewrite(rp, sizeof(*rp)); // restore original *rp + } + + // Set arr[0] to the first user init routine. + unsigned r_info = get_te64(&rp->r_info); + unsigned r_type = ELF64_R_TYPE(r_info); + u64_t word; + if (Elf64_Ehdr::EM_ARM64 == e_machine) { + if (R_AARCH64_RELATIVE == r_type) { + set_te64(&word, init_rva); } - break; + else if (R_AARCH64_ABS64 == r_type) { + word = 0; + } + else { + char msg[40]; snprintf(msg, sizeof(msg), "unknown relocation: %#x", + r_type); + throwCantUnpack(msg); + } + } + else if (Elf64_Ehdr::EM_AMD64 == e_machine) { + if (R_X86_64_RELATIVE == r_type) { + set_te64(&word, init_rva); + } + else if (R_X86_64_64 == r_type) { + word = 0; + } + else { + char msg[40]; snprintf(msg, sizeof(msg), "unknown relocation: %#x", + r_type); + throwCantUnpack(msg); + } + } + if (fo) { + fo->seek(arr_off, SEEK_SET); + fo->rewrite(&word, sizeof(word)); + fo->seek(0, SEEK_END); } } } @@ -7147,6 +7367,23 @@ Elf32_Dyn *PackLinuxElf32::elf_find_dynptr(unsigned int key) const return nullptr; } +Elf64_Dyn *PackLinuxElf64::elf_find_dynptr(unsigned int key) const +{ + Elf64_Dyn *dynp= dynseg; + if (dynp) { + Elf64_Dyn *const last = (Elf64_Dyn *)(sz_dynseg + (char *)dynseg); + for (; dynp < last; ++dynp) { + if (get_te64(&dynp->d_tag)==key) { + return dynp; + } + if (Elf64_Dyn::DT_NULL == dynp->d_tag) { + return nullptr; + } + } + } + return nullptr; +} + void * PackLinuxElf32::elf_find_dynamic(unsigned int key) const { diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h index 27e93166..b773d236 100644 --- a/src/p_lx_elf.h +++ b/src/p_lx_elf.h @@ -155,6 +155,7 @@ protected: virtual int pack2(OutputFile *, Filter &) override; // append compressed data virtual off_t pack3(OutputFile *, Filter &) override; // append loader virtual void pack4(OutputFile *, Filter &) override; // append pack header + virtual void forward_Shdrs(OutputFile *fo); virtual void unpack(OutputFile *fo) override; virtual void un_asl_dynsym(unsigned orig_file_size, OutputFile *); virtual void un_shlib_1( @@ -312,6 +313,7 @@ protected: virtual int pack2(OutputFile *, Filter &) override; // append compressed data virtual off_t pack3(OutputFile *, Filter &) override; // append loader virtual void pack4(OutputFile *, Filter &) override; // append pack header + virtual void forward_Shdrs(OutputFile *fo); virtual void unpack(OutputFile *fo) override; virtual void un_asl_dynsym(unsigned orig_file_size, OutputFile *); virtual void un_shlib_1( @@ -352,10 +354,11 @@ protected: virtual Elf64_Sym const *elf_lookup(char const *) const; virtual upx_uint64_t elf_get_offset_from_address(upx_uint64_t) const; - virtual Elf64_Phdr *elf_find_Phdr_for_va(upx_uint64_t addr, Elf64_Phdr *phdr, unsigned phnum); + virtual Elf64_Phdr const *elf_find_Phdr_for_va(upx_uint64_t addr, Elf64_Phdr const *phdr, unsigned phnum); Elf64_Phdr const *elf_find_ptype(unsigned type, Elf64_Phdr const *phdr0, unsigned phnum); Elf64_Shdr const *elf_find_section_name(char const *) const; Elf64_Shdr *elf_find_section_type(unsigned) const; + Elf64_Dyn *elf_find_dynptr(unsigned) const; unsigned elf_find_table_size(unsigned dt_type, unsigned sh_type); void sort_DT64_offsets(Elf64_Dyn const *const dynp0); int is_LOAD64(Elf64_Phdr const *phdr) const; // beware confusion with (1+ LO_PROC) @@ -389,8 +392,9 @@ protected: Elf64_Sym const *jni_onload_sym; Elf64_Shdr *sec_strndx; - Elf64_Shdr const *sec_dynsym; + Elf64_Shdr *sec_dynsym; Elf64_Shdr const *sec_dynstr; + Elf64_Shdr *sec_arm_attr; // SHT_ARM_ATTRIBUTES; __packed_struct(cprElfHdr1) Elf64_Ehdr ehdr;