WIP: PackLinuxElf64::unpack working for shared libraries (non-Android)

modified:   p_lx_elf.cpp
	modified:   p_lx_elf.h
	modified:   p_unix.cpp
This commit is contained in:
John Reiser 2021-03-05 16:20:36 -08:00 committed by Markus F.X.J. Oberhumer
parent c9cb42df40
commit 35555bfb8a
3 changed files with 109 additions and 124 deletions

View File

@ -4568,6 +4568,7 @@ PackLinuxElf64::unRela64(
void PackLinuxElf64::un_shlib_1( void PackLinuxElf64::un_shlib_1(
OutputFile *const fo, OutputFile *const fo,
Elf64_Phdr *const phdro,
unsigned &c_adler, unsigned &c_adler,
unsigned &u_adler, unsigned &u_adler,
Elf64_Phdr const *const dynhdr, Elf64_Phdr const *const dynhdr,
@ -4637,15 +4638,16 @@ void PackLinuxElf64::un_shlib_1(
c_adler, u_adler, false, szb_info); c_adler, u_adler, false, szb_info);
// Copy (slide) the remaining PT_LOAD; they are not compressed // Copy (slide) the remaining PT_LOAD; they are not compressed
Elf64_Phdr *phdr = phdri; Elf64_Phdr *phdr = phdro;
unsigned slide = 0; unsigned slide = 0;
int first = 0; int first = 0;
for (unsigned k = 0; k < e_phnum; ++k, ++phdr) { for (unsigned k = 0; k < e_phnum; ++k, ++phdr) {
if (PT_LOAD64==get_te32(&phdr->p_type)) { unsigned type = get_te32(&phdr->p_type);
unsigned vaddr = get_te64(&phdr->p_vaddr); unsigned vaddr = get_te64(&phdr->p_vaddr);
if (xct_off <= vaddr) { unsigned offset = get_te64(&phdr->p_offset);
unsigned offset = get_te64(&phdr->p_offset); unsigned filesz = get_te64(&phdr->p_filesz);
unsigned filesz = get_te64(&phdr->p_filesz); if (xct_off <= vaddr) {
if (PT_LOAD64==type) {
if (0==first++) { // the partially-compressed PT_LOAD if (0==first++) { // the partially-compressed PT_LOAD
set_te64(&phdr->p_filesz, ph.u_len + xct_off - vaddr); set_te64(&phdr->p_filesz, ph.u_len + xct_off - vaddr);
set_te64(&phdr->p_memsz, ph.u_len + xct_off - vaddr); set_te64(&phdr->p_memsz, ph.u_len + xct_off - vaddr);
@ -4653,134 +4655,111 @@ void PackLinuxElf64::un_shlib_1(
else { // subsequent PT_LOAD else { // subsequent PT_LOAD
fi->seek(offset, SEEK_SET); fi->seek(offset, SEEK_SET);
fi->readx(ibuf, filesz); fi->readx(ibuf, filesz);
total_in += filesz;
if (0==slide) { if (0==slide) {
slide = vaddr - offset; slide = vaddr - offset;
} }
set_te64(&phdr->p_offset, slide + offset); if (fo) {
fo->seek(slide + offset, SEEK_SET); fo->seek(slide + offset, SEEK_SET);
fo->write(ibuf, filesz); fo->write(ibuf, filesz);
total_out = filesz + slide + offset; // high-water mark
}
} }
} }
set_te64(&phdr->p_offset, slide + offset);
} }
} }
fi->seek(xct_off + sizeof(linfo) + sizeof(p_info) + sizeof(b_info) + up4(ph.c_len), SEEK_SET); // loader offset if (fo) { // Rewrite Phdrs after sliding
fo->seek(sizeof(Elf64_Ehdr), SEEK_SET);
fo->rewrite(phdro, e_phnum * sizeof(Elf64_Phdr));
}
// loader offset
fi->seek(xct_off + sizeof(linfo) + sizeof(p_info) + sizeof(b_info) + up4(ph.c_len), SEEK_SET);
} }
void PackLinuxElf64::un_DT_INIT( void PackLinuxElf64::un_DT_INIT(
Elf64_Phdr const *phdr0,
unsigned u_phnum,
unsigned old_dtinit, unsigned old_dtinit,
Elf64_Phdr const *const phdro,
Elf64_Phdr const *const dynhdr, // in phdri
OutputFile *fo, OutputFile *fo,
unsigned is_asl unsigned is_asl
) )
{ {
// DT_INIT must be restored. // DT_INIT must be restored.
// Search the Phdrs of compressed
int n_ptload = 0;
old_data_off = 0;
old_data_len = 0;
Elf64_Phdr const *phdr = phdr0;
for (unsigned j=0; j < u_phnum; ++phdr, ++j) {
if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) {
old_data_off = get_te64(&phdr->p_offset);
old_data_len = get_te64(&phdr->p_filesz);
break;
}
}
// If android_shlib, then the asl_delta relocations must be un-done. // If android_shlib, then the asl_delta relocations must be un-done.
upx_uint64_t load_off = 0; upx_uint64_t dt_pltrelsz(0), dt_jmprel(0);
phdr = phdr0; upx_uint64_t dt_relasz(0), dt_rela(0);
n_ptload = 0; upx_uint64_t const dyn_len = get_te64(&dynhdr->p_filesz);
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) { upx_uint64_t const dyn_off = get_te64(&dynhdr->p_offset);
if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) { if ((unsigned long)file_size < (dyn_len + dyn_off)) {
load_off = get_te64(&phdr->p_offset); char msg[50]; snprintf(msg, sizeof(msg),
load_va = get_te64(&phdr->p_vaddr); "bad PT_DYNAMIC .p_filesz %#lx", (long unsigned)dyn_len);
fi->seek(old_data_off, SEEK_SET); throwCantUnpack(msg);
fi->readx(ibuf, old_data_len); }
total_in += old_data_len; fi->seek(dyn_off, SEEK_SET);
total_out += old_data_len; fi->readx(ibuf, dyn_len);
Elf64_Dyn *dyn = (Elf64_Dyn *)(void *)ibuf;
dynseg = dyn; invert_pt_dynamic(dynseg, get_te64(&dynhdr->p_filesz));
for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) {
upx_uint64_t const tag = get_te64(&dyn->d_tag);
upx_uint64_t val = get_te64(&dyn->d_val);
if (is_asl) switch (tag) {
case Elf64_Dyn::DT_RELASZ: { dt_relasz = val; } break;
case Elf64_Dyn::DT_RELA: { dt_rela = val; } break;
case Elf64_Dyn::DT_PLTRELSZ: { dt_pltrelsz = val; } break;
case Elf64_Dyn::DT_JMPREL: { dt_jmprel = val; } break;
Elf64_Phdr const *udynhdr = phdr0; case Elf64_Dyn::DT_PLTGOT:
for (unsigned j3= 0; j3 < u_phnum; ++j3, ++udynhdr) case Elf64_Dyn::DT_PREINIT_ARRAY:
if (Elf64_Phdr::PT_DYNAMIC==get_te32(&udynhdr->p_type)) { case Elf64_Dyn::DT_INIT_ARRAY:
upx_uint64_t dt_pltrelsz(0), dt_jmprel(0); case Elf64_Dyn::DT_FINI_ARRAY:
upx_uint64_t dt_relasz(0), dt_rela(0); case Elf64_Dyn::DT_FINI: {
upx_uint64_t const dyn_len = get_te64(&udynhdr->p_filesz); set_te64(&dyn->d_val, val - asl_delta);
upx_uint64_t const dyn_off = get_te64(&udynhdr->p_offset); }; break;
if ((unsigned long)file_size < (dyn_len + dyn_off)) { } // end switch() on tag when is_asl
char msg[50]; snprintf(msg, sizeof(msg), if (upx_dt_init == tag) {
"bad PT_DYNAMIC .p_filesz %#lx", (long unsigned)dyn_len); if (Elf64_Dyn::DT_INIT == tag) {
throwCantUnpack(msg); set_te64(&dyn->d_val, old_dtinit);
} if (!old_dtinit) { // compressor took the slot
if (dyn_off < load_off) { dyn->d_tag = Elf64_Dyn::DT_NULL;
continue; // Oops. Not really is_shlib ? [built by 'rust' ?] dyn->d_val = 0;
} }
Elf64_Dyn *dyn = (Elf64_Dyn *)((unsigned char *)ibuf + }
(dyn_off - load_off)); else if (Elf64_Dyn::DT_INIT_ARRAY == tag
dynseg = dyn; invert_pt_dynamic(dynseg, get_te64(&udynhdr->p_filesz)); || Elf64_Dyn::DT_PREINIT_ARRAY == tag) {
for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) { if (val < load_va || (long unsigned)file_size < (long unsigned)val) {
upx_uint64_t const tag = get_te64(&dyn->d_tag); char msg[50]; snprintf(msg, sizeof(msg),
upx_uint64_t val = get_te64(&dyn->d_val); "Bad Dynamic tag %#lx %#lx",
if (is_asl) switch (tag) { (long unsigned)tag, (long unsigned)val);
case Elf64_Dyn::DT_RELASZ: { dt_relasz = val; } break; throwCantUnpack(msg);
case Elf64_Dyn::DT_RELA: { dt_rela = val; } break; }
case Elf64_Dyn::DT_PLTRELSZ: { dt_pltrelsz = val; } break; set_te64(&ibuf[val - load_va], old_dtinit
case Elf64_Dyn::DT_JMPREL: { dt_jmprel = val; } break; + (is_asl ? asl_delta : 0)); // counter-act unRel64
}
case Elf64_Dyn::DT_PLTGOT: }
case Elf64_Dyn::DT_PREINIT_ARRAY: }
case Elf64_Dyn::DT_INIT_ARRAY: if (fo) { // Write updated dt_*.val
case Elf64_Dyn::DT_FINI_ARRAY: upx_uint64_t dyn_offo = get_te64(&phdro[dynhdr - phdri].p_offset);
case Elf64_Dyn::DT_FINI: { fo->seek(dyn_offo, SEEK_SET);
set_te64(&dyn->d_val, val - asl_delta); fo->rewrite(ibuf, dyn_len);
}; break; }
} // end switch() if (is_asl) {
if (upx_dt_init == tag) { lowmem.alloc(xct_off);
if (Elf64_Dyn::DT_INIT == tag) { fi->seek(0, SEEK_SET);
set_te64(&dyn->d_val, old_dtinit); fi->read(lowmem, xct_off); // contains relocation tables
if (!old_dtinit) { // compressor took the slot if (dt_relasz && dt_rela) {
dyn->d_tag = Elf64_Dyn::DT_NULL; Elf64_Rela *const rela0 = (Elf64_Rela *)lowmem.subref(
dyn->d_val = 0; "bad Rela offset", dt_rela, dt_relasz);
} unRela64(dt_rela, rela0, dt_relasz, ibuf, load_va, old_dtinit, fo);
} }
else if (Elf64_Dyn::DT_INIT_ARRAY == tag if (dt_pltrelsz && dt_jmprel) { // FIXME: overlap w/ DT_REL ?
|| Elf64_Dyn::DT_PREINIT_ARRAY == tag) { Elf64_Rela *const jmp0 = (Elf64_Rela *)lowmem.subref(
if (val < load_va || (long unsigned)file_size < (long unsigned)val) { "bad Jmprel offset", dt_jmprel, dt_pltrelsz);
char msg[50]; snprintf(msg, sizeof(msg), unRela64(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, old_dtinit, fo);
"Bad Dynamic tag %#lx %#lx", }
(long unsigned)tag, (long unsigned)val); // Modified relocation tables are re-written by unRela64
throwCantUnpack(msg); }
}
set_te64(&ibuf[val - load_va], old_dtinit
+ (is_asl ? asl_delta : 0)); // counter-act unRel64
}
}
// Modified DT_*.d_val are re-written later from ibuf[]
}
if (is_asl) {
lowmem.alloc(xct_off);
fi->seek(0, SEEK_SET);
fi->read(lowmem, xct_off); // contains relocation tables
if (dt_relasz && dt_rela) {
Elf64_Rela *const rela0 = (Elf64_Rela *)lowmem.subref(
"bad Rela offset", dt_rela, dt_relasz);
unRela64(dt_rela, rela0, dt_relasz, ibuf, load_va, old_dtinit, fo);
}
if (dt_pltrelsz && dt_jmprel) { // FIXME: overlap w/ DT_REL ?
Elf64_Rela *const jmp0 = (Elf64_Rela *)lowmem.subref(
"bad Jmprel offset", dt_jmprel, dt_pltrelsz);
unRela64(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, old_dtinit, fo);
}
// Modified relocation tables are re-written by unRela64
}
}
if (fo) {
fo->seek(get_te64(&phdr->p_offset), SEEK_SET);
fo->rewrite(ibuf, old_data_len);
}
}
}
} }
void PackLinuxElf64::unpack(OutputFile *fo) void PackLinuxElf64::unpack(OutputFile *fo)
@ -4834,6 +4813,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
unsigned u_adler = upx_adler32(nullptr, 0); unsigned u_adler = upx_adler32(nullptr, 0);
unsigned is_shlib = 0; unsigned is_shlib = 0;
MemBuffer phdro;
Elf64_Phdr const *const dynhdr = elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum); Elf64_Phdr const *const dynhdr = elf_find_ptype(Elf64_Phdr::PT_DYNAMIC, phdri, c_phnum);
if (dynhdr) { if (dynhdr) {
upx_uint64_t dyn_offset = get_te64(&dynhdr->p_offset); upx_uint64_t dyn_offset = get_te64(&dynhdr->p_offset);
@ -4843,9 +4823,10 @@ void PackLinuxElf64::unpack(OutputFile *fo)
if (!(Elf64_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf64_Dyn::DT_FLAGS_1))) { if (!(Elf64_Dyn::DF_1_PIE & elf_unsigned_dynamic(Elf64_Dyn::DT_FLAGS_1))) {
is_shlib = 1; is_shlib = 1;
u_phnum = get_te16(&ehdri.e_phnum); u_phnum = get_te16(&ehdri.e_phnum);
un_shlib_1(fo, c_adler, u_adler, dynhdr, orig_file_size, szb_info); phdro.alloc(u_phnum * sizeof(Elf64_Phdr));
memcpy(phdro, phdri, u_phnum * sizeof(*phdri));
un_shlib_1(fo, (Elf64_Phdr *)(void *)phdro, c_adler, u_adler, dynhdr, orig_file_size, szb_info);
*ehdr = ehdri; *ehdr = ehdri;
memcpy(1+ehdr, phdri, u_phnum * sizeof(*phdri));
} }
} }
else { // main executable else { // main executable
@ -4873,6 +4854,8 @@ void PackLinuxElf64::unpack(OutputFile *fo)
if ((umin64(MAX_ELF_HDR, ph.u_len) - sizeof(Elf64_Ehdr))/sizeof(Elf64_Phdr) < u_phnum) { if ((umin64(MAX_ELF_HDR, ph.u_len) - sizeof(Elf64_Ehdr))/sizeof(Elf64_Phdr) < u_phnum) {
throwCantUnpack("bad compressed e_phnum"); throwCantUnpack("bad compressed e_phnum");
} }
phdro.alloc(u_phnum * sizeof(Elf64_Phdr));
memcpy(phdro, 1+ ehdr, u_phnum * sizeof(Elf64_Phdr));
#undef MAX_ELF_HDR #undef MAX_ELF_HDR
// Decompress each PT_LOAD. // Decompress each PT_LOAD.
@ -4926,7 +4909,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
} }
// The gaps between PT_LOAD and after last PT_LOAD // The gaps between PT_LOAD and after last PT_LOAD
phdr = (Elf64_Phdr *)&u[sizeof(*ehdr)]; phdr = (Elf64_Phdr *)(void *)phdro;
upx_uint64_t hi_offset(0); upx_uint64_t hi_offset(0);
for (unsigned j = 0; j < u_phnum; ++j) { for (unsigned j = 0; j < u_phnum; ++j) {
if (PT_LOAD64==phdr[j].p_type if (PT_LOAD64==phdr[j].p_type
@ -4942,7 +4925,8 @@ void PackLinuxElf64::unpack(OutputFile *fo)
fo->seek(where, SEEK_SET); fo->seek(where, SEEK_SET);
unpackExtent(size, fo, unpackExtent(size, fo,
c_adler, u_adler, false, szb_info, c_adler, u_adler, false, szb_info,
(phdr[j].p_offset != hi_offset)); is_shlib && ((phdr[j].p_offset != hi_offset)));
// FIXME: should not depend on is_shlib ?
} }
} }
@ -4961,7 +4945,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
} }
if (is_shlib) { if (is_shlib) {
un_DT_INIT(phdr, u_phnum, old_dtinit, fo, is_asl); un_DT_INIT(old_dtinit, (Elf64_Phdr *)(void *)phdro, dynhdr, fo, is_asl);
} }
// update header with totals // update header with totals

