upx/src/packer_r.cpp
2024-03-25 11:07:40 +01:00

177 lines
6.2 KiB
C++

/* packer_r.cpp -- Packer relocation handling
This file is part of the UPX executable compressor.
Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2024 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> <ezerotven+github@gmail.com>
*/
#include "conf.h"
#include "packer.h"
/*************************************************************************
// sort and delta-compress relocations with optional bswap within image
// returns number of **bytes** written to 'out'
**************************************************************************/
/*static*/
unsigned Packer::optimizeReloc(unsigned relocnum, SPAN_P(byte) relocs, SPAN_S(byte) out,
SPAN_P(byte) image, unsigned image_size, int bits, bool bswap,
int *big) {
assert(bits == 32 || bits == 64);
mem_size_assert(1, image_size);
#if WITH_XSPAN >= 2
ptr_check_no_overlap(relocs.data(), relocs.size_bytes(), image.data(image_size), image_size,
out.data(), out.size_bytes());
#endif
SPAN_S_VAR(byte, fix, out);
*big = 0;
if (opt->exact)
throwCantPackExact();
if (relocnum == 0)
return 0;
upx_qsort(raw_bytes(relocs, 4 * relocnum), relocnum, 4, le32_compare);
if (0) {
printf("optimizeReloc: u_reloc %9u checksum=0x%08x\n", 4 * relocnum,
upx_adler32(relocs, 4 * relocnum));
printf("optimizeReloc: u_image %9u checksum=0x%08x\n", image_size,
upx_adler32(image, image_size));
}
unsigned pc = (unsigned) -4;
for (unsigned i = 0; i < relocnum; i++) {
unsigned delta = get_le32(relocs + i * 4) - pc;
if (delta == 0)
continue;
else if ((int) delta < 4)
throwCantPack("overlapping fixups");
else if (delta < 0xf0)
*fix++ = (byte) delta;
else if (delta < 0x100000) {
*fix++ = (byte) (0xf0 + (delta >> 16));
*fix++ = (byte) delta;
*fix++ = (byte) (delta >> 8);
} else {
*big = 1;
*fix++ = 0xf0;
*fix++ = 0;
*fix++ = 0;
set_le32(fix, delta);
fix += 4;
}
pc += delta;
if (pc + 4 > image_size)
throwCantPack("bad reloc[%#x] = %#x", i, pc);
if (bswap) {
if (bits == 32)
set_be32(image + pc, get_le32(image + pc));
else
set_be64(image + pc, get_le64(image + pc));
}
}
*fix++ = 0; // end marker
const unsigned bytes = ptr_udiff_bytes(fix, out);
if (0) {
printf("optimizeReloc: c_reloc %9u checksum=0x%08x\n", bytes, upx_adler32(out, bytes));
printf("optimizeReloc: c_image %9u checksum=0x%08x\n", image_size,
upx_adler32(image, image_size));
}
return bytes;
}
/*************************************************************************
// delta-decompress relocations
// advances 'in'
// allocates 'out' and returns number of **relocs** written to 'out'
**************************************************************************/
/*static*/
unsigned Packer::unoptimizeReloc(SPAN_S(const byte) & in, MemBuffer &out, SPAN_P(byte) image,
unsigned image_size, int bits, bool bswap) {
assert(bits == 32 || bits == 64);
mem_size_assert(1, image_size);
#if WITH_XSPAN >= 2
ptr_check_no_overlap(in.data(), in.size_bytes(), image.data(image_size), image_size);
#endif
SPAN_S_VAR(const byte, fix, in);
// count
unsigned relocnum = 0;
for (fix = in; *fix; fix++, relocnum++) {
if (*fix >= 0xf0) {
if (*fix == 0xf0 && get_le16(fix + 1) == 0)
fix += 4;
fix += 2;
}
}
NO_fprintf(stderr, "relocnum=%x\n", relocnum);
if (0) {
const unsigned bytes = ptr_udiff_bytes(fix + 1, in);
printf("unoptimizeReloc: c_reloc %9u checksum=0x%08x\n", bytes, upx_adler32(in, bytes));
printf("unoptimizeReloc: c_image %9u checksum=0x%08x\n", image_size,
upx_adler32(image, image_size));
}
out.alloc(mem_size(4, relocnum + 1)); // one extra entry
SPAN_S_VAR(LE32, relocs, out);
fix = in;
unsigned pc = (unsigned) -4;
for (unsigned i = 0; i < relocnum; i++) {
unsigned delta;
if (*fix < 0xf0)
delta = *fix++;
else {
delta = (*fix & 0x0f) * 0x10000 + get_le16(fix + 1);
fix += 3;
if (delta == 0) {
delta = get_le32(fix);
fix += 4;
}
}
if ((int) delta < 4)
throwCantUnpack("overlapping fixups");
pc += delta;
if (pc + 4 > image_size)
throwCantUnpack("bad reloc[%#x] = %#x", i, pc);
*relocs++ = pc;
if (bswap) {
if (bits == 32)
set_be32(image + pc, get_le32(image + pc));
else
set_be64(image + pc, get_le64(image + pc));
}
}
in = fix + 1; // advance
assert(relocnum == ptr_udiff_bytes(relocs, raw_bytes(out, 0)) / 4);
if (0) {
printf("unoptimizeReloc: u_reloc %9u checksum=0x%08x\n", 4 * relocnum,
upx_adler32(out, 4 * relocnum));
printf("unoptimizeReloc: u_image %9u checksum=0x%08x\n", image_size,
upx_adler32(image, image_size));
}
return relocnum;
}
/* vim:set ts=4 sw=4 et: */