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
+40 -56
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,72 +4655,52 @@ 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;
phdr = phdr0;
n_ptload = 0;
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) {
load_off = get_te64(&phdr->p_offset);
load_va = get_te64(&phdr->p_vaddr);
fi->seek(old_data_off, SEEK_SET);
fi->readx(ibuf, old_data_len);
total_in += old_data_len;
total_out += old_data_len;
Elf64_Phdr const *udynhdr = phdr0;
for (unsigned j3= 0; j3 < u_phnum; ++j3, ++udynhdr)
if (Elf64_Phdr::PT_DYNAMIC==get_te32(&udynhdr->p_type)) {
upx_uint64_t dt_pltrelsz(0), dt_jmprel(0); upx_uint64_t dt_pltrelsz(0), dt_jmprel(0);
upx_uint64_t dt_relasz(0), dt_rela(0); upx_uint64_t dt_relasz(0), dt_rela(0);
upx_uint64_t const dyn_len = get_te64(&udynhdr->p_filesz); upx_uint64_t const dyn_len = get_te64(&dynhdr->p_filesz);
upx_uint64_t const dyn_off = get_te64(&udynhdr->p_offset); upx_uint64_t const dyn_off = get_te64(&dynhdr->p_offset);
if ((unsigned long)file_size < (dyn_len + dyn_off)) { if ((unsigned long)file_size < (dyn_len + dyn_off)) {
char msg[50]; snprintf(msg, sizeof(msg), char msg[50]; snprintf(msg, sizeof(msg),
"bad PT_DYNAMIC .p_filesz %#lx", (long unsigned)dyn_len); "bad PT_DYNAMIC .p_filesz %#lx", (long unsigned)dyn_len);
throwCantUnpack(msg); throwCantUnpack(msg);
} }
if (dyn_off < load_off) { fi->seek(dyn_off, SEEK_SET);
continue; // Oops. Not really is_shlib ? [built by 'rust' ?] fi->readx(ibuf, dyn_len);
} Elf64_Dyn *dyn = (Elf64_Dyn *)(void *)ibuf;
Elf64_Dyn *dyn = (Elf64_Dyn *)((unsigned char *)ibuf + dynseg = dyn; invert_pt_dynamic(dynseg, get_te64(&dynhdr->p_filesz));
(dyn_off - load_off));
dynseg = dyn; invert_pt_dynamic(dynseg, get_te64(&udynhdr->p_filesz));
for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) { for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) {
upx_uint64_t const tag = get_te64(&dyn->d_tag); upx_uint64_t const tag = get_te64(&dyn->d_tag);
upx_uint64_t val = get_te64(&dyn->d_val); upx_uint64_t val = get_te64(&dyn->d_val);
@@ -4735,7 +4717,7 @@ void PackLinuxElf64::un_DT_INIT(
case Elf64_Dyn::DT_FINI: { case Elf64_Dyn::DT_FINI: {
set_te64(&dyn->d_val, val - asl_delta); set_te64(&dyn->d_val, val - asl_delta);
}; break; }; break;
} // end switch() } // end switch() on tag when is_asl
if (upx_dt_init == tag) { if (upx_dt_init == tag) {
if (Elf64_Dyn::DT_INIT == tag) { if (Elf64_Dyn::DT_INIT == tag) {
set_te64(&dyn->d_val, old_dtinit); set_te64(&dyn->d_val, old_dtinit);
@@ -4756,7 +4738,11 @@ void PackLinuxElf64::un_DT_INIT(
+ (is_asl ? asl_delta : 0)); // counter-act unRel64 + (is_asl ? asl_delta : 0)); // counter-act unRel64
} }
} }
// Modified DT_*.d_val are re-written later from ibuf[] }
if (fo) { // Write updated dt_*.val
upx_uint64_t dyn_offo = get_te64(&phdro[dynhdr - phdri].p_offset);
fo->seek(dyn_offo, SEEK_SET);
fo->rewrite(ibuf, dyn_len);
} }
if (is_asl) { if (is_asl) {
lowmem.alloc(xct_off); lowmem.alloc(xct_off);
@@ -4775,13 +4761,6 @@ void PackLinuxElf64::un_DT_INIT(
// Modified relocation tables are re-written by unRela64 // 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
+3 -2
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
); );
+2 -2
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)