upx/src/p_djgpp2.cpp
Markus F.X.J. Oberhumer abc15a398c Updated the file headers.
committer: mfx <mfx> 974118160 +0000
2000-11-13 12:22:40 +00:00

435 lines
12 KiB
C++

/* p_djgpp2.cpp --
This file is part of the UPX executable compressor.
Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2000 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@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu
*/
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "p_djgpp2.h"
static const
#include "stub/stubify.h"
static const
#include "stub/l_djgpp2.h"
/*************************************************************************
//
**************************************************************************/
PackDjgpp2::PackDjgpp2(InputFile *f) :
super(f), coff_offset(0)
{
assert(sizeof(coff_hdr) == 0xa8);
assert(sizeof(stubify_stub) == 2048);
}
int PackDjgpp2::getCompressionMethod() const
{
if (M_IS_NRV2B(opt->method))
return M_NRV2B_LE32;
if (M_IS_NRV2D(opt->method))
return M_NRV2D_LE32;
return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_LE32 : M_NRV2B_LE32;
}
const int *PackDjgpp2::getFilters() const
{
static const int filters[] = { 0x26, 0x24, 0x11, 0x14, 0x13, 0x16,
0x25, 0x15, 0x12, -1 };
return filters;
}
int PackDjgpp2::buildLoader(const Filter *ft)
{
// prepare loader
initLoader(nrv_loader,sizeof(nrv_loader));
addLoader("IDENTSTR""DJ2MAIN1",
ft->id ? "DJCALLT1" : "",
"DJ2MAIN2",
getDecompressor(),
"DJ2BSS00",
NULL
);
if (ft->id)
{
addLoader("DJCALLT2",NULL);
addFilter32(ft->id);
}
addLoader("DJRETURN+40DXXXXUPX1HEAD",NULL);
return getLoaderSize();
}
/*************************************************************************
// util
**************************************************************************/
void PackDjgpp2::handleStub(OutputFile *fo)
{
if (fo && !opt->djgpp2.coff)
{
if (coff_offset > 0)
{
// copy stub from exe
Packer::handleStub(fi,fo,coff_offset);
}
else
{
// "stubify" stub
info("Adding stub: %ld bytes", (long)sizeof(stubify_stub));
fo->write(stubify_stub,sizeof(stubify_stub));
}
}
}
static bool is_dlm(InputFile *fi, long coff_offset)
{
unsigned char buf[4];
long off;
try {
fi->seek(coff_offset,SEEK_SET);
fi->readx(buf,4);
off = get_le32(buf);
if (off < 0 || off > coff_offset + 4)
return false;
fi->seek(off,SEEK_SET);
fi->readx(buf,4);
if (memcmp(buf,"DLMF",4) == 0)
return true;
} catch (IOException&) {
}
return false;
}
static void handle_allegropak(InputFile *fi, OutputFile *fo)
{
unsigned char buf[0x4000];
unsigned pfsize=0, ic;
try {
fi->seek(-8,SEEK_END);
fi->readx(buf,8);
if (memcmp(buf,"slh+",4) != 0)
return;
pfsize = get_be32(buf+4);
fi->seek(-(off_t)pfsize,SEEK_END);
} catch (IOException&) {
return;
}
while (pfsize)
{
ic = pfsize < sizeof(buf) ? pfsize : sizeof(buf);
fi->readx(buf,ic);
fo->write(buf,ic);
pfsize -= ic;
}
}
bool PackDjgpp2::readFileHeader()
{
unsigned char hdr[0x1c];
unsigned char magic[8];
fi->seek(0,SEEK_SET);
fi->readx(hdr,sizeof(hdr));
if (get_le16(hdr) == 0x5a4d) // MZ exe signature, stubbed?
{
coff_offset = 512 * get_le16(hdr+4);
if (get_le16(hdr+2) != 0)
coff_offset += get_le16(hdr+2) - 512;
fi->seek(512,SEEK_SET);
fi->readx(magic,8);
if (memcmp("go32stub",magic,8) != 0)
return false; // not V2 image
fi->seek(coff_offset,SEEK_SET);
if (fi->read(&coff_hdr,sizeof(coff_hdr)) != sizeof(coff_hdr))
throwCantPack("skipping djgpp symlink");
}
else
{
fi->seek(coff_offset,SEEK_SET);
fi->readx(&coff_hdr,0xa8);
}
if (coff_hdr.f_magic != 0x014c) // I386MAGIC
return false;
if ((coff_hdr.f_flags & 2) == 0) // F_EXEC - COFF executable
return false;
if (coff_hdr.a_magic != 0413) // ZMAGIC - demand load format
return false;
// FIXME: check for Linux etc.
text = coff_hdr.sh;
data = text + 1;
bss = data + 1;
return true;
}
// "strip" debug info
void PackDjgpp2::stripDebug()
{
coff_hdr.f_symptr = 0;
coff_hdr.f_nsyms = 0;
coff_hdr.f_flags = 0x10f; // 0x100: "32 bit machine: LSB first"
memset(text->misc,0,12);
}
/*************************************************************************
//
**************************************************************************/
bool PackDjgpp2::canPack()
{
if (!readFileHeader())
return false;
if (is_dlm(fi,coff_offset))
throwCantPack("can't handle DLM");
if (opt->force == 0)
if (text->size != coff_hdr.a_tsize || data->size != coff_hdr.a_dsize)
throwAlreadyPacked();
if (text->vaddr + text->size != data->vaddr
|| data->vaddr + data->size != bss->vaddr)
{
if (text->vaddr + text->size < data->vaddr &&
data->vaddr - text->vaddr == data->scnptr - text->scnptr)
{
// This hack is needed to compress Quake 1!
text->size = coff_hdr.a_tsize = data->vaddr - text->vaddr;
}
else
throwAlreadyPacked();
}
// FIXME: check for Linux etc.
return true;
}
/*************************************************************************
//
**************************************************************************/
void PackDjgpp2::pack(OutputFile *fo)
{
handleStub(fo);
// patch coff header #1: "strip" debug info
stripDebug();
// read file
const unsigned size = text->size + data->size;
const unsigned tpos = text->scnptr;
const unsigned usize = size + (tpos & 0x1ff);
const unsigned hdrsize = 20 + 28 + (40 * coff_hdr.f_nscns);
if (hdrsize < sizeof(coff_hdr) || hdrsize > tpos)
throwCantPack("coff header error");
ibuf.alloc(usize);
obuf.allocForCompression(usize);
fi->seek(coff_offset,SEEK_SET);
fi->readx(ibuf,hdrsize); // orig. coff header
memset(ibuf + hdrsize, 0, tpos - hdrsize);
fi->seek(coff_offset+tpos,SEEK_SET);
fi->readx(ibuf + (tpos & 0x1ff),size);
#if 0
// filter
Filter ft(opt->level);
tryFilters(&ft, ibuf, usize - data->size, text->vaddr & ~0x1ff);
// compress
ph.filter = ft.id;
ph.filter_cto = ft.cto;
ph.u_len = usize;
if (!compress(ibuf,obuf))
throwNotCompressible();
unsigned overlapoh = findOverlapOverhead(obuf,ibuf,512);
overlapoh = (overlapoh + 0x3ff) & ~0x1ff;
// verify filter
ft.verifyUnfilter();
#else
// new version using compressWithFilters()
// prepare packheader
ph.u_len = usize;
ph.filter = 0;
// prepare filter
Filter ft(opt->level);
ft.buf_len = usize - data->size;
ft.addvalue = text->vaddr & ~0x1ff;
// prepare other settings
const unsigned overlap_range = 512;
unsigned overlapoh;
int strategy = -1; // try the first working filter
if (opt->filter >= 0 && isValidFilter(opt->filter))
// try opt->filter or 0 if that fails
strategy = -2;
else if (opt->all_filters)
// choose best from all available filters
strategy = 0;
compressWithFilters(&ft, &overlapoh, overlap_range, strategy);
overlapoh = (overlapoh + 0x3ff) & ~0x1ff;
#endif
// patch coff header #2
const unsigned lsize = getLoaderSize();
text->size = lsize; // new size of .text
data->size = ph.c_len; // new size of .data
if (bss->size < overlapoh) // give it a .bss
bss->size = overlapoh;
text->scnptr = sizeof(coff_hdr);
data->scnptr = text->scnptr + text->size;
data->vaddr = bss->vaddr + ((data->scnptr + data->size) & 0x1ff) - data->size + overlapoh - 0x200;
coff_hdr.f_nscns = 3;
// prepare loader
MemBuffer loader(lsize);
memcpy(loader,getLoader(),lsize);
// patch loader
putPackHeader(loader,lsize);
patch_le32(loader,lsize,"ENTR",coff_hdr.a_entry);
if (ft.id)
{
assert(ft.calls > 0);
if (ft.id > 0x20)
patch_le16(loader,lsize,"??",'?' + (ft.cto << 8));
patch_le32(loader,lsize,"TEXL",(ft.id & 0xf) % 3 == 0 ? ft.calls :
ft.lastcall - ft.calls * 4);
}
patch_le32(loader,lsize,"BSSL",overlapoh/4);
assert(bss->vaddr == ((size + 0x1ff) &~ 0x1ff) + (text->vaddr &~ 0x1ff));
patch_le32(loader,lsize,"OUTP",text->vaddr &~ 0x1ff);
patch_le32(loader,lsize,"INPP",data->vaddr);
// patch coff header #3
text->vaddr = sizeof(coff_hdr);
coff_hdr.a_entry = sizeof(coff_hdr) + getLoaderSection("DJ2MAIN1");
bss->vaddr += overlapoh;
bss->size -= overlapoh;
// because of a feature (bug?) in stub.asm we need some padding
memcpy(obuf+data->size,"UPX",3);
data->size = ALIGN_UP(data->size,4);
// write coff header, loader and compressed file
fo->write(&coff_hdr,sizeof(coff_hdr));
fo->write(loader,lsize);
fo->write(obuf,data->size);
// verify
verifyOverlappingDecompression(&obuf, overlapoh);
// handle overlay
// FIXME: only Allegro pakfiles are supported
handle_allegropak(fi,fo);
}
/*************************************************************************
//
**************************************************************************/
int PackDjgpp2::canUnpack()
{
if (!readFileHeader())
return false;
if (is_dlm(fi,coff_offset))
throwCantUnpack("can't handle DLM");
return readPackHeader(1024, coff_offset) ? 1 : -1;
}
/*************************************************************************
//
**************************************************************************/
void PackDjgpp2::unpack(OutputFile *fo)
{
handleStub(fo);
ibuf.alloc(ph.c_len);
obuf.allocForUncompression(ph.u_len);
fi->seek(coff_offset + ph.buf_offset + ph.getPackHeaderSize(),SEEK_SET);
fi->readx(ibuf,ph.c_len);
// decompress
decompress(ibuf,obuf);
// unfilter
if (ph.filter)
{
memcpy(&coff_hdr,obuf,sizeof(coff_hdr));
Filter ft(ph.level);
ft.init(ph.filter, text->vaddr &~ 0x1ff);
ft.cto = (unsigned char) ph.filter_cto;
if (ph.version < 11)
{
unsigned char ctobuf[4];
fi->readx(ctobuf, 4);
ft.cto = (unsigned char) (get_le32(ctobuf) >> 24);
}
ft.unfilter(obuf, ph.u_len - data->size);
}
// fixup for the aligning bug in strip 2.8+
text = ((coff_header_t*) (unsigned char *) obuf)->sh;
data = text + 1;
text->scnptr &= 0x1ff;
data->scnptr = text->scnptr + text->size;
// write decompressed file
if (fo)
{
fo->write(obuf,ph.u_len);
handle_allegropak(fi,fo);
}
}
/*
vi:ts=4:et
*/