1
0
mirror of https://github.com/upx/upx synced 2025-09-28 19:06:07 +08:00

upgrade ::unpack for --android-shlib

modified:   p_elf_enum.h
    	modified:   p_lx_elf.cpp
    	modified:   p_lx_elf.h
    	modified:   stub/src/arm64-linux.shlib-init.S
    	modified:   stub/src/arm.v4t-linux.shlib-init.S
This commit is contained in:
John Reiser 2018-02-02 14:50:56 -08:00
parent d736da4208
commit 95cedb6640
3 changed files with 221 additions and 108 deletions

View File

@ -166,7 +166,10 @@
DT_JMPREL = 23, /* Address of PLT relocs */
DT_INIT_ARRAY = 25, /* Array with addresses of init fct */
DT_FINI_ARRAY = 26, /* Array with addresses of fini fct */
DT_INIT_ARRAYSZ= 27, /* size in bytes */
DT_FINI_ARRAYSZ= 28, /* size in bytes */
DT_PREINIT_ARRAY = 32, /* Array with addresses of preinit fct*/
DT_PREINIT_ARRAYSZ= 33, /* size in bytes */
DT_CHECKSUM = 0x6ffffdf8, /* Only for prelink? */
DT_GNU_HASH = 0x6ffffef5, /* GNU-style hash table */
DT_VERSYM = 0x6ffffff0, /* version[] for each symbol */

View File