View File

@ -271,6 +271,7 @@ protected:
virtual void unpack(OutputFile *fo); virtual void unpack(OutputFile *fo);
virtual void un_shlib_1( virtual void un_shlib_1(
OutputFile *const fo, OutputFile *const fo,
Elf64_Phdr *const phdro,
unsigned &c_adler, unsigned &c_adler,
unsigned &u_adler, unsigned &u_adler,
Elf64_Phdr const *const dynhdr, Elf64_Phdr const *const dynhdr,
@ -278,9 +279,9 @@ protected:
unsigned const szb_info unsigned const szb_info
); );
virtual void un_DT_INIT( virtual void un_DT_INIT(
Elf64_Phdr const *phdr,
unsigned u_phnum,
unsigned old_dtinit, unsigned old_dtinit,
Elf64_Phdr const *phdro,
Elf64_Phdr const *dynhdr, // in phdri
OutputFile *fo, OutputFile *fo,
unsigned is_asl unsigned is_asl
); );

View File

@ -470,6 +470,7 @@ void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo,
int j = blocksize + OVERHEAD - sz_cpr; int j = blocksize + OVERHEAD - sz_cpr;
fi->readx(ibuf+j, sz_cpr); fi->readx(ibuf+j, sz_cpr);
total_in += sz_cpr;
// update checksum of compressed data // update checksum of compressed data
c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler); c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler);
// decompress // decompress
@ -499,8 +500,6 @@ void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo,
} }
// update checksum of uncompressed data // update checksum of uncompressed data
u_adler = upx_adler32(ibuf + j, sz_unc, u_adler); u_adler = upx_adler32(ibuf + j, sz_unc, u_adler);
total_in += sz_cpr;
total_out += sz_unc;
// write block // write block
if (fo) { if (fo) {
if (is_rewrite) { if (is_rewrite) {
@ -508,6 +507,7 @@ void PackUnix::unpackExtent(unsigned wanted, OutputFile *fo,
} }
else { else {
fo->write(ibuf + j, sz_unc); fo->write(ibuf + j, sz_unc);
total_out += sz_unc;
} }
} }
if (wanted < (unsigned)sz_unc) if (wanted < (unsigned)sz_unc)