177 lines
6.2 KiB
C++
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: */
|