mirror of https://github.com/upx/upx.git
Fix de-comression of shared libraries
modified: p_elf_enum.h modified: p_lx_elf.cpp modified: p_lx_elf.h
This commit is contained in:
parent
5d15e57294
commit
7b68aa7bdc
|
@ -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,
|
||||
|
||||
|
|
451
src/p_lx_elf.cpp
451
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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue