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

src: class Packer decomposition, introduce PackerBase

This commit is contained in:
Markus F.X.J. Oberhumer 2023-08-10 20:40:27 +02:00
parent a66ee9fafd
commit c65c882ecc
8 changed files with 130 additions and 111 deletions

View File

@ -103,17 +103,16 @@ struct PackerNames {
size_t names_count;
const Options *o;
PackerNames() : names_count(0), o(nullptr) {}
void add(const Packer *packer) {
void add(const PackerBase *pb) {
assert(names_count < 64);
names[names_count].fname = packer->getFullName(o);
names[names_count].sname = packer->getName();
names[names_count].fname = pb->getFullName(o);
names[names_count].sname = pb->getName();
names_count++;
}
static Packer *visit(Packer *packer, void *user) {
static bool visit(PackerBase *pb, void *user) {
PackerNames *self = (PackerNames *) user;
self->add(packer);
delete packer;
return nullptr;
self->add(pb);
return false;
}
static int __acc_cdecl_qsort cmp_fname(const void *a, const void *b) {
return strcmp(((const Entry *) a)->fname, ((const Entry *) b)->fname);
@ -129,7 +128,7 @@ static void list_all_packers(FILE *f, int verbose) {
o.reset();
PackerNames pn;
pn.o = &o;
PackMaster::visitAllPackers(PackerNames::visit, nullptr, &o, &pn);
(void) PackMaster::visitAllPackers(PackerNames::visit, nullptr, &o, &pn);
qsort(pn.names, pn.names_count, sizeof(PackerNames::Entry), PackerNames::cmp_fname);
size_t pos = 0;
for (size_t i = 0; i < pn.names_count; ++i) {

View File

@ -36,7 +36,7 @@
//
**************************************************************************/
Packer::Packer(InputFile *f) : fi(f) {
Packer::Packer(InputFile *f) : PackerBase(f) {
if (fi != nullptr)
file_size = fi->st_size();
mem_size_assert(1, file_size_u);

View File

@ -31,6 +31,7 @@
class InputFile;
class OutputFile;
class PackerBase;
class Packer;
class UiPacker;
class Filter;
@ -41,9 +42,10 @@ class Filter;
**************************************************************************/
class PackHeader final {
friend class PackerBase;
friend class Packer;
// these are strictly private to friend Packer
// these are strictly private to friends PackerBase and Packer
explicit PackHeader() noexcept;
void putPackHeader(SPAN_S(byte) p);
bool decodePackHeaderFromBuf(SPAN_S(const byte) b, int blen);
@ -97,21 +99,17 @@ bool ph_testOverlappingDecompression(const PackHeader &ph, SPAN_P(const byte) bu
unsigned overlap_overhead);
/*************************************************************************
// abstract base class for packers
// purely abstract minimal base class for all packers
//
// FIXME later: this class is way too fat and badly needs a decomposition
// clients: PackMaster, UiPacker
**************************************************************************/
class Packer {
class PackerBase {
friend class UiPacker;
protected:
explicit Packer(InputFile *f);
explicit PackerBase(InputFile *f) noexcept : fi(f) {}
public:
virtual ~Packer() noexcept;
virtual void assertPacker() const;
virtual ~PackerBase() noexcept {}
// getVersion() enables detecting forward incompatibility of unpack()
// by old upx when newer upx changes the format of compressed output.
virtual int getVersion() const = 0;
@ -122,14 +120,55 @@ public:
virtual const int *getCompressionMethods(int method, int level) const = 0;
virtual const int *getFilters() const = 0;
// canPack() should throw a cantPackException eplaining why it
// cannot pack a recognized format.
virtual bool canPack() = 0;
// canUnpack() can return -1 meaning "format recognized, but file
// is definitely not packed". See packmast.cpp try_unpack().
virtual int canUnpack() = 0;
// PackMaster entries
void initPackHeader();
void updatePackHeader();
void doPack(OutputFile *fo);
void doUnpack(OutputFile *fo);
void doTest();
void doList();
void doFileInfo();
virtual void assertPacker() const = 0;
virtual void initPackHeader() = 0;
virtual void updatePackHeader() = 0;
virtual void doPack(OutputFile *fo) = 0;
virtual void doUnpack(OutputFile *fo) = 0;
virtual void doTest() = 0;
virtual void doList() = 0;
virtual void doFileInfo() = 0;
protected:
InputFile *fi = nullptr;
union { // unnamed union
upx_int64_t file_size = 0; // must get set by constructor
upx_uint64_t file_size_u; // explicitly unsigned
};
PackHeader ph = PackHeader{}; // must be filled by canUnpack()
};
/*************************************************************************
// abstract default implementation class for packers
//
// Packer can be viewed as "PackerDefaultImplV1"; it is grown historically
// and still would benefit from a decomposition
**************************************************************************/
class Packer : public PackerBase {
protected:
explicit Packer(InputFile *f);
public:
virtual ~Packer() noexcept;
// PackMaster entries
virtual void assertPacker() const override;
virtual void initPackHeader() final override;
virtual void updatePackHeader() final override;
virtual void doPack(OutputFile *fo) final override;
virtual void doUnpack(OutputFile *fo) final override;
virtual void doTest() final override;
virtual void doList() final override;
virtual void doFileInfo() final override;
// unpacker capabilities
virtual bool canUnpackVersion(int version) const { return (version >= 8); }
@ -148,14 +187,6 @@ protected:
virtual void list();
virtual void fileInfo();
public:
// canPack() should throw a cantPackException eplaining why it
// cannot pack a recognized format.
virtual bool canPack() = 0;
// canUnpack() can return -1 meaning "format recognized, but file
// is definitely not packed". See packmast.cpp try_unpack().
virtual int canUnpack() = 0;
protected:
// main compression drivers
bool compress(SPAN_P(byte) i_ptr, unsigned i_len, SPAN_P(byte) o_ptr,
@ -327,14 +358,8 @@ protected:
protected:
const N_BELE_RTP::AbstractPolicy *bele = nullptr; // target endianness
InputFile *fi = nullptr;
union { // unnamed union
upx_int64_t file_size = 0; // will get set by constructor
upx_uint64_t file_size_u; // explicitly unsigned
};
PackHeader ph = PackHeader{}; // must be filled by canUnpack()
// PackHeader
int ph_format = -1;
int ph_version = -1;

View File

@ -25,6 +25,8 @@
<markus@oberhumer.com> <ezerotven+github@gmail.com>
*/
#include "headers.h"
#include <memory>
#include "conf.h"
#include "file.h"
#include "packmast.h"
@ -87,35 +89,32 @@ PackMaster::~PackMaster() noexcept {
//
**************************************************************************/
static Packer *try_can_pack(Packer *p, void *user) {
static bool try_can_pack(PackerBase *pb, void *user) may_throw {
InputFile *f = (InputFile *) user;
try {
p->initPackHeader();
pb->initPackHeader();
f->seek(0, SEEK_SET);
if (p->canPack()) {
if (pb->canPack()) {
if (opt->cmd == CMD_COMPRESS)
p->updatePackHeader();
pb->updatePackHeader();
f->seek(0, SEEK_SET);
return p;
return true;
}
} catch (const IOException &) {
} catch (...) {
delete p;
throw;
// ignored
}
delete p;
return nullptr;
return false;
}
static Packer *try_can_unpack(Packer *p, void *user) {
static bool try_can_unpack(PackerBase *pb, void *user) may_throw {
InputFile *f = (InputFile *) user;
try {
p->initPackHeader();
pb->initPackHeader();
f->seek(0, SEEK_SET);
int r = p->canUnpack();
int r = pb->canUnpack();
if (r > 0) {
f->seek(0, SEEK_SET);
return p;
return true;
}
if (r < 0) {
// FIXME - could stop testing all other unpackers at this time
@ -123,12 +122,8 @@ static Packer *try_can_unpack(Packer *p, void *user) {
}
} catch (const IOException &) {
// ignored
} catch (...) {
delete p;
throw;
}
delete p;
return nullptr;
return false;
}
/*************************************************************************
@ -136,19 +131,18 @@ static Packer *try_can_unpack(Packer *p, void *user) {
**************************************************************************/
/*static*/
Packer *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const Options *o, void *user) {
PackerBase *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const Options *o,
void *user) may_throw {
#define D(Klass) \
ACC_BLOCK_BEGIN \
COMPILE_TIME_ASSERT(std::is_nothrow_destructible_v<Klass>) \
Klass *kp = new Klass(f); \
kp->assertPacker(); \
auto pb = std::unique_ptr<PackerBase>(new Klass(f)); \
pb->assertPacker(); \
if (o->debug.debug_level) \
fprintf(stderr, "visitAllPackers: (ver=%d, fmt=%3d) %s\n", kp->getVersion(), \
kp->getFormat(), #Klass); \
Packer *p = func(kp, user); \
if (p != nullptr) \
return p; \
fprintf(stderr, "visitAllPackers: (ver=%d, fmt=%3d) %s\n", pb->getVersion(), \
pb->getFormat(), #Klass); \
if (func(pb.get(), user)) \
return pb.release(); \
ACC_BLOCK_END
// NOTE: order of tries is important !!!
@ -235,18 +229,18 @@ Packer *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const Optio
#undef D
}
/*static*/ Packer *PackMaster::getPacker(InputFile *f) {
Packer *p = visitAllPackers(try_can_pack, f, opt, f);
if (!p)
/*static*/ PackerBase *PackMaster::getPacker(InputFile *f) {
PackerBase *pb = visitAllPackers(try_can_pack, f, opt, f);
if (!pb)
throwUnknownExecutableFormat();
return p;
return pb;
}
/*static*/ Packer *PackMaster::getUnpacker(InputFile *f) {
Packer *p = visitAllPackers(try_can_unpack, f, opt, f);
if (!p)
/*static*/ PackerBase *PackMaster::getUnpacker(InputFile *f) {
PackerBase *pb = visitAllPackers(try_can_unpack, f, opt, f);
if (!pb)
throwNotPacked();
return p;
return pb;
}
/*************************************************************************

View File

@ -27,12 +27,12 @@
#pragma once
class Packer;
class PackerBase;
class InputFile;
class OutputFile;
/*************************************************************************
// dispatch to a concrete subclass of class Packer; see work.cpp
// dispatch to a concrete subclass of class PackerBase; see work.cpp
**************************************************************************/
class PackMaster final {
@ -46,15 +46,16 @@ public:
void list();
void fileInfo();
typedef Packer *(*visit_func_t)(Packer *p, void *user);
static Packer *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user);
typedef bool (*visit_func_t)(PackerBase *pb, void *user);
static PackerBase *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user)
may_throw;
private:
OwningPointer(Packer) packer = nullptr; // owner
InputFile *fi = nullptr; // reference
OwningPointer(PackerBase) packer = nullptr; // owner
InputFile *fi = nullptr; // reference
static Packer *getPacker(InputFile *f);
static Packer *getUnpacker(InputFile *f);
static PackerBase *getPacker(InputFile *f);
static PackerBase *getUnpacker(InputFile *f);
// setup local options for each file
Options local_options;

View File

@ -156,7 +156,7 @@ static const char *mkline(upx_uint64_t fu_len, upx_uint64_t fc_len, upx_uint64_t
//
**************************************************************************/
UiPacker::UiPacker(const Packer *p_) : p(p_) {
UiPacker::UiPacker(const PackerBase *pb_) : pb(pb_) {
static upx_std_once_flag init_done;
upx_std_call_once(init_done, init_global_constants);
@ -195,12 +195,12 @@ UiPacker::~UiPacker() noexcept {
void UiPacker::printInfo(int nl) {
if (opt->all_methods && s->total_passes > 1)
con_fprintf(stdout, "Compressing %s [%s]%s", p->fi->getName(), p->getName(),
con_fprintf(stdout, "Compressing %s [%s]%s", pb->fi->getName(), pb->getName(),
nl ? "\n" : "");
else {
char method_name[32 + 1];
set_method_name(method_name, sizeof(method_name), p->ph.method, p->ph.level);
con_fprintf(stdout, "Compressing %s [%s, %s]%s", p->fi->getName(), p->getName(),
set_method_name(method_name, sizeof(method_name), pb->ph.method, pb->ph.level);
con_fprintf(stdout, "Compressing %s [%s, %s]%s", pb->fi->getName(), pb->getName(),
method_name, nl ? "\n" : "");
}
}
@ -240,7 +240,7 @@ void UiPacker::startCallback(unsigned u_len, unsigned step, int pass, int total_
cb.user = this; // parameter for static function UiPacker::progress_callback()
if (s->mode == M_CB_TERM) {
const char *fname = fn_basename(p->fi->getName());
const char *fname = fn_basename(pb->fi->getName());
int l = (int) strlen(fname);
if (l > 0 && l <= 30) {
strcpy(&s->msg_buf[s->bar_pos], fname);
@ -462,13 +462,13 @@ void UiPacker::uiPackEnd(const OutputFile *fo) {
printClearLine(stdout);
}
const char *name = p->fi->getName();
const char *name = pb->fi->getName();
if (opt->output_name)
name = opt->output_name;
else if (opt->to_stdout)
name = "<stdout>";
con_fprintf(stdout, "%s\n",
mkline(p->ph.u_file_size, fo->st_size(), p->ph.u_len, p->ph.c_len, p->getName(),
mkline(pb->ph.u_file_size, fo->st_size(), pb->ph.u_len, pb->ph.c_len, pb->getName(),
fn_basename(name)));
printSetNl(0);
}
@ -493,14 +493,14 @@ void UiPacker::uiUnpackEnd(const OutputFile *fo) {
if (s->mode == M_QUIET)
return;
const char *name = p->fi->getName();
const char *name = pb->fi->getName();
if (opt->output_name)
name = opt->output_name;
else if (opt->to_stdout)
name = "<stdout>";
con_fprintf(stdout, "%s\n",
mkline(fo->getBytesWritten(), p->file_size, p->ph.u_len, p->ph.c_len, p->getName(),
fn_basename(name), true));
mkline(fo->getBytesWritten(), pb->file_size, pb->ph.u_len, pb->ph.c_len,
pb->getName(), fn_basename(name), true));
printSetNl(0);
}
@ -516,10 +516,10 @@ void UiPacker::uiUnpackEnd(const OutputFile *fo) {
void UiPacker::uiListStart() { total_files++; }
void UiPacker::uiList() {
const char *name = p->fi->getName();
const char *name = pb->fi->getName();
con_fprintf(
stdout, "%s\n",
mkline(p->ph.u_file_size, p->file_size, p->ph.u_len, p->ph.c_len, p->getName(), name));
mkline(pb->ph.u_file_size, pb->file_size, pb->ph.u_len, pb->ph.c_len, pb->getName(), name));
printSetNl(0);
}
@ -545,7 +545,7 @@ void UiPacker::uiTestStart() {
total_files++;
if (opt->verbose >= 1) {
con_fprintf(stdout, "testing %s ", p->fi->getName());
con_fprintf(stdout, "testing %s ", pb->fi->getName());
fflush(stdout);
printSetNl(1);
}
@ -570,16 +570,16 @@ bool UiPacker::uiFileInfoStart() {
total_files++;
int fg = con_fg(stdout, FG_CYAN);
con_fprintf(stdout, "%s [%s, %s]\n", p->fi->getName(), p->getFullName(opt), p->getName());
con_fprintf(stdout, "%s [%s, %s]\n", pb->fi->getName(), pb->getFullName(opt), pb->getName());
fg = con_fg(stdout, fg);
UNUSED(fg);
if (p->ph.c_len > 0) {
con_fprintf(stdout, " %8llu bytes", p->file_size_u);
if (pb->ph.c_len > 0) {
con_fprintf(stdout, " %8llu bytes", pb->file_size_u);
con_fprintf(stdout, ", compressed by UPX %d, method %d, level %d, filter 0x%02x/0x%02x\n",
p->ph.version, p->ph.method, p->ph.level, p->ph.filter, p->ph.filter_cto);
pb->ph.version, pb->ph.method, pb->ph.level, pb->ph.filter, pb->ph.filter_cto);
return false;
} else {
con_fprintf(stdout, " %8llu bytes", p->file_size_u);
con_fprintf(stdout, " %8llu bytes", pb->file_size_u);
con_fprintf(stdout, ", not compressed by UPX\n");
return true;
}
@ -624,10 +624,10 @@ void UiPacker::uiFileInfoEnd() { uiUpdate(); }
}
void UiPacker::uiUpdate(upx_off_t fc_len, upx_off_t fu_len) {
update_fc_len = (fc_len >= 0) ? fc_len : p->file_size_u;
update_fu_len = (fu_len >= 0) ? fu_len : p->ph.u_file_size;
update_c_len = p->ph.c_len;
update_u_len = p->ph.u_len;
update_fc_len = (fc_len >= 0) ? fc_len : pb->file_size_u;
update_fu_len = (fu_len >= 0) ? fu_len : pb->ph.u_file_size;
update_c_len = pb->ph.c_len;
update_u_len = pb->ph.u_len;
}
/*static*/ void UiPacker::uiConfirmUpdate() {

View File

@ -28,7 +28,7 @@
#pragma once
class OutputFile;
class Packer;
class PackerBase;
/*************************************************************************
//
@ -36,7 +36,7 @@ class Packer;
class UiPacker final {
public:
explicit UiPacker(const Packer *p_);
explicit UiPacker(const PackerBase *);
public:
virtual ~UiPacker() noexcept;
@ -84,7 +84,7 @@ public:
protected:
virtual void printInfo(int nl = 0);
const Packer *const p; // reference
const PackerBase *const pb; // reference, required
// callback
upx_callback_t cb = {};

View File

@ -27,7 +27,7 @@
// This file implements the central loop, and it uses class PackMaster to
// dispatch. PackMaster by itself will instantiate a concrete subclass
// of class Packer which then does the actual work.
// of class PackerBase which then does the actual work.
// And see p_com.cpp for a simple executable format.
#include "conf.h"