@ -3685,6 +3685,50 @@ void PackLinuxElf64::pack4(OutputFile *fo, Filter &ft)
}
}
void
PackLinuxElf32::unRel32(
unsigned dt_rel,
Elf32_Rel *rel0,
unsigned relsz,
MemBuffer &ptload1,
unsigned const load_off,
OutputFile *fo
)
{
Elf32_Rel *rel = rel0;
for (int k = relsz / sizeof(Elf32_Rel); --k >= 0; ++rel) {
unsigned r_offset = get_te32(&rel->r_offset);
unsigned r_info = get_te32(&rel->r_info);
unsigned r_type = ELF32_R_TYPE(r_info);
if (xct_off <= r_offset) {
set_te32(&rel->r_offset, r_offset - asl_delta);
}
if (Elf32_Ehdr::EM_ARM == e_machine) {
if (R_ARM_RELATIVE == r_type) {
unsigned d = r_offset - load_off - asl_delta;
unsigned w = get_te32(&ptload1[d]);
if (xct_off <= w) {
set_te32(&ptload1[d], w - asl_delta);
}
}
if (R_ARM_JUMP_SLOT == r_type) {
++n_jmp_slot;
// .rel.plt contains offset of the "first time" target
unsigned d = r_offset - load_off - asl_delta;
if (plt_off > d) {
plt_off = d;
}
unsigned w = get_te32(&ptload1[d]);
if (xct_off <= w) {
set_te32(&ptload1[d], w - asl_delta);
}
}
}
}
fo->seek(dt_rel, SEEK_SET);
fo->rewrite(rel0, relsz);
}
void
PackLinuxElf64::unRela64(
upx_uint64_t dt_rela,
@ -3737,6 +3781,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
upx_uint64_t old_data_off = 0;
upx_uint64_t old_data_len = 0;
upx_uint64_t old_dtinit = 0;
unsigned is_asl = 0; // is Android Shared Library
unsigned szb_info = sizeof(b_info);
{
@ -3750,6 +3795,9 @@ void PackLinuxElf64::unpack(OutputFile *fo)
fi->seek(overlay_offset - sizeof(l_info), SEEK_SET);
fi->readx(&linfo, sizeof(linfo));
lsize = get_te16(&linfo.l_lsize);
if (UPX_MAGIC_LE32 != get_le32(&linfo.l_magic)) {
throwCantUnpack("l_info corrupted");
}
p_info hbuf; fi->readx(&hbuf, sizeof(hbuf));
unsigned orig_file_size = get_te32(&hbuf.p_filesize);
blocksize = get_te32(&hbuf.p_blocksize);
@ -3821,6 +3869,21 @@ void PackLinuxElf64::unpack(OutputFile *fo)
if (e_shoff == xct_off2) {
xct_off = e_shoff;
}
// un-Relocate dynsym (DT_SYMTAB) which is below xct_off
upx_uint64_t const off_dynsym = get_te64(&sec_dynsym->sh_offset);
upx_uint64_t const sz_dynsym = get_te64(&sec_dynsym->sh_size);
Elf64_Sym *const sym0 = (Elf64_Sym *)ibuf.subref(
"bad dynsym", off_dynsym, sz_dynsym);
Elf64_Sym *sym = sym0;
for (int j = sz_dynsym / sizeof(Elf64_Sym); --j>=0; ++sym) {
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
&& xct_off <= symval) {
set_te64(&sym->st_value, symval - asl_delta);
}
}
}
if (fo) {
fo->write(ibuf + ph.u_len, xct_off - ph.u_len);
@ -3890,6 +3953,7 @@ void PackLinuxElf64::unpack(OutputFile *fo)
unsigned d_info[6]; fi->readx(d_info, sizeof(d_info));
if (0==old_dtinit) {
old_dtinit = d_info[2 + (0==d_info[0])];
is_asl = 1u& d_info[1];
}
fi->seek(lsize - sizeof(d_info), SEEK_CUR);
}
@ -3929,9 +3993,11 @@ void PackLinuxElf64::unpack(OutputFile *fo)
throwCompressedDataViolation();
}
if (is_shlib) { // the non-first PT_LOAD
if (is_shlib) {
// DT_INIT must be restored.
// If android_shlib, then the asl_delta relocations must be un-done.
int n_ptload = 0;
unsigned load_off = 0;
upx_uint64_t load_off = 0;
phdr = (Elf64_Phdr *)&u[sizeof(*ehdr)];
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (PT_LOAD64==get_te32(&phdr->p_type) && 0!=n_ptload++) {
@ -3941,76 +4007,61 @@ void PackLinuxElf64::unpack(OutputFile *fo)
fi->readx(ibuf, old_data_len);
total_in += old_data_len;
total_out += old_data_len;
Elf64_Phdr *dynhdr = (Elf64_Phdr *)&u[sizeof(*ehdr)];
for (unsigned j3= 0; j3 < u_phnum; ++j3, ++dynhdr)
if (Elf64_Phdr::PT_DYNAMIC==get_te32(&dynhdr->p_type)) {
upx_uint64_t dt_pltrelsz(0), dt_jmprel(0);
upx_uint64_t dt_relasz(0), dt_rela(0);
upx_uint64_t const dyn_off = get_te64(&dynhdr->p_offset);
upx_uint64_t const dyn_len = get_te64(&dynhdr->p_filesz);
Elf64_Dyn *dyn = (Elf64_Dyn *)((unsigned char *)ibuf +
(dyn_off - load_off));
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 (Elf64_Dyn::DT_INIT == tag) {
set_te64(&dyn->d_val, old_dtinit);
}
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;
case Elf64_Dyn::DT_PLTGOT:
case Elf64_Dyn::DT_PREINIT_ARRAY:
case Elf64_Dyn::DT_INIT_ARRAY:
case Elf64_Dyn::DT_FINI_ARRAY:
case Elf64_Dyn::DT_FINI: {
set_te64(&dyn->d_val, val - asl_delta);
}; break;
} // end switch()
// 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, 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, 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);
}
}
}
// Restore DT_INIT.d_val
phdr = (Elf64_Phdr *)&u[sizeof(*ehdr)];
if (fo)
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (Elf64_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) {
upx_uint64_t dt_pltrelsz(0), dt_jmprel(0);
upx_uint64_t dt_relasz(0), dt_rela(0);
unsigned const dyn_off = get_te64(&phdr->p_offset);
unsigned const dyn_len = get_te64(&phdr->p_filesz);
Elf64_Dyn *dyn = (Elf64_Dyn *)((unsigned char *)ibuf +
(dyn_off - load_off));
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);
switch (tag) {
case Elf64_Dyn::DT_INIT: {
fo->seek(sizeof(upx_uint64_t) + j2 + dyn_off, SEEK_SET);
fo->rewrite(&old_dtinit, sizeof(old_dtinit));
} break;
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_PLTGOT: {
fo->seek(sizeof(upx_uint64_t) + j2 + dyn_off, SEEK_SET);
val -= asl_delta;
fo->rewrite(&val, sizeof(val));
} break;
case Elf64_Dyn::DT_PLTRELSZ: { // Size in bytes of PLT relocs
dt_pltrelsz = val; (void)dt_pltrelsz;
} break;
case Elf64_Dyn::DT_JMPREL: { // Address of PLT relocs
dt_jmprel = val; (void)dt_jmprel;
} break;
case Elf64_Dyn::DT_RELASZ: { // Total size of Rela relocs
dt_relasz = val;
} break;
case Elf64_Dyn::DT_RELA: { // Address of Rela relocs
dt_rela = val;
} break;
} // end switch()
}
lowmem.alloc(xct_off);
fi->seek(0, SEEK_SET);
fi->read(lowmem, xct_off); // contains relocation tables
if (dt_relasz) {
Elf64_Rela *const rela0 = (Elf64_Rela *)lowmem.subref(
"bad Rela offset", dt_rela, dt_relasz);
unRela64(dt_rela, rela0, dt_relasz, ibuf, load_va, fo);
}
if (dt_pltrelsz) {
Elf64_Rela *const jmp0 = (Elf64_Rela *)lowmem.subref(
"bad Jmprel offset", dt_jmprel, dt_pltrelsz);
plt_off = ~0ull; n_jmp_slot = 0;
unRela64(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, fo);
if (~0ull != plt_off) {
fo->seek(plt_off + load_off, SEEK_SET);
fo->rewrite(&ibuf[plt_off], n_jmp_slot * sizeof(void *));
}
}
}
}
if (fo) fo->seek(0, SEEK_END);
}
// update header with totals
@ -4453,6 +4504,7 @@ void PackLinuxElf32::unpack(OutputFile *fo)
unsigned old_data_off = 0;
unsigned old_data_len = 0;
unsigned old_dtinit = 0;
unsigned is_asl = 0; // is Android Shared Library
unsigned szb_info = sizeof(b_info);
{
@ -4468,6 +4520,9 @@ void PackLinuxElf32::unpack(OutputFile *fo)
fi->seek(overlay_offset - sizeof(l_info), SEEK_SET);
fi->readx(&linfo, sizeof(linfo));
lsize = get_te16(&linfo.l_lsize);
if (UPX_MAGIC_LE32 != get_le32(&linfo.l_magic)) {
throwCantUnpack("l_info corrupted");
}
p_info hbuf; fi->readx(&hbuf, sizeof(hbuf));
unsigned orig_file_size = get_te32(&hbuf.p_filesize);
blocksize = get_te32(&hbuf.p_blocksize);
@ -4475,29 +4530,24 @@ void PackLinuxElf32::unpack(OutputFile *fo)
|| !mem_size_valid(1, blocksize, OVERHEAD))
throwCantUnpack("p_info corrupted");
#define MAX_ELF_HDR 512
union {
unsigned char buf[MAX_ELF_HDR];
struct { Elf32_Ehdr ehdr; Elf32_Phdr phdr; } e;
} u;
COMPILE_TIME_ASSERT(sizeof(u) == MAX_ELF_HDR)
Elf32_Ehdr *const ehdr = (Elf32_Ehdr *) u.buf;
Elf32_Phdr const *phdr = 0;
ibuf.alloc(blocksize + OVERHEAD);
b_info bhdr; memset(&bhdr, 0, sizeof(bhdr));
fi->readx(&bhdr, szb_info);
ph.u_len = get_te32(&bhdr.sz_unc);
ph.c_len = get_te32(&bhdr.sz_cpr);
if (ph.c_len > (unsigned)file_size || ph.c_len == 0 || ph.u_len == 0
|| ph.u_len > sizeof(u))
|| ph.u_len > orig_file_size)
throwCantUnpack("b_info corrupted");
ph.filter_cto = bhdr.b_cto8;
// Peek at resulting Ehdr and Phdrs for use in controlling unpacking.
// Uncompress an extra time, and don't verify or update checksums.
if (ibuf.getSize() < ph.c_len || sizeof(u) < ph.u_len)
MemBuffer u(ph.u_len);
Elf32_Ehdr *const ehdr = (Elf32_Ehdr *)&u[0];
Elf32_Phdr const *phdr = 0;
// Uncompress Ehdr and Phdrs.
if (ibuf.getSize() < ph.c_len) {
throwCompressedDataViolation();
}
fi->readx(ibuf, ph.c_len);
decompress(ibuf, (upx_byte *)ehdr, false);
if (ehdr->e_type !=ehdri.e_type
@ -4506,8 +4556,9 @@ void PackLinuxElf32::unpack(OutputFile *fo)
|| ehdr->e_flags !=ehdri.e_flags
|| ehdr->e_ehsize !=ehdri.e_ehsize
// check EI_MAG[0-3], EI_CLASS, EI_DATA, EI_VERSION
|| memcmp(ehdr->e_ident, ehdri.e_ident, Elf32_Ehdr::EI_OSABI))
|| memcmp(ehdr->e_ident, ehdri.e_ident, Elf32_Ehdr::EI_OSABI)) {
throwCantUnpack("ElfXX_Ehdr corrupted");
}
fi->seek(- (off_t) (szb_info + ph.c_len), SEEK_CUR);
unsigned const u_phnum = get_te16(&ehdr->e_phnum);
@ -4515,9 +4566,11 @@ void PackLinuxElf32::unpack(OutputFile *fo)
unsigned total_out = 0;
unsigned c_adler = upx_adler32(NULL, 0);
unsigned u_adler = upx_adler32(NULL, 0);
#define MAX_ELF_HDR 512
if ((MAX_ELF_HDR - sizeof(Elf32_Ehdr))/sizeof(Elf32_Phdr) < u_phnum) {
throwCantUnpack("bad compressed e_phnum");
}
#undef MAX_ELF_HDR
// Packed ET_EXE has no PT_DYNAMIC.
// Packed ET_DYN has original PT_DYNAMIC for info needed by rtld.
@ -4533,8 +4586,31 @@ void PackLinuxElf32::unpack(OutputFile *fo)
fi->seek(0, SEEK_SET);
fi->readx(ibuf, overlay_offset + sizeof(hbuf) + szb_info + ph.c_len);
overlay_offset -= sizeof(linfo);
xct_off = overlay_offset;
e_shoff = get_te32(&ehdri.e_shoff);
if (e_shoff && shdri) { // --android-shlib
unsigned xct_off2 = get_te32(&shdri->sh_offset);
if (e_shoff == xct_off2) {
xct_off = e_shoff;
}
// un-Relocate dynsym (DT_SYMTAB) which is below xct_off
unsigned const off_dynsym = get_te32(&sec_dynsym->sh_offset);
unsigned const sz_dynsym = get_te32(&sec_dynsym->sh_size);
Elf32_Sym *const sym0 = (Elf32_Sym *)ibuf.subref(
"bad dynsym", off_dynsym, sz_dynsym);
Elf32_Sym *sym = sym0;
for (int j = sz_dynsym / sizeof(Elf32_Sym); --j>=0; ++sym) {
unsigned symval = get_te32(&sym->st_value);
unsigned symsec = get_te16(&sym->st_shndx);
if (Elf32_Sym::SHN_UNDEF != symsec
&& Elf32_Sym::SHN_ABS != symsec
&& xct_off <= symval) {
set_te32(&sym->st_value, symval - asl_delta);
}
}
}
if (fo) {
fo->write(ibuf + ph.u_len, overlay_offset - ph.u_len);
fo->write(ibuf + ph.u_len, xct_off - ph.u_len);
}
// Search the Phdrs of compressed
int n_ptload = 0;
@ -4547,15 +4623,15 @@ void PackLinuxElf32::unpack(OutputFile *fo)
}
}
total_in = overlay_offset;
total_out = overlay_offset;
total_in = xct_off;
total_out = xct_off;
ph.u_len = 0;
// Decompress and unfilter the tail of first PT_LOAD.
phdr = (Elf32_Phdr *) (void *) (1+ ehdr);
for (unsigned j=0; j < u_phnum; ++phdr, ++j) {
if (PT_LOAD32==get_te32(&phdr->p_type)) {
ph.u_len = get_te32(&phdr->p_filesz) - overlay_offset;
ph.u_len = get_te32(&phdr->p_filesz) - xct_off;
break;
}
}
@ -4601,12 +4677,13 @@ void PackLinuxElf32::unpack(OutputFile *fo)
unsigned d_info[4]; fi->readx(d_info, sizeof(d_info));
if (0==old_dtinit) {
old_dtinit = d_info[2 + (0==d_info[0])];
is_asl = 1u& d_info[1];
}
fi->seek(lsize - sizeof(d_info), SEEK_CUR);
}
// The gaps between PT_LOAD and after last PT_LOAD
phdr = (Elf32_Phdr *) (u.buf + sizeof(*ehdr));
phdr = (Elf32_Phdr *)&u[sizeof(*ehdr)];
unsigned hi_offset(0);
for (unsigned j = 0; j < u_phnum; ++j) {
if (PT_LOAD32==phdr[j].p_type
@ -4640,43 +4717,75 @@ void PackLinuxElf32::unpack(OutputFile *fo)
throwCompressedDataViolation();
}
if (is_shlib) { // the non-first PT_LOAD
if (is_shlib) {
// DT_INIT must be restored.
// If android_shlib, then the asl_delta relocations must be un-done.
int n_ptload = 0;
unsigned load_off = 0;
phdr = (Elf32_Phdr *) (u.buf + sizeof(*ehdr));
phdr = (Elf32_Phdr *)&u[sizeof(*ehdr)];
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (PT_LOAD32==get_te32(&phdr->p_type) && 0!=n_ptload++) {
load_off = get_te32(&phdr->p_offset);
load_va = get_te32(&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;
Elf32_Phdr *dynhdr = (Elf32_Phdr *)&u[sizeof(*ehdr)];
for (unsigned j3= 0; j3 < u_phnum; ++j3, ++dynhdr)
if (Elf32_Phdr::PT_DYNAMIC==get_te32(&dynhdr->p_type)) {
unsigned dt_pltrelsz(0), dt_jmprel(0);
unsigned dt_relsz(0), dt_rel(0);
unsigned const dyn_off = get_te32(&dynhdr->p_offset);
unsigned const dyn_len = get_te32(&dynhdr->p_filesz);
Elf32_Dyn *dyn = (Elf32_Dyn *)((unsigned char *)ibuf +
(dyn_off - load_off));
for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) {
unsigned const tag = get_te32(&dyn->d_tag);
unsigned val = get_te32(&dyn->d_val);
if (Elf32_Dyn::DT_INIT == tag) {
set_te32(&dyn->d_val, old_dtinit);
}
if (is_asl) switch (tag) {
case Elf32_Dyn::DT_RELSZ: { dt_relsz = val; } break;
case Elf32_Dyn::DT_REL: { dt_rel = val; } break;
case Elf32_Dyn::DT_PLTRELSZ: { dt_pltrelsz = val; } break;
case Elf32_Dyn::DT_JMPREL: { dt_jmprel = val; } break;
case Elf32_Dyn::DT_PLTGOT:
case Elf32_Dyn::DT_PREINIT_ARRAY:
case Elf32_Dyn::DT_INIT_ARRAY:
case Elf32_Dyn::DT_FINI_ARRAY:
case Elf32_Dyn::DT_FINI: {
set_te32(&dyn->d_val, val - asl_delta);
}; break;
} // end switch()
// 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_relsz && dt_rel) {
Elf32_Rel *const rel0 = (Elf32_Rel *)lowmem.subref(
"bad Rel offset", dt_rel, dt_relsz);
unRel32(dt_rel, rel0, dt_relsz, ibuf, load_va, fo);
}
if (dt_pltrelsz && dt_jmprel) { // FIXME: overlap w/ DT_REL ?
Elf32_Rel *const jmp0 = (Elf32_Rel *)lowmem.subref(
"bad Jmprel offset", dt_jmprel, dt_pltrelsz);
unRel32(dt_jmprel, jmp0, dt_pltrelsz, ibuf, load_va, fo);
}
// Modified relocation tables are re-written by unRel32
}
}
if (fo) {
fo->seek(get_te32(&phdr->p_offset), SEEK_SET);
fo->rewrite(ibuf, old_data_len);
}
}
}
// Restore DT_INIT.d_val
phdr = (Elf32_Phdr *) (u.buf + sizeof(*ehdr));
for (unsigned j= 0; j < u_phnum; ++j, ++phdr) {
if (Elf32_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) {
unsigned const dyn_off = get_te32(&phdr->p_offset);
unsigned const dyn_len = get_te32(&phdr->p_filesz);
Elf32_Dyn *dyn = (Elf32_Dyn *)((unsigned char *)ibuf +
(dyn_off - load_off));
for (unsigned j2= 0; j2 < dyn_len; ++dyn, j2 += sizeof(*dyn)) {
if (Elf32_Dyn::DT_INIT==get_te32(&dyn->d_tag)) {
if (fo) {
fo->seek(sizeof(unsigned) + j2 + dyn_off, SEEK_SET);
fo->rewrite(&old_dtinit, sizeof(old_dtinit));
fo->seek(0, SEEK_END);
}
break;
}
}
}
}
}
// update header with totals
@ -4690,7 +4799,6 @@ void PackLinuxElf32::unpack(OutputFile *fo)
// finally test the checksums
if (ph.c_adler != c_adler || ph.u_adler != u_adler)
throwChecksumError();
#undef MAX_ELF_HDR
}
void PackLinuxElf::unpack(OutputFile * /*fo*/)

View File

@ -123,6 +123,8 @@ protected:
virtual off_t pack3(OutputFile *, Filter &); // append loader
virtual void pack4(OutputFile *, Filter &); // append pack header
virtual void unpack(OutputFile *fo);
virtual void unRel32(unsigned dt_rel, Elf32_Rel *rel0, unsigned relsz,
MemBuffer &membuf, unsigned const load_off, OutputFile *fo);
virtual void generateElfHdr(
OutputFile *,