1723 lines
53 KiB
C++
1723 lines
53 KiB
C++
/* p_vmlinx.cpp -- pack vmlinux ET_EXEC file (before bootsect or setup)
|
|
|
|
This file is part of the UPX executable compressor.
|
|
|
|
Copyright (C) 2004-2006 John Reiser
|
|
Copyright (C) 1996-2006 Markus Franz Xaver Johannes Oberhumer
|
|
Copyright (C) 1996-2006 Laszlo Molnar
|
|
All Rights Reserved.
|
|
|
|
UPX and the UCL library are free software; you can redistribute them
|
|
and/or modify them under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING.
|
|
If not, write to the Free Software Foundation, Inc.,
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
Markus F.X.J. Oberhumer Laszlo Molnar
|
|
markus@oberhumer.com ml1050@users.sourceforge.net
|
|
|
|
John Reiser
|
|
jreiser@users.sourceforge.net
|
|
*/
|
|
|
|
|
|
#include "conf.h"
|
|
|
|
#include "file.h"
|
|
#include "filter.h"
|
|
#include "packer.h"
|
|
#include "p_vmlinx.h"
|
|
#include "linker.h"
|
|
|
|
static const
|
|
#include "stub/i386-linux.kernel.vmlinux.h"
|
|
static const
|
|
#include "stub/amd64-linux.kernel.vmlinux.h"
|
|
static const
|
|
#include "stub/arm-linux.kernel.vmlinux.h"
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
PackVmlinuxI386::PackVmlinuxI386(InputFile *f) :
|
|
super(f), n_ptload(0), phdri(NULL), shdri(NULL), shstrtab(NULL)
|
|
{
|
|
bele = &N_BELE_RTP::le_policy;
|
|
}
|
|
|
|
PackVmlinuxI386::~PackVmlinuxI386()
|
|
{
|
|
delete [] shstrtab;
|
|
delete [] phdri;
|
|
delete [] shdri;
|
|
}
|
|
|
|
const int *PackVmlinuxI386::getCompressionMethods(int method, int level) const
|
|
{
|
|
return Packer::getDefaultCompressionMethods_le32(method, level);
|
|
}
|
|
|
|
|
|
const int *PackVmlinuxI386::getFilters() const
|
|
{
|
|
static const int filters[] = {
|
|
0x49, 0x46,
|
|
FT_END };
|
|
return filters;
|
|
}
|
|
|
|
// copied from PackUnix 2006-10-13.
|
|
int PackVmlinuxI386::getStrategy(Filter &/*ft*/)
|
|
{
|
|
// Called just before reading and compressing each block.
|
|
// Might want to adjust blocksize, etc.
|
|
|
|
// If user specified the filter, then use it (-2==filter_strategy).
|
|
// Else try the first two filters, and pick the better (2==filter_strategy).
|
|
return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
|
|
}
|
|
|
|
|
|
Elf_LE32_Shdr const *
|
|
PackVmlinuxI386::getElfSections()
|
|
{
|
|
Elf_LE32_Shdr const *p, *shstrsec=0;
|
|
shdri = new Elf_LE32_Shdr[(unsigned) ehdri.e_shnum];
|
|
fi->seek(ehdri.e_shoff, SEEK_SET);
|
|
fi->readx(shdri, ehdri.e_shnum * sizeof(*shdri));
|
|
int j;
|
|
for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if (Elf32_Shdr::SHT_STRTAB==p->sh_type
|
|
&& (p->sh_size + p->sh_offset) <= (unsigned) file_size
|
|
&& (10+ p->sh_name) <= p->sh_size // 1+ strlen(".shstrtab")
|
|
) {
|
|
delete [] shstrtab;
|
|
shstrtab = new char[1+ p->sh_size];
|
|
fi->seek(p->sh_offset, SEEK_SET);
|
|
fi->readx(shstrtab, p->sh_size);
|
|
shstrtab[p->sh_size] = '\0';
|
|
if (0==strcmp(".shstrtab", shstrtab + p->sh_name)) {
|
|
shstrsec = p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return shstrsec;
|
|
}
|
|
|
|
PackVmlinuxARM::PackVmlinuxARM(InputFile *f) :
|
|
super(f), n_ptload(0), phdri(NULL), shdri(NULL), shstrtab(NULL)
|
|
{
|
|
bele = &N_BELE_RTP::le_policy;
|
|
}
|
|
|
|
PackVmlinuxARM::~PackVmlinuxARM()
|
|
{
|
|
delete [] shstrtab;
|
|
delete [] phdri;
|
|
delete [] shdri;
|
|
}
|
|
|
|
const int *PackVmlinuxARM::getCompressionMethods(int method, int level) const
|
|
{
|
|
return Packer::getDefaultCompressionMethods_8(method, level);
|
|
}
|
|
|
|
|
|
const int *PackVmlinuxARM::getFilters() const
|
|
{
|
|
static const int f50[] = { 0x50, FT_END };
|
|
return f50;
|
|
}
|
|
|
|
// copied from PackUnix 2006-10-13.
|
|
int PackVmlinuxARM::getStrategy(Filter &/*ft*/)
|
|
{
|
|
// Called just before reading and compressing each block.
|
|
// Might want to adjust blocksize, etc.
|
|
|
|
// If user specified the filter, then use it (-2==filter_strategy).
|
|
// Else try the first two filters, and pick the better (2==filter_strategy).
|
|
return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
|
|
}
|
|
|
|
|
|
Elf_LE32_Shdr const *
|
|
PackVmlinuxARM::getElfSections()
|
|
{
|
|
Elf_LE32_Shdr const *p, *shstrsec=0;
|
|
shdri = new Elf_LE32_Shdr[(unsigned) ehdri.e_shnum];
|
|
fi->seek(ehdri.e_shoff, SEEK_SET);
|
|
fi->readx(shdri, ehdri.e_shnum * sizeof(*shdri));
|
|
int j;
|
|
for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if (Elf32_Shdr::SHT_STRTAB==p->sh_type
|
|
&& (p->sh_size + p->sh_offset) <= (unsigned) file_size
|
|
&& (10+ p->sh_name) <= p->sh_size // 1+ strlen(".shstrtab")
|
|
) {
|
|
delete [] shstrtab;
|
|
shstrtab = new char[1+ p->sh_size];
|
|
fi->seek(p->sh_offset, SEEK_SET);
|
|
fi->readx(shstrtab, p->sh_size);
|
|
shstrtab[p->sh_size] = '\0';
|
|
if (0==strcmp(".shstrtab", shstrtab + p->sh_name)) {
|
|
shstrsec = p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return shstrsec;
|
|
}
|
|
|
|
static int __acc_cdecl_qsort
|
|
compare_Phdr(void const *aa, void const *bb)
|
|
{
|
|
Elf32_Phdr const *const a = (Elf32_Phdr const *)aa;
|
|
Elf32_Phdr const *const b = (Elf32_Phdr const *)bb;
|
|
unsigned const xa = a->p_type - Elf32_Phdr::PT_LOAD;
|
|
unsigned const xb = b->p_type - Elf32_Phdr::PT_LOAD;
|
|
if (xa < xb) return -1; // PT_LOAD first
|
|
if (xa > xb) return 1;
|
|
if (a->p_paddr < b->p_paddr) return -1; // ascending by .p_paddr
|
|
if (a->p_paddr > b->p_paddr) return 1;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Examples as of 2004-07-16 [readelf --segments vmlinux # before fiddling]:
|
|
//
|
|
//----- kernel-2.6.7 plain [defconfig?]
|
|
//Program Headers(2):
|
|
// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
|
|
// LOAD 0x001000 0x00100000 0x00100000 0x1c7e61 0x1c7e61 R E 0x1000
|
|
// LOAD 0x1c8e64 0x002c8e64 0x002c8e64 0x00000 0x00000 RW 0x1000
|
|
//
|
|
//----- kernel-2.6.7-1.488 Fedora Core 3 test 1
|
|
//Program Headers(5):
|
|
// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
|
|
// LOAD 0x001000 0x02100000 0x02100000 0x202246 0x202246 R E 0x1000
|
|
// LOAD 0x204000 0xffff3000 0x02303000 0x00664 0x00664 R E 0x1000
|
|
// LOAD 0x205000 0x02304000 0x02304000 0x43562 0x43562 R 0x1000
|
|
// LOAD 0x249000 0x02348000 0x02348000 0x81800 0xcb0fc RWE 0x1000
|
|
// STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
|
|
//
|
|
//----- kernel-2.6.18-1.2778 Fedora Core 6 test 3
|
|
//Program Headers(3)
|
|
// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
|
|
// LOAD 0x001000 0xc0400000 0x00400000 0x279820 0x279820 R E 0x1000
|
|
// LOAD 0x27b000 0xc067a000 0x0067a000 0x10ee64 0x1b07e8 RWE 0x1000
|
|
// NOTE 0x000000 0x00000000 0x00000000 0x00000 0x00000 R 0x4
|
|
|
|
bool PackVmlinuxI386::canPack()
|
|
{
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
|
|
// now check the ELF header
|
|
if (memcmp(&ehdri, "\x7f\x45\x4c\x46\x01\x01\x01", 7) // ELF 32-bit LSB
|
|
|| !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded
|
|
|| ehdri.e_version != 1 // version
|
|
|| ehdri.e_ehsize != sizeof(ehdri) // different <elf.h> ?
|
|
|| ehdri.e_phoff != sizeof(ehdri) // Phdrs not contiguous with Ehdr
|
|
|| ehdri.e_phentsize!=sizeof(Elf32_Phdr)
|
|
)
|
|
return false;
|
|
|
|
// additional requirements for vmlinux
|
|
if (ehdri.e_machine != Elf32_Ehdr::EM_386
|
|
|| ehdri.e_type!=Elf32_Ehdr::ET_EXEC
|
|
|| 0!=(0x000fffff & ehdri.e_entry) // entry not on whole 1MB
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// A Linux kernel must have a __ksymtab section. [??]
|
|
Elf_LE32_Shdr const *p, *const shstrsec = getElfSections();
|
|
if (0==shstrsec) {
|
|
return false;
|
|
}
|
|
{
|
|
int j;
|
|
for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if (Elf32_Shdr::SHT_PROGBITS==p->sh_type
|
|
&& 0==strcmp("__ksymtab", p->sh_name + shstrtab)) {
|
|
break;
|
|
}
|
|
}
|
|
if (j < 0)
|
|
return false;
|
|
}
|
|
|
|
phdri = new Elf_LE32_Phdr[(unsigned) ehdri.e_phnum];
|
|
fi->seek(ehdri.e_phoff, SEEK_SET);
|
|
fi->readx(phdri, ehdri.e_phnum * sizeof(*phdri));
|
|
|
|
// Put PT_LOAD together at the beginning, ascending by .p_paddr.
|
|
qsort(phdri, ehdri.e_phnum, sizeof(*phdri), compare_Phdr);
|
|
|
|
// Check that PT_LOADs form one contiguous chunk of the file.
|
|
for (unsigned j = 0; j < ehdri.e_phnum; ++j) {
|
|
if (Elf32_Phdr::PT_LOAD==phdri[j].p_type) {
|
|
if (0xfff & (phdri[j].p_offset | phdri[j].p_paddr
|
|
| phdri[j].p_align | phdri[j].p_vaddr) ) {
|
|
return false;
|
|
}
|
|
if (0 < j) {
|
|
unsigned const sz = (0u - phdri[j-1].p_align)
|
|
& (phdri[j-1].p_align -1 + phdri[j-1].p_filesz);
|
|
if ((sz + phdri[j-1].p_offset)!=phdri[j].p_offset) {
|
|
return false;
|
|
}
|
|
}
|
|
++n_ptload;
|
|
sz_ptload = phdri[j].p_filesz + phdri[j].p_offset - phdri[0].p_offset;
|
|
}
|
|
}
|
|
return 0 < n_ptload;
|
|
}
|
|
|
|
|
|
Linker* PackVmlinuxI386::newLinker() const
|
|
{
|
|
return new ElfLinkerX86;
|
|
}
|
|
|
|
|
|
void PackVmlinuxI386::buildLoader(const Filter *ft)
|
|
{
|
|
// prepare loader
|
|
initLoader(stub_i386_linux_kernel_vmlinux, sizeof(stub_i386_linux_kernel_vmlinux));
|
|
addLoader("LINUX000",
|
|
(0x40==(0xf0 & ft->id)) ? "LXCKLLT1" : (ft->id ? "LXCALLT1" : ""),
|
|
"LXMOVEUP",
|
|
getDecompressorSections(),
|
|
NULL
|
|
);
|
|
if (ft->id) {
|
|
assert(ft->calls > 0);
|
|
if (0x40==(0xf0 & ft->id)) {
|
|
addLoader("LXCKLLT9", NULL);
|
|
}
|
|
else {
|
|
addLoader("LXCALLT9", NULL);
|
|
}
|
|
addFilter32(ft->id);
|
|
}
|
|
addLoader("LINUX990",
|
|
ph.first_offset_found == 1 ? "LINUX991" : "",
|
|
"LINUX992,IDENTSTR,UPX1HEAD", NULL);
|
|
}
|
|
|
|
bool PackVmlinuxARM::canPack()
|
|
{
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
|
|
// now check the ELF header
|
|
if (memcmp(&ehdri, "\x7f\x45\x4c\x46\x01\x01\x01", 7) // ELF 32-bit LSB
|
|
|| !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded
|
|
|| ehdri.e_version != 1 // version
|
|
|| ehdri.e_ehsize != sizeof(ehdri) // different <elf.h> ?
|
|
|| ehdri.e_phoff != sizeof(ehdri) // Phdrs not contiguous with Ehdr
|
|
|| ehdri.e_phentsize!=sizeof(Elf32_Phdr)
|
|
)
|
|
return false;
|
|
|
|
// additional requirements for vmlinux
|
|
if (ehdri.e_machine != Elf32_Ehdr::EM_ARM
|
|
|| ehdri.e_type!=Elf32_Ehdr::ET_EXEC
|
|
|| 0xc0008000!=ehdri.e_entry
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// A Linux kernel must have a __ksymtab section. [??]
|
|
Elf_LE32_Shdr const *p, *const shstrsec = getElfSections();
|
|
if (0==shstrsec) {
|
|
return false;
|
|
}
|
|
{
|
|
int j;
|
|
for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if (Elf32_Shdr::SHT_PROGBITS==p->sh_type
|
|
&& 0==strcmp("__ksymtab", p->sh_name + shstrtab)) {
|
|
break;
|
|
}
|
|
}
|
|
if (j < 0)
|
|
return false;
|
|
}
|
|
|
|
phdri = new Elf_LE32_Phdr[(unsigned) ehdri.e_phnum];
|
|
fi->seek(ehdri.e_phoff, SEEK_SET);
|
|
fi->readx(phdri, ehdri.e_phnum * sizeof(*phdri));
|
|
|
|
// Put PT_LOAD together at the beginning, ascending by .p_paddr.
|
|
qsort(phdri, ehdri.e_phnum, sizeof(*phdri), compare_Phdr);
|
|
|
|
// Check that PT_LOADs form one contiguous chunk of the file.
|
|
for (unsigned j = 0; j < ehdri.e_phnum; ++j) {
|
|
if (Elf32_Phdr::PT_LOAD==phdri[j].p_type) {
|
|
if (0xfff & (phdri[j].p_offset | phdri[j].p_paddr
|
|
| phdri[j].p_align | phdri[j].p_vaddr) ) {
|
|
return false;
|
|
}
|
|
if (0 < j) {
|
|
unsigned const sz = (0u - phdri[j-1].p_align)
|
|
& (phdri[j-1].p_align -1 + phdri[j-1].p_filesz);
|
|
if ((sz + phdri[j-1].p_offset)!=phdri[j].p_offset) {
|
|
return false;
|
|
}
|
|
}
|
|
++n_ptload;
|
|
sz_ptload = phdri[j].p_filesz + phdri[j].p_offset - phdri[0].p_offset;
|
|
}
|
|
}
|
|
return 0 < n_ptload;
|
|
}
|
|
|
|
|
|
Linker* PackVmlinuxARM::newLinker() const
|
|
{
|
|
return new ElfLinkerArmLE;
|
|
}
|
|
|
|
|
|
void PackVmlinuxARM::buildLoader(const Filter *ft)
|
|
{
|
|
// prepare loader
|
|
initLoader(stub_arm_linux_kernel_vmlinux, sizeof(stub_arm_linux_kernel_vmlinux));
|
|
addLoader("LINUX000", NULL);
|
|
if (ft->id) {
|
|
assert(ft->calls > 0);
|
|
addLoader("LINUX010", NULL);
|
|
}
|
|
addLoader("LINUX020", NULL);
|
|
if (ft->id) {
|
|
addFilter32(ft->id);
|
|
}
|
|
addLoader("LINUX030", NULL);
|
|
if (ph.method == M_NRV2E_8) addLoader("NRV2E", NULL);
|
|
else if (ph.method == M_NRV2B_8) addLoader("NRV2B", NULL);
|
|
else if (ph.method == M_NRV2D_8) addLoader("NRV2D", NULL);
|
|
else if (M_IS_LZMA(ph.method)) addLoader("LZMA_ELF00,LZMA_DEC10,LZMA_DEC30", NULL);
|
|
else throwBadLoader();
|
|
addLoader("IDENTSTR,UPX1HEAD", NULL);
|
|
}
|
|
|
|
|
|
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
|
|
#include "stub/amd64-linux.kernel.vmlinux-head.h"
|
|
static const
|
|
#include "stub/arm-linux.kernel.vmlinux-head.h"
|
|
|
|
void PackVmlinuxI386::pack(OutputFile *fo)
|
|
{
|
|
unsigned fo_off = 0;
|
|
Elf32_Ehdr ehdro;
|
|
LE32 tmp_le32;
|
|
|
|
// NULL
|
|
// .text(PT_LOADs) .note(1st page) .note(rest)
|
|
// .shstrtab /* .symtab .strtab */
|
|
Elf32_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 = Elf32_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 = Elf32_Shdr::SHT_PROGBITS;
|
|
shdro[1].sh_flags = Elf32_Shdr::SHF_ALLOC | Elf32_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_le32 = ehdri.e_entry; fo->write(&tmp_le32, 4);
|
|
|
|
// COMPRESSED_LENGTH
|
|
fo->write(&stub_i386_linux_kernel_vmlinux_head[sizeof(stub_i386_linux_kernel_vmlinux_head)-(1+ 4)], 1);
|
|
tmp_le32 = ph.c_len; fo->write(&tmp_le32, 4);
|
|
|
|
fo_off += 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 = Elf32_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_le32 = ph.u_len; fo->write(&tmp_le32, 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 = Elf32_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_le32 = ph.u_len; fo->write(&tmp_le32, 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 = Elf32_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 = Elf32_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 Elf32_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 = Elf32_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
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
// unpack
|
|
**************************************************************************/
|
|
|
|
int PackVmlinuxI386::canUnpack()
|
|
{
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
|
|
// now check the ELF header
|
|
if (memcmp(&ehdri, "\x7f\x45\x4c\x46\x01\x01\x01", 7) // ELF 32-bit LSB
|
|
|| !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded
|
|
|| ehdri.e_machine != Elf32_Ehdr::EM_386
|
|
|| ehdri.e_version != 1 // version
|
|
|| ehdri.e_type != Elf32_Ehdr::ET_REL
|
|
|| ehdri.e_shnum < 4
|
|
|| (unsigned)file_size < (ehdri.e_shnum * sizeof(Elf32_Shdr) + ehdri.e_shoff)
|
|
)
|
|
return false;
|
|
|
|
// find the .shstrtab section
|
|
Elf_LE32_Shdr const *const shstrsec = getElfSections();
|
|
if (0==shstrsec) {
|
|
return false;
|
|
}
|
|
|
|
// check for .text .note .note and sane (.sh_size + .sh_offset)
|
|
p_note0 = p_note1 = p_text = 0;
|
|
int j;
|
|
Elf_LE32_Shdr *p;
|
|
for (p= shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if ((unsigned)file_size < (p->sh_size + p->sh_offset)
|
|
|| shstrsec->sh_size < (5+ p->sh_name) ) {
|
|
continue;
|
|
}
|
|
if (0==strcmp(".text", shstrtab + p->sh_name)) {
|
|
p_text = p;
|
|
}
|
|
if (0==strcmp(".note", shstrtab + p->sh_name)) {
|
|
if (0==p_note0) {
|
|
p_note0 = p;
|
|
} else
|
|
if (0==p_note1) {
|
|
p_note1 = p;
|
|
}
|
|
}
|
|
}
|
|
if (0==p_text || 0==p_note0 || 0==p_note1) {
|
|
return false;
|
|
}
|
|
|
|
char buf[1024];
|
|
fi->seek(p_text->sh_offset + p_text->sh_size - sizeof(buf), SEEK_SET);
|
|
fi->readx(buf, sizeof(buf));
|
|
if (!getPackHeader(buf, sizeof(buf)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void PackVmlinuxI386::unpack(OutputFile *fo)
|
|
{
|
|
unsigned char buf[5];
|
|
PackHeader const ph_tmp(ph);
|
|
|
|
fi->seek(p_note0->sh_offset, SEEK_SET);
|
|
fi->readx(&buf[0], 4);
|
|
ph.u_len = get_le32(buf);
|
|
ph.c_len = p_note0->sh_size - 4;
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf, false);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
ph = ph_tmp;
|
|
fi->seek(p_text->sh_offset + sizeof(stub_i386_linux_kernel_vmlinux_head) -5, SEEK_SET);
|
|
fi->readx(&buf[0], 5);
|
|
if (0xE8!=buf[0] || get_le32(&buf[1]) != ph.c_len)
|
|
{
|
|
throwCantUnpack(".text corrupted");
|
|
}
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf);
|
|
|
|
Filter ft(ph.level);
|
|
ft.init(ph.filter, 0);
|
|
ft.cto = (unsigned char) ph.filter_cto;
|
|
ft.unfilter(obuf, ph.u_len);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
fi->seek(p_note1->sh_offset, SEEK_SET);
|
|
fi->readx(&buf[0], 4);
|
|
ph.u_len = get_le32(buf);
|
|
ph.c_len = p_note1->sh_size - 4;
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, p_note1->sh_size - sizeof(ph.u_len));
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf, false);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
ph = ph_tmp;
|
|
}
|
|
|
|
void PackVmlinuxARM::pack(OutputFile *fo)
|
|
{
|
|
unsigned fo_off = 0;
|
|
Elf32_Ehdr ehdro;
|
|
LE32 tmp_le32;
|
|
|
|
// NULL
|
|
// .text(PT_LOADs) .note(1st page) .note(rest)
|
|
// .shstrtab .symtab .strtab
|
|
Elf32_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 = Elf32_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("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 = Elf32_Shdr::SHT_PROGBITS;
|
|
shdro[1].sh_flags = Elf32_Shdr::SHF_ALLOC | Elf32_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;
|
|
|
|
// First word from vmlinux-head.S
|
|
fo->write(&stub_arm_linux_kernel_vmlinux_head[0], 4);
|
|
|
|
// Second word
|
|
tmp_le32 = (0xff000000 & get_le32(&stub_arm_linux_kernel_vmlinux_head[4]))
|
|
| (0x00ffffff & (-1+ ((3+ txt_c_len)>>2)));
|
|
fo->write(&tmp_le32, 4);
|
|
|
|
fo_off += sizeof(stub_arm_linux_kernel_vmlinux_head);
|
|
|
|
fo->write(obuf, txt_c_len); fo_off += txt_c_len;
|
|
tmp_le32 = 0; fo->write(&tmp_le32, 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 = Elf32_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_le32 = ph.u_len; fo->write(&tmp_le32, 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 = Elf32_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_le32 = ph.u_len; fo->write(&tmp_le32, 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 = Elf32_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 = Elf32_Shdr::SHT_SYMTAB;
|
|
shdro[5].sh_offset = fo_off;
|
|
shdro[5].sh_size = 5*sizeof(Elf_LE32_Sym);
|
|
//shdro[5].sh_flags = Elf32_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(Elf_LE32_Sym);
|
|
|
|
Elf_LE32_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.St_info(Elf32_Sym::STB_LOCAL, Elf32_Sym::STT_SECTION);
|
|
sec_sym.st_other = Elf32_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
|
|
Elf_LE32_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.St_info(Elf32_Sym::STB_GLOBAL, Elf32_Sym::STT_FUNC);
|
|
unc_ker.st_other = Elf32_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 = Elf32_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
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
// unpack
|
|
**************************************************************************/
|
|
|
|
int PackVmlinuxARM::canUnpack()
|
|
{
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
|
|
// now check the ELF header
|
|
if (memcmp(&ehdri, "\x7f\x45\x4c\x46\x01\x01\x01", 7) // ELF 32-bit LSB
|
|
|| !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded
|
|
|| ehdri.e_machine != Elf32_Ehdr::EM_ARM
|
|
|| ehdri.e_version != 1 // version
|
|
|| ehdri.e_type != Elf32_Ehdr::ET_REL
|
|
|| ehdri.e_shnum < 4
|
|
|| (unsigned)file_size < (ehdri.e_shnum * sizeof(Elf32_Shdr) + ehdri.e_shoff)
|
|
)
|
|
return false;
|
|
|
|
// find the .shstrtab section
|
|
Elf_LE32_Shdr const *const shstrsec = getElfSections();
|
|
if (0==shstrsec) {
|
|
return false;
|
|
}
|
|
|
|
// check for .text .note .note and sane (.sh_size + .sh_offset)
|
|
p_note0 = p_note1 = p_text = 0;
|
|
int j;
|
|
Elf_LE32_Shdr *p;
|
|
for (p= shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if ((unsigned)file_size < (p->sh_size + p->sh_offset)
|
|
|| shstrsec->sh_size < (5+ p->sh_name) ) {
|
|
continue;
|
|
}
|
|
if (0==strcmp(".text", shstrtab + p->sh_name)) {
|
|
p_text = p;
|
|
}
|
|
if (0==strcmp(".note", shstrtab + p->sh_name)) {
|
|
if (0==p_note0) {
|
|
p_note0 = p;
|
|
} else
|
|
if (0==p_note1) {
|
|
p_note1 = p;
|
|
}
|
|
}
|
|
}
|
|
if (0==p_text || 0==p_note0 || 0==p_note1) {
|
|
return false;
|
|
}
|
|
|
|
char buf[1024];
|
|
fi->seek(p_text->sh_offset + p_text->sh_size - sizeof(buf), SEEK_SET);
|
|
fi->readx(buf, sizeof(buf));
|
|
if (!getPackHeader(buf, sizeof(buf)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void PackVmlinuxARM::unpack(OutputFile *fo)
|
|
{
|
|
unsigned char buf[5];
|
|
PackHeader const ph_tmp(ph);
|
|
|
|
fi->seek(p_note0->sh_offset, SEEK_SET);
|
|
fi->readx(&buf[0], 4);
|
|
ph.u_len = get_le32(buf);
|
|
ph.c_len = p_note0->sh_size - 4;
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf, false);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
ph = ph_tmp;
|
|
fi->seek(p_text->sh_offset + sizeof(stub_arm_linux_kernel_vmlinux_head) -5, SEEK_SET);
|
|
fi->readx(&buf[0], 5);
|
|
if (0xE8!=buf[0] || get_le32(&buf[1]) != ph.c_len)
|
|
{
|
|
throwCantUnpack(".text corrupted");
|
|
}
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf);
|
|
|
|
Filter ft(ph.level);
|
|
ft.init(ph.filter, 0);
|
|
ft.cto = (unsigned char) ph.filter_cto;
|
|
ft.unfilter(obuf, ph.u_len);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
fi->seek(p_note1->sh_offset, SEEK_SET);
|
|
fi->readx(&buf[0], 4);
|
|
ph.u_len = get_le32(buf);
|
|
ph.c_len = p_note1->sh_size - 4;
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, p_note1->sh_size - sizeof(ph.u_len));
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf, false);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
ph = ph_tmp;
|
|
}
|
|
|
|
//
|
|
// Example usage within build system of Linux kernel-2.6.18:
|
|
//
|
|
//----- arch/i386/boot/compressed/Makefile
|
|
//#
|
|
//# linux/arch/i386/boot/compressed/Makefile
|
|
//#
|
|
//# create a compressed vmlinux image from the original vmlinux
|
|
//#
|
|
//
|
|
//targets := vmlinux upx-head.o upx-piggy.o
|
|
//
|
|
//LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32
|
|
//
|
|
//$(obj)/vmlinux: $(obj)/upx-head.o $(obj)/upx-piggy.o FORCE
|
|
// $(call if_changed,ld)
|
|
// @:
|
|
//
|
|
//$(obj)/upx-piggy.o: vmlinux FORCE
|
|
// rm -f $@
|
|
// upx --best -o $@ $<
|
|
// touch $@
|
|
//
|
|
//#
|
|
//# The ORIGINAL build sequence using gzip is:
|
|
//# vmlinux Elf executable at top level in tree
|
|
//# (in same directory as MAINTAINERS)
|
|
//# In arch/i386:
|
|
//# boot/compressed/vmlinux.bin by objcopy -O binary
|
|
//# boot/compressed/vmlinux.bin.gz by gzip
|
|
//# boot/compressed/piggy.o by ld --format binary --oformat elf32-i386
|
|
//#
|
|
//# The 3 steps above create a linkable
|
|
//# compressed blob.
|
|
//# In arch/i386:
|
|
//# boot/compressed/vmlinux by ld head.o misc.o piggy.o
|
|
//# boot/vmlinux.bin by objcopy
|
|
//# boot/bzImage by arch/i386/boot/tools/build with
|
|
//# bootsect and setup
|
|
//#
|
|
//#
|
|
//# The MODIFIED build sequence using upx is:
|
|
//# vmlinux Elf executable at top level in tree
|
|
//# (in same directory as MAINTAINERS)
|
|
//# In arch/i386:
|
|
//# boot/compressed/upx-piggy.o by upx format vmlinux/386
|
|
//#
|
|
//# In arch/i386/boot:
|
|
//# boot/compressed/vmlinux by ld upx-head.o upx-piggy.o
|
|
//# boot/vmlinux.bin by objcopy
|
|
//# boot/bzImage by arch/i386/boot/tools/build with
|
|
//# bootsect and setup
|
|
//#
|
|
//-----
|
|
//
|
|
//----- arch/i386/boot/compressed/upx-head.S
|
|
//startup_32: .globl startup_32 # In: %esi=0x90000 setup data "real_mode pointer"
|
|
// /* All code is in stub/src/i386-linux.kernel.vmlinux-head.S */
|
|
//-----
|
|
|
|
#if 0 /*{*/
|
|
// For Debian nslu2-linux (2.6.19), only this Makefile changes:
|
|
--- ./debian/build/build-arm-none-ixp4xx/arch/arm/boot/compressed/Makefile.orig 2006-11-29 13:57:37.000000000 -0800
|
|
+++ ./debian/build/build-arm-none-ixp4xx/arch/arm/boot/compressed/Makefile 2006-12-16 02:39:38.000000000 -0800
|
|
@@ -5,7 +5,7 @@
|
|
#
|
|
|
|
HEAD = head.o
|
|
-OBJS = misc.o
|
|
+OBJS =
|
|
FONTC = drivers/video/console/font_acorn_8x8.c
|
|
|
|
FONT = $(addprefix ../../../../drivers/video/console/, font_acorn_8x8.o)
|
|
@@ -73,8 +73,8 @@
|
|
|
|
SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
|
|
|
|
-targets := vmlinux vmlinux.lds piggy.gz piggy.o $(FONT) \
|
|
- head.o misc.o $(OBJS)
|
|
+targets := vmlinux vmlinux.lds upx-piggy.o $(FONT) \
|
|
+ head.o $(OBJS)
|
|
EXTRA_CFLAGS := -fpic
|
|
EXTRA_AFLAGS :=
|
|
|
|
@@ -95,20 +95,16 @@
|
|
# would otherwise mess up our GOT table
|
|
CFLAGS_misc.o := -Dstatic=
|
|
|
|
-$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
|
|
- $(addprefix $(obj)/, $(OBJS)) FORCE
|
|
+$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) \
|
|
+ $(addprefix $(obj)/, $(OBJS)) $(obj)/upx-piggy.o FORCE
|
|
$(call if_changed,ld)
|
|
@:
|
|
|
|
-$(obj)/piggy.gz: $(obj)/../Image FORCE
|
|
- $(call if_changed,gzip)
|
|
-
|
|
-$(obj)/piggy.o: $(obj)/piggy.gz FORCE
|
|
+$(obj)/upx-piggy.o: vmlinux FORCE
|
|
+ rm -f $@; upx --lzma -o $@ $<; touch $@
|
|
|
|
CFLAGS_font_acorn_8x8.o := -Dstatic=
|
|
|
|
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config
|
|
@sed "$(SEDFLAGS)" < $< > $@
|
|
|
|
-$(obj)/misc.o: $(obj)/misc.c include/asm/arch/uncompress.h lib/inflate.c
|
|
-
|
|
#endif /*}*/
|
|
|
|
// Approximate translation for Linux 2.4.x:
|
|
// - - -
|
|
// arch/i386/Makefile: LD_FLAGS=-e startup_32
|
|
//----- arch/i386/boot/compressed/Makefile
|
|
//# linux/arch/i386/boot/compressed/Makefile
|
|
//#
|
|
//# create a compressed vmlinux image from the original vmlinux
|
|
//#
|
|
//
|
|
//HEAD = upx-head.o
|
|
//SYSTEM = $(TOPDIR)/vmlinux
|
|
//
|
|
//OBJECTS = $(HEAD)
|
|
//
|
|
//ZLDFLAGS = -e startup_32
|
|
//
|
|
//#
|
|
//# ZIMAGE_OFFSET is the load offset of the compression loader
|
|
//# BZIMAGE_OFFSET is the load offset of the high loaded compression loader
|
|
//#
|
|
//ZIMAGE_OFFSET = 0x1000
|
|
//BZIMAGE_OFFSET = 0x100000
|
|
//
|
|
//ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS)
|
|
//BZLINKFLAGS = -Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS)
|
|
//
|
|
//all: vmlinux
|
|
//
|
|
//vmlinux: upx-piggy.o $(OBJECTS)
|
|
// $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) upx-piggy.o
|
|
//
|
|
//bvmlinux: upx-piggy.o $(OBJECTS)
|
|
// $(LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) upx-piggy.o
|
|
//
|
|
//upx-piggy.o: $(SYSTEM)
|
|
// $(RM) -f $@
|
|
// upx --best -o $@ $<
|
|
// touch $@
|
|
//
|
|
//clean:
|
|
// rm -f vmlinux bvmlinux _tmp_*
|
|
//-----
|
|
|
|
//
|
|
// Example test jig:
|
|
// $ gcc -o test-piggy -nostartfiles -nostdlib test-piggy.o piggy.o
|
|
// $ gdb test-piggy
|
|
// (gdb) run >dumped
|
|
// (gdb) /* Execute [single step, etc.; the decompressor+unfilter moves!]
|
|
// * until reaching the 'lret' at the end of unfilter.
|
|
// */
|
|
// (gdb) set $pc= &dump
|
|
// (gdb) stepi
|
|
// (gdb) set $edx=<actual_uncompressed_length>
|
|
// (gdb) continue
|
|
// (gdb) q
|
|
// $ # Compare file 'dumped' with the portion of vmlinux that made piggy.o.
|
|
// $ dd if=vmlinux bs=<leader_size> skip=1 | cmp - dumped
|
|
// cmp: EOF on dumped
|
|
// $
|
|
//----- test-piggy.S
|
|
//#include <asm/mman.h>
|
|
//#include <asm/unistd.h>
|
|
//
|
|
//dump:
|
|
// movl $0x456789,%edx # length MODIFY THIS VALUE TO SUIT YOUR CASE
|
|
// movl $0x100000,%ecx # base
|
|
// movl $1,%ebx # stdout
|
|
// movl $ __NR_write,%eax
|
|
// int $0x80
|
|
// nop
|
|
// hlt
|
|
//mmap:
|
|
// pushl %ebx
|
|
// leal 2*4(%esp),%ebx
|
|
// pushl $ __NR_mmap; popl %eax
|
|
// int $0x80
|
|
// popl %ebx
|
|
// ret $6*4
|
|
//
|
|
//_start: .globl _start
|
|
// nop
|
|
// int3 # enter debugger!
|
|
// pushl $0
|
|
// pushl $0
|
|
// pushl $ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
|
|
// pushl $ PROT_EXEC | PROT_WRITE | PROT_READ
|
|
// pushl $0x600000 # 6MB length
|
|
// pushl $0x100000 # 1MB address
|
|
// call mmap
|
|
// leal -0x9000(%esp),%esi # expect "lea 0x9000(%esi),%esp" later
|
|
// push %cs
|
|
///* Fall into .text of upx-compressed vmlinux. */
|
|
//-----
|
|
|
|
/*************************************************************************
|
|
//
|
|
**************************************************************************/
|
|
|
|
PackVmlinuxAMD64::PackVmlinuxAMD64(InputFile *f) :
|
|
super(f), n_ptload(0), phdri(NULL), shdri(NULL), shstrtab(NULL)
|
|
{
|
|
bele = &N_BELE_RTP::le_policy;
|
|
}
|
|
|
|
PackVmlinuxAMD64::~PackVmlinuxAMD64()
|
|
{
|
|
delete [] shstrtab;
|
|
delete [] phdri;
|
|
delete [] shdri;
|
|
}
|
|
|
|
const int *PackVmlinuxAMD64::getCompressionMethods(int method, int level) const
|
|
{
|
|
return Packer::getDefaultCompressionMethods_le32(method, level);
|
|
}
|
|
|
|
|
|
const int *PackVmlinuxAMD64::getFilters() const
|
|
{
|
|
static const int filters[] = {
|
|
0x49, 0x46,
|
|
-1 };
|
|
return filters;
|
|
}
|
|
|
|
// copied from PackUnix 2006-10-13.
|
|
int PackVmlinuxAMD64::getStrategy(Filter &/*ft*/)
|
|
{
|
|
// Called just before reading and compressing each block.
|
|
// Might want to adjust blocksize, etc.
|
|
|
|
// If user specified the filter, then use it (-2==strategy).
|
|
// Else try the first two filters, and pick the better (2==strategy).
|
|
return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
|
|
}
|
|
|
|
|
|
Elf_LE64_Shdr const *
|
|
PackVmlinuxAMD64::getElfSections()
|
|
{
|
|
Elf_LE64_Shdr const *p, *shstrsec=0;
|
|
shdri = new Elf_LE64_Shdr[(unsigned) ehdri.e_shnum];
|
|
fi->seek(ehdri.e_shoff, SEEK_SET);
|
|
fi->readx(shdri, ehdri.e_shnum * sizeof(*shdri));
|
|
int j;
|
|
for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if (Elf64_Shdr::SHT_STRTAB==p->sh_type
|
|
&& (p->sh_size + p->sh_offset) <= (unsigned) file_size
|
|
&& (10+ p->sh_name) <= p->sh_size // 1+ strlen(".shstrtab")
|
|
) {
|
|
delete [] shstrtab;
|
|
shstrtab = new char[1+ p->sh_size];
|
|
fi->seek(p->sh_offset, SEEK_SET);
|
|
fi->readx(shstrtab, p->sh_size);
|
|
shstrtab[p->sh_size] = '\0';
|
|
if (0==strcmp(".shstrtab", shstrtab + p->sh_name)) {
|
|
shstrsec = p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return shstrsec;
|
|
}
|
|
|
|
static int __acc_cdecl_qsort
|
|
compare_Phdr64(void const *aa, void const *bb)
|
|
{
|
|
Elf64_Phdr const *const a = (Elf64_Phdr const *)aa;
|
|
Elf64_Phdr const *const b = (Elf64_Phdr const *)bb;
|
|
unsigned const xa = a->p_type - Elf64_Phdr::PT_LOAD;
|
|
unsigned const xb = b->p_type - Elf64_Phdr::PT_LOAD;
|
|
if (xa < xb) return -1; // PT_LOAD first
|
|
if (xa > xb) return 1;
|
|
if (a->p_paddr < b->p_paddr) return -1; // ascending by .p_paddr
|
|
if (a->p_paddr > b->p_paddr) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool PackVmlinuxAMD64::canPack()
|
|
{
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
|
|
// now check the ELF header
|
|
if (memcmp(&ehdri, "\x7f\x45\x4c\x46\x02\x01\x01", 7) // ELF 64-bit LSB
|
|
|| ehdri.e_version != 1 // version
|
|
|| ehdri.e_ehsize != sizeof(ehdri) // different <elf.h> ?
|
|
|| ehdri.e_phoff != sizeof(ehdri) // Phdrs not contiguous with Ehdr
|
|
|| ehdri.e_phentsize!=sizeof(Elf64_Phdr)
|
|
)
|
|
return false;
|
|
|
|
// additional requirements for vmlinux
|
|
if (ehdri.e_machine != Elf64_Ehdr::EM_X86_64
|
|
|| ehdri.e_type!=Elf64_Ehdr::ET_EXEC
|
|
|| ehdri.e_entry < 0x200000 // entry below 2MB
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// A Linux kernel must have a __ksymtab section. [??]
|
|
Elf_LE64_Shdr const *p, *const shstrsec = getElfSections();
|
|
if (0==shstrsec) {
|
|
return false;
|
|
}
|
|
int j;
|
|
for (p = shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if (Elf64_Shdr::SHT_PROGBITS==p->sh_type
|
|
&& 0==strcmp("__ksymtab", p->sh_name + shstrtab)) {
|
|
break;
|
|
}
|
|
}
|
|
if (j < 0) {
|
|
return false;
|
|
}
|
|
|
|
phdri = new Elf_LE64_Phdr[(unsigned) ehdri.e_phnum];
|
|
fi->seek(ehdri.e_phoff, SEEK_SET);
|
|
fi->readx(phdri, ehdri.e_phnum * sizeof(*phdri));
|
|
|
|
// Put PT_LOAD together at the beginning, ascending by .p_paddr.
|
|
qsort(phdri, ehdri.e_phnum, sizeof(*phdri), compare_Phdr64);
|
|
|
|
// Check that PT_LOADs form one contiguous chunk of the file.
|
|
for (unsigned j = 0; j < ehdri.e_phnum; ++j) {
|
|
if (Elf64_Phdr::PT_LOAD==phdri[j].p_type) {
|
|
if (0xfff & (phdri[j].p_offset | phdri[j].p_paddr
|
|
| phdri[j].p_align | phdri[j].p_vaddr) ) {
|
|
return false;
|
|
}
|
|
if (0 < j) {
|
|
unsigned const org = (0u - phdri[j].p_align) &
|
|
(-1 + phdri[j].p_align +
|
|
phdri[j-1].p_filesz + phdri[j-1].p_offset);
|
|
unsigned const loc = (0u - phdri[j].p_align) &
|
|
(-1 + phdri[j].p_align + phdri[j].p_offset);
|
|
if (org!=loc) {
|
|
return false;
|
|
}
|
|
}
|
|
++n_ptload;
|
|
sz_ptload = phdri[j].p_filesz + phdri[j].p_offset - phdri[0].p_offset;
|
|
}
|
|
}
|
|
return 0 < n_ptload;
|
|
}
|
|
|
|
|
|
Linker* PackVmlinuxAMD64::newLinker() const
|
|
{
|
|
return new ElfLinkerX86;
|
|
}
|
|
|
|
|
|
void PackVmlinuxAMD64::buildLoader(const Filter *ft)
|
|
{
|
|
// prepare loader
|
|
initLoader(stub_amd64_linux_kernel_vmlinux, sizeof(stub_amd64_linux_kernel_vmlinux));
|
|
addLoader("LINUX000",
|
|
(0x40==(0xf0 & ft->id)) ? "LXCKLLT1" : (ft->id ? "LXCALLT1" : ""),
|
|
"LXMOVEUP",
|
|
getDecompressorSections(),
|
|
NULL
|
|
);
|
|
if (ft->id) {
|
|
assert(ft->calls > 0);
|
|
if (0x40==(0xf0 & ft->id)) {
|
|
addLoader("LXCKLLT9", NULL);
|
|
}
|
|
else {
|
|
addLoader("LXCALLT9", NULL);
|
|
}
|
|
addFilter32(ft->id);
|
|
}
|
|
addLoader("LINUX990,IDENTSTR,UPX1HEAD", NULL);
|
|
}
|
|
|
|
|
|
void PackVmlinuxAMD64::pack(OutputFile *fo)
|
|
{
|
|
unsigned fo_off = 0;
|
|
Elf64_Ehdr ehdro;
|
|
LE64 tmp_le32;
|
|
|
|
// NULL
|
|
// .text(PT_LOADs) .note(1st page) .note(rest)
|
|
// .shstrtab /* .symtab .strtab */
|
|
Elf64_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 = Elf64_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 = Elf64_Shdr::SHT_PROGBITS;
|
|
shdro[1].sh_flags = Elf64_Shdr::SHF_ALLOC | Elf64_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_le32 = ehdri.e_entry; fo->write(&tmp_le32, 4);
|
|
|
|
// COMPRESSED_LENGTH
|
|
fo->write(&stub_amd64_linux_kernel_vmlinux_head[sizeof(stub_amd64_linux_kernel_vmlinux_head)-(1+ 4)], 1);
|
|
tmp_le32 = ph.c_len; fo->write(&tmp_le32, 4);
|
|
|
|
fo_off += 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 = Elf64_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_le32 = ph.u_len; fo->write(&tmp_le32, 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 = Elf64_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_le32 = ph.u_len; fo->write(&tmp_le32, 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 = Elf64_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 = Elf64_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 Elf64_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 = Elf64_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();
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
// unpack
|
|
**************************************************************************/
|
|
|
|
int PackVmlinuxAMD64::canUnpack()
|
|
{
|
|
fi->seek(0, SEEK_SET);
|
|
fi->readx(&ehdri, sizeof(ehdri));
|
|
|
|
// now check the ELF header
|
|
if (memcmp(&ehdri, "\x7f\x45\x4c\x46\x01\x01\x01", 7) // ELF 32-bit LSB
|
|
|| !memcmp(&ehdri.e_ident[8], "FreeBSD", 7) // branded
|
|
|| ehdri.e_machine != Elf64_Ehdr::EM_X86_64
|
|
|| ehdri.e_version != 1 // version
|
|
|| ehdri.e_type != Elf64_Ehdr::ET_REL
|
|
|| ehdri.e_shnum < 4
|
|
|| (unsigned)file_size < (ehdri.e_shnum * sizeof(Elf64_Shdr) + ehdri.e_shoff)
|
|
)
|
|
return false;
|
|
|
|
// find the .shstrtab section
|
|
Elf_LE64_Shdr const *const shstrsec = getElfSections();
|
|
if (0==shstrsec) {
|
|
return false;
|
|
}
|
|
|
|
// check for .text .note .note and sane (.sh_size + .sh_offset)
|
|
p_note0 = p_note1 = p_text = 0;
|
|
int j;
|
|
Elf_LE64_Shdr *p;
|
|
for (p= shdri, j= ehdri.e_shnum; --j>=0; ++p) {
|
|
if ((unsigned)file_size < (p->sh_size + p->sh_offset)
|
|
|| shstrsec->sh_size < (5+ p->sh_name) ) {
|
|
continue;
|
|
}
|
|
if (0==strcmp(".text", shstrtab + p->sh_name)) {
|
|
p_text = p;
|
|
}
|
|
if (0==strcmp(".note", shstrtab + p->sh_name)) {
|
|
if (0==p_note0) {
|
|
p_note0 = p;
|
|
} else
|
|
if (0==p_note1) {
|
|
p_note1 = p;
|
|
}
|
|
}
|
|
}
|
|
if (0==p_text || 0==p_note0 || 0==p_note1) {
|
|
return false;
|
|
}
|
|
|
|
char buf[1024];
|
|
fi->seek(p_text->sh_offset + p_text->sh_size - sizeof(buf), SEEK_SET);
|
|
fi->readx(buf, sizeof(buf));
|
|
if (!getPackHeader(buf, sizeof(buf)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void PackVmlinuxAMD64::unpack(OutputFile *fo)
|
|
{
|
|
unsigned char buf[5];
|
|
PackHeader const ph_tmp(ph);
|
|
|
|
fi->seek(p_note0->sh_offset, SEEK_SET);
|
|
fi->readx(&buf[0], 4);
|
|
ph.u_len = get_le32(buf);
|
|
ph.c_len = p_note0->sh_size - 4;
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf, false);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
ph = ph_tmp;
|
|
fi->seek(p_text->sh_offset + sizeof(stub_amd64_linux_kernel_vmlinux_head) -5, SEEK_SET);
|
|
fi->readx(&buf[0], 5);
|
|
if (0xE8!=buf[0] || get_le32(&buf[1]) != ph.c_len)
|
|
{
|
|
throwCantUnpack(".text corrupted");
|
|
}
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, ph.c_len);
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf);
|
|
|
|
Filter ft(ph.level);
|
|
ft.init(ph.filter, 0);
|
|
ft.cto = (unsigned char) ph.filter_cto;
|
|
ft.unfilter(obuf, ph.u_len);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
fi->seek(p_note1->sh_offset, SEEK_SET);
|
|
fi->readx(&buf[0], 4);
|
|
ph.u_len = get_le32(buf);
|
|
ph.c_len = p_note1->sh_size - 4;
|
|
ibuf.alloc(ph.c_len);
|
|
fi->readx(ibuf, p_note1->sh_size - sizeof(ph.u_len));
|
|
obuf.allocForUncompression(ph.u_len);
|
|
decompress(ibuf, obuf, false);
|
|
fo->write(obuf, ph.u_len);
|
|
obuf.dealloc();
|
|
ibuf.dealloc();
|
|
|
|
ph = ph_tmp;
|
|
}
|
|
/*
|
|
vi:ts=4:et
|
|
*/
|
|
|
|
|