diff --git a/src/p_vmlinx.cpp b/src/p_vmlinx.cpp index 0d8e5969..5c411ba4 100644 --- a/src/p_vmlinx.cpp +++ b/src/p_vmlinx.cpp @@ -194,6 +194,220 @@ bool PackVmlinuxBase::canPack() return 0 < n_ptload; } +static bool defineFilterSymbols(Linker *linker, const Filter *ft) +{ + if (ft->id == 0) { + linker->defineSymbol("filter_length", 0); + return false; + } + assert(ft->calls > 0); + + if (0x50==(0xF0 & ft->id)) { + linker->defineSymbol("FID", ft->id); + linker->defineSymbol("CTO", ft->cto); + } + else { + linker->defineSymbol("filter_cto", ft->cto); + linker->defineSymbol("filter_length", + (ft->id & 0xf) % 3 == 0 ? ft->calls : + ft->lastcall - ft->calls * 4); + } + return true; +} + +template +void PackVmlinuxBase::pack(OutputFile *fo) +{ + unsigned fo_off = 0; + Ehdr ehdro; + U32 tmp_u32; + + // NULL + // .text(PT_LOADs) .note(1st page) .note(rest) + // .shstrtab .symtab .strtab + Shdr shdro[1+3+3]; + memset(shdro, 0, sizeof(shdro)); + + ibuf.alloc(file_size); + obuf.allocForCompression(file_size); + + // .e_ident, .e_machine, .e_version, .e_flags + memcpy(&ehdro, &ehdri, sizeof(ehdro)); + ehdro.e_type = Ehdr::ET_REL; + ehdro.e_entry = 0; + ehdro.e_phoff = 0; + ehdro.e_shoff = sizeof(ehdro); + ehdro.e_phentsize = 0; + ehdro.e_phnum = 0; + ehdro.e_shnum = 1+3+3; + ehdro.e_shstrndx = 4; + fo->write(&ehdro, sizeof(ehdro)); fo_off+= sizeof(ehdro); + fo->write(shdro, sizeof(shdro)); fo_off+= sizeof(shdro); + + ph.u_len = sz_ptload; + fi->seek(phdri[0].p_offset, SEEK_SET); + fi->readx(ibuf, ph.u_len); + checkAlreadyPacked(ibuf + ph.u_len - 1024, 1024); + + // prepare filter + ph.filter = 0; + Filter ft(ph.level); + ft.buf_len = ph.u_len; + ft.addvalue = 0; // we are independent of actual runtime address; see ckt32 + + upx_compress_config_t cconf; cconf.reset(); + // limit stack size needed for runtime decompression + cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28KB stack + compressWithFilters(&ft, 512, &cconf, getStrategy(ft)); + unsigned const txt_c_len = ph.c_len; + + const unsigned lsize = getLoaderSize(); + + defineDecompressorSymbols(); + defineFilterSymbols(linker, &ft); + if (0x40==(0xf0 & ft.id)) { + linker->defineSymbol("filter_length", ph.u_len); // redefine + } + relocateLoader(); + + MemBuffer loader(lsize); + memcpy(loader, getLoader(), lsize); + patchPackHeader(loader, lsize); + +#define shstrtab local_shstrtab // avoid -Wshadow warning + char const shstrtab[]= "\0.text\0.note\0.shstrtab\0.symtab\0.strtab"; + char const *p = shstrtab; + while (0!=*p++) ; + shdro[1].sh_name = ptr_diff(p, shstrtab); + shdro[1].sh_type = Shdr::SHT_PROGBITS; + shdro[1].sh_flags = Shdr::SHF_ALLOC | Shdr::SHF_EXECINSTR; + shdro[1].sh_offset = fo_off; + shdro[1].sh_size = txt_c_len + lsize; // plus more ... + shdro[1].sh_addralign = 1; // default + + fo_off += write_vmlinux_head(fo, &shdro[1]); + fo->write(obuf, txt_c_len); fo_off += txt_c_len; + unsigned const a = (shdro[1].sh_addralign -1) & -txt_c_len; + if (0!=a) { // align + fo_off += a; + shdro[1].sh_size += a; + fo->seek(a, SEEK_CUR); + } + fo->write(loader, lsize); fo_off += lsize; + +#if 0 + printf("%-13s: compressed : %8u bytes\n", getName(), txt_c_len); + printf("%-13s: decompressor : %8u bytes\n", getName(), lsize); +#endif + verifyOverlappingDecompression(); + + // .note with 1st page -------------------------------- + ph.u_len = phdri[0].p_offset; + fi->seek(0, SEEK_SET); + fi->readx(ibuf, ph.u_len); + compress(ibuf, ph.u_len, obuf, &cconf); + + while (0!=*p++) ; + shdro[2].sh_name = ptr_diff(p, shstrtab); + shdro[2].sh_type = Shdr::SHT_NOTE; + shdro[2].sh_offset = fo_off; + shdro[2].sh_size = sizeof(ph.u_len) + ph.c_len; + shdro[2].sh_addralign = 1; + tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); + fo->write(obuf, ph.c_len); fo_off += shdro[2].sh_size; + + // .note with rest -------------------------------- + ph.u_len = file_size - (sz_ptload + phdri[0].p_offset); + fi->seek(sz_ptload + phdri[0].p_offset, SEEK_SET); + fi->readx(ibuf, ph.u_len); + + // Temporarily decrease ph.level by about (1+ log2(sz_rest / sz_ptload)) + // to avoid spending unreasonable effort compressing large symbol tables + // that are discarded 99.9% of the time anyway. + int const old_level = ph.level; + for (unsigned v = ((ph.u_len>>3) + ph.u_len) / sz_ptload; 0 < v; v>>=1) { + if (0== --ph.level) { + ph.level = 1; + } + } + compress(ibuf, ph.u_len, obuf, &cconf); + ph.level = old_level; + + // while (0!=*p++) ; // name is the same + shdro[3].sh_name = ptr_diff(p, shstrtab); + shdro[3].sh_type = Shdr::SHT_NOTE; + shdro[3].sh_offset = fo_off; + shdro[3].sh_size = sizeof(ph.u_len) + ph.c_len; + shdro[3].sh_addralign = 1; + tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); + fo->write(obuf, ph.c_len); fo_off += shdro[3].sh_size; + + while (0!=*p++) ; + shdro[4].sh_name = ptr_diff(p, shstrtab); + shdro[4].sh_type = Shdr::SHT_STRTAB; + shdro[4].sh_offset = fo_off; + shdro[4].sh_size = sizeof(shstrtab); // already includes terminating '\0' + shdro[4].sh_addralign = 1; + fo->write(shstrtab, shdro[4].sh_size); fo_off += shdro[4].sh_size; + + fo_off = ~3 & (3+ fo_off); fo->seek(fo_off, SEEK_SET); + while (0!=*p++) ; + shdro[5].sh_name = ptr_diff(p, shstrtab); + shdro[5].sh_type = Shdr::SHT_SYMTAB; + shdro[5].sh_offset = fo_off; + shdro[5].sh_size = 5*sizeof(Sym); + //shdro[5].sh_flags = Shdr::SHF_INFO_LINK; + shdro[5].sh_link = 6; // to .strtab for symbols + shdro[5].sh_info = 1+3; // number of non-global symbols [binutils/bfd/elf.c] + shdro[5].sh_addralign = 4; + shdro[5].sh_entsize = sizeof(Sym); + + Sym sec_sym; + + // Symbol 0; no references, but bfd demands it. + memset(&sec_sym, 0, sizeof(sec_sym)); + fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); + + // Each section before .shstrtab needs a symbol. + sec_sym.st_info = sec_sym.make_st_info(Sym::STB_LOCAL, Sym::STT_SECTION); + sec_sym.st_other = Sym::STV_DEFAULT; + sec_sym.st_shndx = 1; // .text + fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); + sec_sym.st_shndx = 2; // .note + fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); + sec_sym.st_shndx = 3; // .note + fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); + + // the symbol we care about + Sym unc_ker; + unc_ker.st_name = 1; // 1 byte into strtab + unc_ker.st_value = 0; + unc_ker.st_size = txt_c_len; + unc_ker.st_info = unc_ker.make_st_info(Sym::STB_GLOBAL, Sym::STT_FUNC); + unc_ker.st_other = Sym::STV_DEFAULT; + unc_ker.st_shndx = 1; // .text + fo->write(&unc_ker, sizeof(unc_ker)); fo_off += sizeof(unc_ker); + + // '\0' before and after the name we want + char const strtab[] = "\0decompress_kernel"; + + while (0!=*p++) ; + shdro[6].sh_name = ptr_diff(p, shstrtab); + shdro[6].sh_type = Shdr::SHT_STRTAB; + shdro[6].sh_offset = fo_off; + shdro[6].sh_size = sizeof(strtab); // includes both '\0' + shdro[6].sh_addralign = 1; + fo->write(strtab, sizeof(strtab)); fo_off += sizeof(strtab); + + fo->seek(0, SEEK_SET); + fo->write(&ehdro, sizeof(ehdro)); + fo->write(&shdro, sizeof(shdro)); + + if (!checkFinalCompressionRatio(fo)) + throwNotCompressible(); +#undef shstrtab +} + template int PackVmlinuxBase::canUnpack() { @@ -213,7 +427,7 @@ int PackVmlinuxBase::canUnpack() return false; if (ehdri.e_type != Ehdr::ET_REL - || ehdri.e_shoff != sizeof(ehdri) // Shdr not contiguous with Ehdr + //i386 fails || ehdri.e_shoff != sizeof(ehdri) // Shdr not contiguous with Ehdr || ehdri.e_shentsize!=sizeof(Shdr) || ehdri.e_shnum < 4 || (unsigned)file_size < (ehdri.e_shnum * sizeof(Shdr) + ehdri.e_shoff) @@ -437,27 +651,6 @@ void PackVmlinuxARM::buildLoader(const Filter *ft) } -static bool defineFilterSymbols(Linker *linker, const Filter *ft) -{ - if (ft->id == 0) { - linker->defineSymbol("filter_length", 0); - return false; - } - assert(ft->calls > 0); - - if (0x50==(0xF0 & ft->id)) { - linker->defineSymbol("FID", ft->id); - linker->defineSymbol("CTO", ft->cto); - } - else { - linker->defineSymbol("filter_cto", ft->cto); - linker->defineSymbol("filter_length", - (ft->id & 0xf) % 3 == 0 ? ft->calls : - ft->lastcall - ft->calls * 4); - } - return true; -} - static const #include "stub/i386-linux.kernel.vmlinux-head.h" static const @@ -465,376 +658,53 @@ static const static const #include "stub/arm-linux.kernel.vmlinux-head.h" -void PackVmlinuxI386::pack(OutputFile *fo) +unsigned PackVmlinuxI386::write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt +) { - unsigned fo_off = 0; - Ehdr ehdro; - U32 tmp_u32; // for external representation - - // NULL - // .text(PT_LOADs) .note(1st page) .note(rest) - // .shstrtab /* .symtab .strtab */ - Shdr shdro[1+3+1/*+2*/]; - memset(shdro, 0, sizeof(shdro)); - - ibuf.alloc(file_size); - obuf.allocForCompression(file_size); - - // .e_ident, .e_machine, .e_version, .e_flags - memcpy(&ehdro, &ehdri, sizeof(ehdro)); - ehdro.e_type = Ehdr::ET_REL; - ehdro.e_entry = 0; - ehdro.e_phoff = 0; - ehdro.e_shoff = 0; // later - ehdro.e_phentsize = 0; - ehdro.e_phnum = 0; - ehdro.e_shnum = 1+3+1/*+2*/; - ehdro.e_shstrndx = 4; - fo->write(&ehdro, sizeof(ehdro)); fo_off+= sizeof(ehdro); - - ph.u_len = sz_ptload; - fi->seek(phdri[0].p_offset, SEEK_SET); - fi->readx(ibuf, ph.u_len); - checkAlreadyPacked(ibuf + ph.u_len - 1024, 1024); - - // prepare filter - ph.filter = 0; - Filter ft(ph.level); - ft.buf_len = ph.u_len; - ft.addvalue = 0; // we are independent of actual runtime address; see ckt32 - - upx_compress_config_t cconf; cconf.reset(); - // limit stack size needed for runtime decompression - cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28KB stack - compressWithFilters(&ft, 512, &cconf, getStrategy(ft)); - - const unsigned lsize = getLoaderSize(); - - defineFilterSymbols(linker, &ft); - if (0x40==(0xf0 & ft.id)) { - linker->defineSymbol("filter_length", ph.u_len); // redefine - } - defineDecompressorSymbols(); - relocateLoader(); - - MemBuffer loader(lsize); - memcpy(loader, getLoader(), lsize); - patchPackHeader(loader, lsize); - -#define shstrtab local_shstrtab // avoid -Wshadow warning - char const shstrtab[]= "\0.text\0.note\0.shstrtab\0.symtab\0.strtab"; - char const *p = shstrtab; - while (0!=*p++) ; - shdro[1].sh_name = ptr_diff(p, shstrtab); - shdro[1].sh_type = Shdr::SHT_PROGBITS; - shdro[1].sh_flags = Shdr::SHF_ALLOC | Shdr::SHF_EXECINSTR; - shdro[1].sh_offset = fo_off; - shdro[1].sh_size = sizeof(stub_i386_linux_kernel_vmlinux_head) + ph.c_len + lsize; - shdro[1].sh_addralign = 1; - // ENTRY_POINT - fo->write(&stub_i386_linux_kernel_vmlinux_head[0], sizeof(stub_i386_linux_kernel_vmlinux_head)-2*(1+ 4) +1); - tmp_u32 = ehdri.e_entry; fo->write(&tmp_u32, 4); + fo->write(&stub_i386_linux_kernel_vmlinux_head[0], + sizeof(stub_i386_linux_kernel_vmlinux_head)-2*(1+ 4) +1); + U32 tmp_u32 = ehdri.e_entry; fo->write(&tmp_u32, 4); // COMPRESSED_LENGTH - fo->write(&stub_i386_linux_kernel_vmlinux_head[sizeof(stub_i386_linux_kernel_vmlinux_head)-(1+ 4)], 1); + fo->write(&stub_i386_linux_kernel_vmlinux_head[ + sizeof(stub_i386_linux_kernel_vmlinux_head)-(1+ 4)], 1); tmp_u32 = ph.c_len; fo->write(&tmp_u32, 4); - fo_off += sizeof(stub_i386_linux_kernel_vmlinux_head); + stxt->sh_size += sizeof(stub_i386_linux_kernel_vmlinux_head); - fo->write(obuf, ph.c_len); fo_off += ph.c_len; - fo->write(loader, lsize); fo_off += lsize; - -#if 0 - printf("%-13s: compressed : %8u bytes\n", getName(), ph.c_len); - printf("%-13s: decompressor : %8u bytes\n", getName(), lsize); -#endif - verifyOverlappingDecompression(); - - // .note with 1st page -------------------------------- - ph.u_len = phdri[0].p_offset; - fi->seek(0, SEEK_SET); - fi->readx(ibuf, ph.u_len); - compress(ibuf, ph.u_len, obuf); - - while (0!=*p++) ; - shdro[2].sh_name = ptr_diff(p, shstrtab); - shdro[2].sh_type = Shdr::SHT_NOTE; - shdro[2].sh_offset = fo_off; - shdro[2].sh_size = sizeof(ph.u_len) + ph.c_len; - shdro[2].sh_addralign = 1; - tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); - fo->write(obuf, ph.c_len); fo_off += shdro[2].sh_size; - - // .note with rest -------------------------------- - ph.u_len = file_size - (sz_ptload + phdri[0].p_offset); - fi->seek(sz_ptload + phdri[0].p_offset, SEEK_SET); - fi->readx(ibuf, ph.u_len); - - // Temporarily decrease ph.level by about (1+ log2(sz_rest / sz_ptload)) - // to avoid spending unreasonable effort compressing large symbol tables - // that are discarded 99.9% of the time anyway. - int const old_level = ph.level; - for (unsigned v = ((ph.u_len>>3) + ph.u_len) / sz_ptload; 0 < v; v>>=1) { - if (0== --ph.level) { - ph.level = 1; - } - } - compress(ibuf, ph.u_len, obuf, &cconf); - ph.level = old_level; - - // while (0!=*p++) ; // name is the same - shdro[3].sh_name = ptr_diff(p, shstrtab); - shdro[3].sh_type = Shdr::SHT_NOTE; - shdro[3].sh_offset = fo_off; - shdro[3].sh_size = sizeof(ph.u_len) + ph.c_len; - shdro[3].sh_addralign = 1; - tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); - fo->write(obuf, ph.c_len); fo_off += shdro[3].sh_size; - - while (0!=*p++) ; - shdro[4].sh_name = ptr_diff(p, shstrtab); - shdro[4].sh_type = Shdr::SHT_STRTAB; - shdro[4].sh_offset = fo_off; - shdro[4].sh_size = sizeof(shstrtab); // already includes terminating '\0' - shdro[4].sh_addralign = 1; - fo->write(shstrtab, shdro[4].sh_size); fo_off += shdro[4].sh_size; - -#if 0 /*{ no symbols! */ - while (0!=*p++) ; - fo_off = ~3 & (3+ fo_off); - shdro[5].sh_name = ptr_diff(p, shstrtab); - shdro[5].sh_type = Shdr::SHT_SYMTAB; - shdro[5].sh_offset = fo_off; - shdro[5].sh_size = 16; // XXX ? - shdro[5].sh_link = 6; // to .strtab for symbols - shdro[5].sh_addralign = 4; - shdro[5].sh_entsize = 16; // XXX Sym - fo->seek(fo_off, SEEK_SET); - fo->write("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); fo_off += 16; - - while (0!=*p++) ; - shdro[6].sh_name = ptr_diff(p, shstrtab); - shdro[6].sh_type = Shdr::SHT_STRTAB; - shdro[6].sh_offset = fo_off; - shdro[6].sh_size = 1; // XXX ? - shdro[6].sh_addralign = 1; - fo->write("", 1); fo_off += 1; -#endif /*}*/ - - fo_off = ~3 & (3+ fo_off); - fo->seek(fo_off, SEEK_SET); - ehdro.e_shoff = fo_off; - fo->write(shdro, sizeof(shdro)); - - fo->seek(0, SEEK_SET); - fo->write(&ehdro, sizeof(ehdro)); - - if (!checkFinalCompressionRatio(fo)) - throwNotCompressible(); -#undef shstrtab + return sizeof(stub_i386_linux_kernel_vmlinux_head); } -void PackVmlinuxARM::pack(OutputFile *fo) +void PackVmlinuxARM::defineDecompressorSymbols() { - unsigned fo_off = 0; - Ehdr ehdro; - U32 tmp_u32; - - // NULL - // .text(PT_LOADs) .note(1st page) .note(rest) - // .shstrtab .symtab .strtab - Shdr shdro[1+3+3]; - memset(shdro, 0, sizeof(shdro)); - - ibuf.alloc(file_size); - obuf.allocForCompression(file_size); - - // .e_ident, .e_machine, .e_version, .e_flags - memcpy(&ehdro, &ehdri, sizeof(ehdro)); - ehdro.e_type = Ehdr::ET_REL; - ehdro.e_entry = 0; - ehdro.e_phoff = 0; - ehdro.e_shoff = sizeof(ehdro); // later - ehdro.e_phentsize = 0; - ehdro.e_phnum = 0; - ehdro.e_shnum = 1+3+3; - ehdro.e_shstrndx = 4; - fo->write(&ehdro, sizeof(ehdro)); fo_off+= sizeof(ehdro); - fo->write(shdro, sizeof(shdro)); fo_off+= sizeof(shdro); - - ph.u_len = sz_ptload; - fi->seek(phdri[0].p_offset, SEEK_SET); - fi->readx(ibuf, ph.u_len); - checkAlreadyPacked(ibuf + ph.u_len - 1024, 1024); - - // prepare filter - ph.filter = 0; - Filter ft(ph.level); - ft.buf_len = ph.u_len; - ft.addvalue = 0; // we are independent of actual runtime address; see ckt32 - - upx_compress_config_t cconf; cconf.reset(); - // limit stack size needed for runtime decompression - cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28KB stack - compressWithFilters(&ft, 512, &cconf, getStrategy(ft)); - unsigned const txt_c_len = ph.c_len; - unsigned const txt_u_len = ph.u_len; - - const unsigned lsize = getLoaderSize(); - - linker->defineSymbol( "COMPRESSED_LENGTH", txt_c_len); - linker->defineSymbol("UNCOMPRESSED_LENGTH", txt_u_len); + linker->defineSymbol( "COMPRESSED_LENGTH", ph.c_len); + linker->defineSymbol("UNCOMPRESSED_LENGTH", ph.u_len); linker->defineSymbol("METHOD", ph.method); +} - defineFilterSymbols(linker, &ft); - if (0x40==(0xf0 & ft.id)) { - linker->defineSymbol("filter_length", ph.u_len); // redefine - } - defineDecompressorSymbols(); - relocateLoader(); - - MemBuffer loader(lsize); - memcpy(loader, getLoader(), lsize); - patchPackHeader(loader, lsize); - -#define shstrtab local_shstrtab // avoid -Wshadow warning - char const shstrtab[]= "\0.text\0.note\0.shstrtab\0.symtab\0.strtab"; - char const *p = shstrtab; - while (0!=*p++) ; - shdro[1].sh_name = ptr_diff(p, shstrtab); - shdro[1].sh_type = Shdr::SHT_PROGBITS; - shdro[1].sh_flags = Shdr::SHF_ALLOC | Shdr::SHF_EXECINSTR; - shdro[1].sh_offset = fo_off; - shdro[1].sh_size = sizeof(stub_arm_linux_kernel_vmlinux_head) + - txt_c_len + (3& -txt_c_len) + lsize; - shdro[1].sh_addralign = 1; - +unsigned PackVmlinuxARM::write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt +) +{ // First word from vmlinux-head.S fo->write(&stub_arm_linux_kernel_vmlinux_head[0], 4); // Second word - tmp_u32 = (0xff000000 & BeLePolicy::get32(&stub_arm_linux_kernel_vmlinux_head[4])) - | (0x00ffffff & (-1+ ((3+ txt_c_len)>>2))); + U32 tmp_u32; + unsigned const t = (0xff000000 & + BeLePolicy::get32(&stub_arm_linux_kernel_vmlinux_head[4])) + | (0x00ffffff & (-1+ ((3+ ph.c_len)>>2))); + tmp_u32 = t; fo->write(&tmp_u32, 4); - fo_off += sizeof(stub_arm_linux_kernel_vmlinux_head); + stxt->sh_addralign = 4; + stxt->sh_size += sizeof(stub_arm_linux_kernel_vmlinux_head); - fo->write(obuf, txt_c_len); fo_off += txt_c_len; - tmp_u32 = 0; fo->write(&tmp_u32, 3& -txt_c_len); fo_off += 3& -txt_c_len; - fo->write(loader, lsize); fo_off += lsize; - -#if 0 - printf("%-13s: compressed : %8u bytes\n", getName(), txt_c_len); - printf("%-13s: decompressor : %8u bytes\n", getName(), lsize); -#endif - verifyOverlappingDecompression(); - - // .note with 1st page -------------------------------- - ph.u_len = phdri[0].p_offset; - fi->seek(0, SEEK_SET); - fi->readx(ibuf, ph.u_len); - compress(ibuf, ph.u_len, obuf); - - while (0!=*p++) ; - shdro[2].sh_name = ptr_diff(p, shstrtab); - shdro[2].sh_type = Shdr::SHT_NOTE; - shdro[2].sh_offset = fo_off; - shdro[2].sh_size = sizeof(ph.u_len) + ph.c_len; - shdro[2].sh_addralign = 1; - tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); - fo->write(obuf, ph.c_len); fo_off += shdro[2].sh_size; - - // .note with rest -------------------------------- - ph.u_len = file_size - (sz_ptload + phdri[0].p_offset); - fi->seek(sz_ptload + phdri[0].p_offset, SEEK_SET); - fi->readx(ibuf, ph.u_len); - - // Temporarily decrease ph.level by about (1+ log2(sz_rest / sz_ptload)) - // to avoid spending unreasonable effort compressing large symbol tables - // that are discarded 99.9% of the time anyway. - int const old_level = ph.level; - for (unsigned v = ((ph.u_len>>3) + ph.u_len) / sz_ptload; 0 < v; v>>=1) { - if (0== --ph.level) { - ph.level = 1; - } - } - compress(ibuf, ph.u_len, obuf, &cconf); - ph.level = old_level; - - // while (0!=*p++) ; // name is the same - shdro[3].sh_name = ptr_diff(p, shstrtab); - shdro[3].sh_type = Shdr::SHT_NOTE; - shdro[3].sh_offset = fo_off; - shdro[3].sh_size = sizeof(ph.u_len) + ph.c_len; - shdro[3].sh_addralign = 1; - tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); - fo->write(obuf, ph.c_len); fo_off += shdro[3].sh_size; - - while (0!=*p++) ; - shdro[4].sh_name = ptr_diff(p, shstrtab); - shdro[4].sh_type = Shdr::SHT_STRTAB; - shdro[4].sh_offset = fo_off; - shdro[4].sh_size = sizeof(shstrtab); // already includes terminating '\0' - shdro[4].sh_addralign = 1; - fo->write(shstrtab, shdro[4].sh_size); fo_off += shdro[4].sh_size; - - fo_off = ~3 & (3+ fo_off); fo->seek(fo_off, SEEK_SET); - while (0!=*p++) ; - shdro[5].sh_name = ptr_diff(p, shstrtab); - shdro[5].sh_type = Shdr::SHT_SYMTAB; - shdro[5].sh_offset = fo_off; - shdro[5].sh_size = 5*sizeof(Sym); - //shdro[5].sh_flags = Shdr::SHF_INFO_LINK; - shdro[5].sh_link = 6; // to .strtab for symbols - shdro[5].sh_info = 1+3; // number of non-global symbols [binutils/bfd/elf.c] - shdro[5].sh_addralign = 4; - shdro[5].sh_entsize = sizeof(Sym); - - Sym sec_sym; - - // Symbol 0; no references, but bfd demands it. - memset(&sec_sym, 0, sizeof(sec_sym)); - fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); - - // Each section before .shstrtab needs a symbol. - sec_sym.st_info = sec_sym.get_st_info(Sym::STB_LOCAL, Sym::STT_SECTION); - sec_sym.st_other = Sym::STV_DEFAULT; - sec_sym.st_shndx = 1; // .text - fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); - sec_sym.st_shndx = 2; // .note - fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); - sec_sym.st_shndx = 3; // .note - fo->write(&sec_sym, sizeof(sec_sym)); fo_off += sizeof(sec_sym); - - // the symbol we care about - Sym unc_ker; - unc_ker.st_name = 1; // 1 byte into strtab - unc_ker.st_value = 0; - unc_ker.st_size = txt_c_len; - unc_ker.st_info = unc_ker.get_st_info(Sym::STB_GLOBAL, Sym::STT_FUNC); - unc_ker.st_other = Sym::STV_DEFAULT; - unc_ker.st_shndx = 1; // .text - fo->write(&unc_ker, sizeof(unc_ker)); fo_off += sizeof(unc_ker); - - // '\0' before and after the name we want - char const strtab[] = "\0decompress_kernel"; - - while (0!=*p++) ; - shdro[6].sh_name = ptr_diff(p, shstrtab); - shdro[6].sh_type = Shdr::SHT_STRTAB; - shdro[6].sh_offset = fo_off; - shdro[6].sh_size = sizeof(strtab); // includes both '\0' - shdro[6].sh_addralign = 1; - fo->write(strtab, sizeof(strtab)); fo_off += sizeof(strtab); - - fo->seek(0, SEEK_SET); - fo->write(&ehdro, sizeof(ehdro)); - fo->write(&shdro, sizeof(shdro)); - - if (!checkFinalCompressionRatio(fo)) - throwNotCompressible(); -#undef shstrtab + return sizeof(stub_arm_linux_kernel_vmlinux_head); } @@ -1136,172 +1006,26 @@ void PackVmlinuxAMD64::buildLoader(const Filter *ft) } -void PackVmlinuxAMD64::pack(OutputFile *fo) +unsigned PackVmlinuxAMD64::write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt +) { - unsigned fo_off = 0; - Ehdr ehdro; - U32 tmp_u32; // for external representation - - // NULL - // .text(PT_LOADs) .note(1st page) .note(rest) - // .shstrtab /* .symtab .strtab */ - Shdr shdro[1+3+1/*+2*/]; - memset(shdro, 0, sizeof(shdro)); - char const shstrtab[]= "\0.text\0.note\0.shstrtab\0.symtab\0.strtab"; - char const *p = shstrtab; - - ibuf.alloc(file_size); - obuf.allocForCompression(file_size); - - // .e_ident, .e_machine, .e_version, .e_flags - memcpy(&ehdro, &ehdri, sizeof(ehdro)); - ehdro.e_type = Ehdr::ET_REL; - ehdro.e_entry = 0; - ehdro.e_phoff = 0; - ehdro.e_shoff = 0; // later - ehdro.e_phentsize = 0; - ehdro.e_phnum = 0; - ehdro.e_shnum = 1+3+1/*+2*/; - ehdro.e_shstrndx = 4; - fo->write(&ehdro, sizeof(ehdro)); fo_off+= sizeof(ehdro); - - ph.u_len = sz_ptload; - fi->seek(phdri[0].p_offset, SEEK_SET); - fi->readx(ibuf, ph.u_len); - checkAlreadyPacked(ibuf + ph.u_len - 1024, 1024); - - // prepare filter - ph.filter = 0; - Filter ft(ph.level); - ft.buf_len = ph.u_len; - ft.addvalue = 0; // we are independent of actual runtime address; see ckt32 - - upx_compress_config_t cconf; cconf.reset(); - // limit stack size needed for runtime decompression - cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28KB stack - compressWithFilters(&ft, 512, &cconf, getStrategy(ft)); - - const unsigned lsize = getLoaderSize(); - - defineFilterSymbols(linker, &ft); - if (0x40==(0xf0 & ft.id)) { - linker->defineSymbol("filter_length", ph.u_len); // redefine - } - defineDecompressorSymbols(); - relocateLoader(); - - MemBuffer loader(lsize); - memcpy(loader, getLoader(), lsize); - patchPackHeader(loader, lsize); - - while (0!=*p++) ; - shdro[1].sh_name = ptr_diff(p, shstrtab); - shdro[1].sh_type = Shdr::SHT_PROGBITS; - shdro[1].sh_flags = Shdr::SHF_ALLOC | Shdr::SHF_EXECINSTR; - shdro[1].sh_offset = fo_off; - shdro[1].sh_size = sizeof(stub_amd64_linux_kernel_vmlinux_head) + ph.c_len + lsize; - shdro[1].sh_addralign = 1; - // ENTRY_POINT - fo->write(&stub_amd64_linux_kernel_vmlinux_head[0], sizeof(stub_amd64_linux_kernel_vmlinux_head)-2*(1+ 4) +1); - tmp_u32 = ehdri.e_entry; fo->write(&tmp_u32, 4); + fo->write(&stub_amd64_linux_kernel_vmlinux_head[0], + sizeof(stub_amd64_linux_kernel_vmlinux_head)-2*(1+ 4) +1); + unsigned const t = BeLePolicy::get32(&ehdri.e_entry); + U32 tmp_u32; tmp_u32 = t; + fo->write(&tmp_u32, 4); // COMPRESSED_LENGTH - fo->write(&stub_amd64_linux_kernel_vmlinux_head[sizeof(stub_amd64_linux_kernel_vmlinux_head)-(1+ 4)], 1); + fo->write(&stub_amd64_linux_kernel_vmlinux_head[ + sizeof(stub_amd64_linux_kernel_vmlinux_head)-(1+ 4)], 1); tmp_u32 = ph.c_len; fo->write(&tmp_u32, 4); - fo_off += sizeof(stub_amd64_linux_kernel_vmlinux_head); + stxt->sh_size += sizeof(stub_amd64_linux_kernel_vmlinux_head); - fo->write(obuf, ph.c_len); fo_off += ph.c_len; - fo->write(loader, lsize); fo_off += lsize; - -#if 0 - printf("%-13s: compressed : %8u bytes\n", getName(), ph.c_len); - printf("%-13s: decompressor : %8u bytes\n", getName(), lsize); -#endif - verifyOverlappingDecompression(); - - // .note with 1st page -------------------------------- - ph.u_len = phdri[0].p_offset; - fi->seek(0, SEEK_SET); - fi->readx(ibuf, ph.u_len); - compress(ibuf, ph.u_len, obuf, &cconf); - - while (0!=*p++) ; - shdro[2].sh_name = ptr_diff(p, shstrtab); - shdro[2].sh_type = Shdr::SHT_NOTE; - shdro[2].sh_offset = fo_off; - shdro[2].sh_size = sizeof(ph.u_len) + ph.c_len; - shdro[2].sh_addralign = 1; - tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); - fo->write(obuf, ph.c_len); fo_off += shdro[2].sh_size; - - // .note with rest -------------------------------- - ph.u_len = file_size - (sz_ptload + phdri[0].p_offset); - fi->seek(sz_ptload + phdri[0].p_offset, SEEK_SET); - fi->readx(ibuf, ph.u_len); - - // Temporarily decrease ph.level by about (1+ log2(sz_rest / sz_ptload)) - // to avoid spending unreasonable effort compressing large symbol tables - // that are discarded 99.9% of the time anyway. - int const old_level = ph.level; - for (unsigned v = ((ph.u_len>>3) + ph.u_len) / sz_ptload; 0 < v; v>>=1) { - if (0== --ph.level) { - ph.level = 1; - } - } - compress(ibuf, ph.u_len, obuf, &cconf); - ph.level = old_level; - - // while (0!=*p++) ; // name is the same - shdro[3].sh_name = ptr_diff(p, shstrtab); - shdro[3].sh_type = Shdr::SHT_NOTE; - shdro[3].sh_offset = fo_off; - shdro[3].sh_size = sizeof(ph.u_len) + ph.c_len; - shdro[3].sh_addralign = 1; - tmp_u32 = ph.u_len; fo->write(&tmp_u32, 4); - fo->write(obuf, ph.c_len); fo_off += shdro[3].sh_size; - - while (0!=*p++) ; - shdro[4].sh_name = ptr_diff(p, shstrtab); - shdro[4].sh_type = Shdr::SHT_STRTAB; - shdro[4].sh_offset = fo_off; - shdro[4].sh_size = sizeof(shstrtab); // already includes terminating '\0' - shdro[4].sh_addralign = 1; - fo->write(shstrtab, shdro[4].sh_size); fo_off += shdro[4].sh_size; - -#if 0 /*{ no symbols! */ - while (0!=*p++) ; - fo_off = ~3 & (3+ fo_off); - shdro[5].sh_name = ptr_diff(p, shstrtab); - shdro[5].sh_type = Shdr::SHT_SYMTAB; - shdro[5].sh_offset = fo_off; - shdro[5].sh_size = 16; // XXX ? - shdro[5].sh_link = 6; // to .strtab for symbols - shdro[5].sh_addralign = 4; - shdro[5].sh_entsize = 16; // XXX Sym - fo->seek(fo_off, SEEK_SET); - fo->write("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); fo_off += 16; - - while (0!=*p++) ; - shdro[6].sh_name = ptr_diff(p, shstrtab); - shdro[6].sh_type = Shdr::SHT_STRTAB; - shdro[6].sh_offset = fo_off; - shdro[6].sh_size = 1; // XXX ? - shdro[6].sh_addralign = 1; - fo->write("", 1); fo_off += 1; -#endif /*}*/ - - fo_off = ~3 & (3+ fo_off); - fo->seek(fo_off, SEEK_SET); - ehdro.e_shoff = fo_off; - fo->write(shdro, sizeof(shdro)); - - fo->seek(0, SEEK_SET); - fo->write(&ehdro, sizeof(ehdro)); - - if (!checkFinalCompressionRatio(fo)) - throwNotCompressible(); + return sizeof(stub_amd64_linux_kernel_vmlinux_head); } diff --git a/src/p_vmlinx.h b/src/p_vmlinx.h index f531d6bf..9ddacd1f 100644 --- a/src/p_vmlinx.h +++ b/src/p_vmlinx.h @@ -81,8 +81,13 @@ protected: virtual bool is_valid_e_entry(Addr) = 0; virtual bool has_valid_vmlinux_head() = 0; virtual bool canPack(); + virtual void pack(OutputFile *fo); virtual int canUnpack(); // bool, except -1: format known, but not packed virtual void unpack(OutputFile *fo); + virtual unsigned write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt + ) = 0; static int __acc_cdecl_qsort compare_Phdr(void const *aa, void const *bb); }; @@ -99,13 +104,15 @@ public: virtual const int *getCompressionMethods(int method, int level) const; virtual const int *getFilters() const; - virtual void pack(OutputFile *fo); - protected: virtual void buildLoader(const Filter *ft); virtual Linker* newLinker() const; virtual bool is_valid_e_entry(Addr); virtual bool has_valid_vmlinux_head(); + virtual unsigned write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt + ); }; @@ -121,13 +128,16 @@ public: virtual const int *getCompressionMethods(int method, int level) const; virtual const int *getFilters() const; - virtual void pack(OutputFile *fo); - protected: virtual void buildLoader(const Filter *ft); virtual Linker* newLinker() const; virtual bool is_valid_e_entry(Addr); virtual bool has_valid_vmlinux_head(); + virtual void defineDecompressorSymbols(); + virtual unsigned write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt + ); }; @@ -143,13 +153,15 @@ public: virtual const int *getCompressionMethods(int method, int level) const; virtual const int *getFilters() const; - virtual void pack(OutputFile *fo); - protected: virtual void buildLoader(const Filter *ft); virtual Linker* newLinker() const; virtual bool is_valid_e_entry(Addr); virtual bool has_valid_vmlinux_head(); + virtual unsigned write_vmlinux_head( + OutputFile *const fo, + Shdr *const stxt + ); };