Coverage Report

Created: 2026-01-09 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/packmast.cpp
Line
Count
Source
1
/* packmast.cpp --
2
3
   This file is part of the UPX executable compressor.
4
5
   Copyright (C) Markus Franz Xaver Johannes Oberhumer
6
   Copyright (C) Laszlo Molnar
7
   All Rights Reserved.
8
9
   UPX and the UCL library are free software; you can redistribute them
10
   and/or modify them under the terms of the GNU General Public License as
11
   published by the Free Software Foundation; either version 2 of
12
   the License, or (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program; see the file COPYING.
21
   If not, write to the Free Software Foundation, Inc.,
22
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24
   Markus F.X.J. Oberhumer              Laszlo Molnar
25
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
26
 */
27
28
// dispatch to a concrete subclass of class PackerBase; see work.cpp
29
30
#include "conf.h"
31
#include "file.h"
32
#include "packmast.h"
33
#include "packer.h"
34
35
#include "lefile.h"
36
#include "pefile.h"
37
#include "p_elf.h"
38
#include "p_unix.h"
39
40
#include "p_com.h"
41
#include "p_djgpp2.h"
42
#include "p_exe.h"
43
#include "p_lx_elf.h"
44
#include "p_lx_exc.h"
45
#include "p_lx_interp.h"
46
#include "p_lx_sh.h"
47
#include "p_mach.h"
48
#include "p_ps1.h"
49
#include "p_sys.h"
50
#include "p_tmt.h"
51
#include "p_tos.h"
52
#include "p_vmlinx.h"
53
#include "p_vmlinz.h"
54
#include "p_w32pe_i386.h"
55
#include "p_w64pe_amd64.h"
56
#include "p_w64pe_arm64.h"
57
#include "p_wcle.h"
58
#include "p_wince_arm.h"
59
60
/*************************************************************************
61
//
62
**************************************************************************/
63
64
3.84k
PackMaster::PackMaster(InputFile *f, Options *o) noexcept : fi(f) {
65
    // replace global options with local options
66
3.84k
    if (o != nullptr) {
67
#if WITH_THREADS
68
        // TODO later: check for possible "noexcept" violation here
69
        std::lock_guard<std::mutex> lock(opt_lock_mutex);
70
#endif
71
3.84k
        saved_opt = o;
72
3.84k
        memcpy(&this->local_options, o, sizeof(*o)); // struct copy
73
3.84k
        opt = &this->local_options;
74
3.84k
    }
75
3.84k
}
76
77
3.84k
PackMaster::~PackMaster() noexcept {
78
3.84k
    upx::owner_delete(packer);
79
    // restore global options
80
3.84k
    if (saved_opt != nullptr) {
81
#if WITH_THREADS
82
        // TODO later: check for possible "noexcept" violation here
83
        std::lock_guard<std::mutex> lock(opt_lock_mutex);
84
#endif
85
3.84k
        opt = saved_opt;
86
3.84k
        saved_opt = nullptr;
87
3.84k
    }
88
3.84k
}
89
90
/*************************************************************************
91
//
92
**************************************************************************/
93
94
0
static noinline tribool try_can_pack(PackerBase *pb, void *user) may_throw {
95
0
    InputFile *f = (InputFile *) user;
96
0
    try {
97
0
        pb->initPackHeader();
98
0
        f->seek(0, SEEK_SET);
99
0
        tribool r = pb->canPack();
100
0
        if (r) {
101
0
            if (opt->cmd == CMD_COMPRESS)
102
0
                pb->updatePackHeader();
103
0
            f->seek(0, SEEK_SET);
104
0
            return true; // success
105
0
        }
106
0
        if (r.isThird()) // aka "-1"
107
0
            return r;    // canPack() says the format is recognized and we should fail early
108
0
    } catch (const IOException &) {
109
        // ignored
110
0
    }
111
0
    return false;
112
0
}
113
114
113k
static noinline tribool try_can_unpack(PackerBase *pb, void *user) may_throw {
115
113k
    InputFile *f = (InputFile *) user;
116
113k
    try {
117
113k
        pb->initPackHeader();
118
113k
        f->seek(0, SEEK_SET);
119
113k
        tribool r = pb->canUnpack();
120
113k
        if (r) {
121
977
            f->seek(0, SEEK_SET);
122
977
            return true; // success
123
977
        }
124
112k
        if (r.isThird()) // aka "-1"
125
29
            return r;    // canUnpack() says the format is recognized and we should fail early
126
112k
    } catch (const IOException &) {
127
        // ignored
128
6.45k
    }
129
111k
    return false;
130
113k
}
131
132
/*************************************************************************
133
//
134
**************************************************************************/
135
136
/*static*/
137
PackerBase *PackMaster::visitAllPackers(visit_func_t func, InputFile *f, const Options *o,
138
3.84k
                                        void *user) may_throw {
139
3.84k
#define VISIT(Klass)                                                                               \
140
146k
    do {                                                                                           \
141
146k
        static_assert(std::is_class_v<Klass>);                                                     \
142
146k
        static_assert(std::is_nothrow_destructible_v<Klass>);                                      \
143
146k
        auto pb = std::unique_ptr<PackerBase>(new Klass(f));                                       \
144
146k
        if (o->debug.debug_level)                                                                  \
145
146k
            fprintf(stderr, "visitAllPackers: (ver=%d, fmt=%3d) %s\n", pb->getVersion(),           \
146
0
                    pb->getFormat(), #Klass);                                                      \
147
146k
        pb->assertPacker();                                                                        \
148
146k
        tribool r = func(pb.get(), user);                                                          \
149
146k
        if (r)                                                                                     \
150
146k
            return pb.release(); /* success */                                                     \
151
146k
        if (r.isThird())                                                                           \
152
145k
            return nullptr; /* stop and fail early */                                              \
153
145k
    } while (0)
154
155
    // NOTE: order of tries is important !!!
156
157
    //
158
    // .exe
159
    //
160
3.84k
    if (!o->dos_exe.force_stub) {
161
        // dos32
162
3.84k
        VISIT(PackDjgpp2);
163
3.84k
        VISIT(PackTmt);
164
3.83k
        VISIT(PackWcle);
165
        // Windows
166
        // VISIT(PackW64PeArm64EC); // NOT YET IMPLEMENTED
167
        // VISIT(PackW64PeArm64); // NOT YET IMPLEMENTED
168
3.81k
        VISIT(PackW64PeAmd64);
169
3.80k
        VISIT(PackW32PeI386);
170
3.77k
        VISIT(PackWinCeArm);
171
3.77k
    }
172
3.76k
    VISIT(PackExe); // dos/exe
173
174
    //
175
    // linux kernel
176
    //
177
3.75k
    VISIT(PackVmlinuxARMEL);
178
3.75k
    VISIT(PackVmlinuxARMEB);
179
3.75k
    VISIT(PackVmlinuxPPC32);
180
3.75k
    VISIT(PackVmlinuxPPC64LE);
181
3.75k
    VISIT(PackVmlinuxAMD64);
182
3.75k
    VISIT(PackVmlinuxI386);
183
3.75k
#if (WITH_ZLIB)
184
3.75k
    VISIT(PackVmlinuzI386);
185
3.74k
    VISIT(PackBvmlinuzI386);
186
3.74k
    VISIT(PackVmlinuzARMEL);
187
3.74k
#endif
188
189
    //
190
    // linux
191
    //
192
3.74k
    if (!o->o_unix.force_execve) {
193
3.43k
        if (o->o_unix.use_ptinterp) {
194
0
            VISIT(PackLinuxElf32x86interp);
195
0
        }
196
3.43k
        VISIT(PackFreeBSDElf32x86);
197
3.43k
        VISIT(PackNetBSDElf32x86);
198
3.43k
        VISIT(PackOpenBSDElf32x86);
199
3.43k
        VISIT(PackLinuxElf32x86);
200
3.28k
        VISIT(PackLinuxElf64amd);
201
3.13k
        VISIT(PackLinuxElf32armLe);
202
3.11k
        VISIT(PackLinuxElf32armBe);
203
3.06k
        VISIT(PackLinuxElf64arm);
204
3.02k
        VISIT(PackLinuxElf64riscv64);
205
3.02k
        VISIT(PackLinuxElf32ppc);
206
2.99k
        VISIT(PackLinuxElf64ppc);
207
2.98k
        VISIT(PackLinuxElf64ppcle);
208
2.96k
        VISIT(PackLinuxElf32mipsel);
209
2.93k
        VISIT(PackLinuxElf32mipseb);
210
2.91k
        VISIT(PackLinuxI386sh);
211
2.91k
    }
212
3.21k
    VISIT(PackBSDI386);
213
3.21k
    VISIT(PackMachFat);   // cafebabe conflict
214
3.21k
    VISIT(PackLinuxI386); // cafebabe conflict
215
216
    //
217
    // Mach (Darwin / macOS)
218
    //
219
3.03k
    VISIT(PackDylibAMD64);
220
3.03k
    VISIT(PackMachPPC32); // TODO: this works with upx 3.91..3.94 but got broken in 3.95; FIXME
221
3.01k
    VISIT(PackMachI386);
222
3.01k
    VISIT(PackMachAMD64);
223
2.96k
    VISIT(PackMachARMEL);
224
2.95k
    VISIT(PackMachARM64EL);
225
226
    // 2010-03-12  omit these because PackMachBase<T>::pack4dylib (p_mach.cpp)
227
    // does not understand what the Darwin (Apple Mac OS X) dynamic loader
228
    // assumes about .dylib file structure.
229
    //   VISIT(PackDylibI386);
230
    //   VISIT(PackDylibPPC32);
231
232
    //
233
    // misc
234
    //
235
2.86k
    VISIT(PackTos); // atari/tos
236
2.86k
    VISIT(PackPs1); // ps1/exe
237
2.86k
    VISIT(PackSys); // dos/sys
238
2.85k
    VISIT(PackCom); // dos/com
239
240
2.84k
    return nullptr;
241
2.85k
#undef VISIT
242
2.85k
}
243
244
0
/*static*/ PackerBase *PackMaster::getPacker(InputFile *f) may_throw {
245
0
    PackerBase *pb = visitAllPackers(try_can_pack, f, opt, f);
246
0
    if (!pb)
247
0
        throwUnknownExecutableFormat();
248
0
    return pb;
249
0
}
250
251
3.84k
/*static*/ PackerBase *PackMaster::getUnpacker(InputFile *f) may_throw {
252
3.84k
    PackerBase *pb = visitAllPackers(try_can_unpack, f, opt, f);
253
3.84k
    if (!pb)
254
968
        throwNotPacked();
255
2.88k
    return pb;
256
3.84k
}
257
258
/*************************************************************************
259
// delegation from work.cpp
260
**************************************************************************/
261
262
0
void PackMaster::pack(OutputFile *fo) may_throw {
263
0
    assert(packer == nullptr);
264
0
    packer = getPacker(fi);
265
0
    packer->doPack(fo);
266
0
}
267
268
0
void PackMaster::unpack(OutputFile *fo) may_throw {
269
0
    assert(packer == nullptr);
270
0
    packer = getUnpacker(fi);
271
0
    packer->doUnpack(fo);
272
0
}
273
274
3.84k
void PackMaster::test() may_throw {
275
3.84k
    assert(packer == nullptr);
276
0
    packer = getUnpacker(fi);
277
3.84k
    packer->doTest();
278
3.84k
}
279
280
0
void PackMaster::list() may_throw {
281
0
    assert(packer == nullptr);
282
0
    packer = getUnpacker(fi);
283
0
    packer->doList();
284
0
}
285
286
0
void PackMaster::fileInfo() may_throw {
287
0
    assert(packer == nullptr);
288
0
    packer = visitAllPackers(try_can_unpack, fi, opt, fi);
289
0
    if (!packer)
290
0
        packer = visitAllPackers(try_can_pack, fi, opt, fi);
291
0
    if (!packer)
292
0
        throwUnknownExecutableFormat(nullptr, true); // make a warning here
293
0
    packer->doFileInfo();
294
0
}
295
296
/* vim:set ts=4 sw=4 et: */