mirror of
https://github.com/upx/upx.git
synced 2024-11-23 04:39:59 +00:00
all: more cleanups; NFCI
This commit is contained in:
parent
809a1b3dae
commit
7e839e6962
@ -1,6 +1,6 @@
|
||||
# vim:set ft=yaml ts=2 sw=2 et:
|
||||
# Copyright (C) Markus Franz Xaver Johannes Oberhumer
|
||||
# for clang-tidy-16 from https://clang.llvm.org/extra/clang-tidy/
|
||||
# for clang-tidy from https://clang.llvm.org/extra/clang-tidy/
|
||||
---
|
||||
Checks: >
|
||||
-*,
|
||||
|
3
.clangd
3
.clangd
@ -1,6 +1,6 @@
|
||||
# vim:set ft=yaml ts=2 sw=2 et:
|
||||
# Copyright (C) Markus Franz Xaver Johannes Oberhumer
|
||||
# for clangd-17 from https://clangd.llvm.org/
|
||||
# for clangd from https://clangd.llvm.org/
|
||||
---
|
||||
# treat *.h files as C++ source code
|
||||
If:
|
||||
@ -17,6 +17,7 @@ If:
|
||||
CompileFlags:
|
||||
Add:
|
||||
- -std=gnu++17
|
||||
# -std=gnu++20 # requires clang >= 10.0
|
||||
---
|
||||
# common flags for all C/C++ files
|
||||
If:
|
||||
|
@ -15,8 +15,8 @@ trim_trailing_whitespace = true
|
||||
indent_size = 8
|
||||
indent_style = tab
|
||||
|
||||
[*.S]
|
||||
[{*.asm,*.S}]
|
||||
indent_size = 8
|
||||
|
||||
[*.yml]
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -16,8 +16,8 @@ env:
|
||||
UPX_CMAKE_BUILD_FLAGS: --verbose
|
||||
UPX_CMAKE_CONFIG_FLAGS: -Wdev --warn-uninitialized
|
||||
UPX_DEBUG_TEST_FLOAT_DIVISION_BY_ZERO: 1
|
||||
# 2024-03-16
|
||||
ZIG_DIST_VERSION: 0.12.0-dev.3322+a4508ad71
|
||||
# 2024-03-23
|
||||
ZIG_DIST_VERSION: 0.12.0-dev.3429+13a9d94a8
|
||||
|
||||
jobs:
|
||||
job-rebuild-and-verify-stubs:
|
||||
@ -473,7 +473,7 @@ jobs:
|
||||
- { zig_target: i386-linux-musl, qemu: qemu-i386 -cpu Conroe }
|
||||
# { zig_target: i386-linux-musl, qemu: qemu-i386 -cpu Conroe, zig_pic: -fPIE }
|
||||
- { zig_target: i386-windows-gnu }
|
||||
# mips and mipsel: bad hard-float code generation; see https://github.com/upx/upx/issues/788
|
||||
# mips and mipsel: bad hard-float code generation(??); or QEMU bug; or UPX bug; see https://github.com/upx/upx/issues/788
|
||||
- { zig_target: mips-linux-musl, zig_flags: -msoft-float, qemu: qemu-mips }
|
||||
- { zig_target: mipsel-linux-musl, zig_flags: -msoft-float, qemu: qemu-mipsel }
|
||||
- { zig_target: powerpc-linux-musl, qemu: qemu-ppc }
|
||||
|
12
Makefile
12
Makefile
@ -46,10 +46,10 @@ build/release: PHONY
|
||||
.SUFFIXES:
|
||||
|
||||
# shortcuts (all => debug + release)
|
||||
debug: build/debug
|
||||
release: build/release
|
||||
all build/all: build/debug build/release
|
||||
build/%/all: $$(dir $$@)debug $$(dir $$@)release ;
|
||||
debug: build/debug PHONY
|
||||
release: build/release PHONY
|
||||
all build/all: build/debug build/release PHONY
|
||||
build/%/all: $$(dir $$@)debug $$(dir $$@)release PHONY;
|
||||
|
||||
#
|
||||
# END of Makefile
|
||||
@ -63,9 +63,7 @@ include ./misc/make/Makefile-extra.mk
|
||||
endif
|
||||
|
||||
# developer convenience
|
||||
CTEST = ctest
|
||||
test:: $(.DEFAULT_GOAL) PHONY
|
||||
cd $(.DEFAULT_GOAL) && $(CTEST)
|
||||
test:: build/all+test PHONY
|
||||
ifneq ($(wildcard /usr/bin/env),) # need Unix utils like bash, perl, sed, xargs, etc.
|
||||
ifneq ($(wildcard ./misc/scripts/.),)
|
||||
check-whitespace clang-format run-testsuite run-testsuite-debug run-testsuite-release: src/Makefile PHONY
|
||||
|
@ -11,6 +11,23 @@ override check_undefined = $(foreach 1,$1,$(if $(filter undefined,$(origin $1)),
|
||||
$(call check_defined,run_config run_build)
|
||||
$(call check_undefined,run_config_and_build)
|
||||
|
||||
#***********************************************************************
|
||||
# build and test
|
||||
#***********************************************************************
|
||||
|
||||
CTEST = ctest
|
||||
|
||||
build/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST)
|
||||
build/%/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST)
|
||||
build/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST)
|
||||
build/%/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST)
|
||||
build/%/all+test: $$(dir $$@)debug+test $$(dir $$@)release+test PHONY ;
|
||||
|
||||
# shortcuts
|
||||
debug+test: build/debug+test PHONY
|
||||
release+test: build/release+test PHONY
|
||||
all+test build/all+test: build/debug+test build/release+test PHONY
|
||||
|
||||
#***********************************************************************
|
||||
# extra builds: some pre-defined build configurations
|
||||
#***********************************************************************
|
||||
|
29
src/Makefile
29
src/Makefile
@ -20,27 +20,22 @@ endif
|
||||
# NOTE that the top-level Makefile .DEFAULT_GOAL is build/release
|
||||
.DEFAULT_GOAL = build/all
|
||||
|
||||
build/debug: $(top_srcdir)/build/debug
|
||||
build/release: $(top_srcdir)/build/release
|
||||
|
||||
.NOTPARALLEL: # because the actual builds use "cmake --parallel"
|
||||
.PHONY: PHONY
|
||||
.SECONDEXPANSION:
|
||||
.SUFFIXES:
|
||||
|
||||
# shortcuts (all => debug + release)
|
||||
debug: build/debug
|
||||
release: build/release
|
||||
all build/all: build/debug build/release
|
||||
build/debug build/release build/all: PHONY; $(MAKE) -C $(top_srcdir) $@
|
||||
build/debug+test build/release+test build/all+test: PHONY; $(MAKE) -C $(top_srcdir) $@
|
||||
# shortcuts
|
||||
debug release all: PHONY; $(MAKE) -C $(top_srcdir) $@
|
||||
debug+test release+test all+test: PHONY; $(MAKE) -C $(top_srcdir) $@
|
||||
|
||||
# actual rules - redirect to top-level
|
||||
$(top_srcdir)/build/debug: PHONY; $(MAKE) -C $(top_srcdir) build/debug
|
||||
$(top_srcdir)/build/release: PHONY; $(MAKE) -C $(top_srcdir) build/release
|
||||
test:: build/all+test PHONY
|
||||
|
||||
# convenience
|
||||
CTEST = ctest
|
||||
test:: $(top_srcdir)/build/debug PHONY; cd $< && $(CTEST)
|
||||
test:: $(top_srcdir)/build/release PHONY; cd $< && $(CTEST)
|
||||
# OLD names [deprecated]
|
||||
$(top_srcdir)/build/debug: build/debug PHONY
|
||||
$(top_srcdir)/build/release: build/release PHONY
|
||||
|
||||
#***********************************************************************
|
||||
# make run-testsuite
|
||||
@ -65,15 +60,15 @@ endif
|
||||
# The actual (new) checksums are in ./tmp-upx-testsuite-*/testsuite_1/.sha256sums.recreate
|
||||
ifneq ($(wildcard $(upx_testsuite_SRCDIR)/files/packed/.),)
|
||||
ifneq ($(wildcard $(top_srcdir)/misc/testsuite/upx_testsuite_1.sh),)
|
||||
run-testsuite: run-testsuite-release
|
||||
run-testsuite: run-testsuite-release PHONY
|
||||
run-testsuite-%: export upx_testsuite_SRCDIR := $(upx_testsuite_SRCDIR)
|
||||
run-testsuite-debug: export upx_testsuite_BUILDDIR := ./tmp-upx-testsuite-debug
|
||||
run-testsuite-debug: export upx_exe := $(top_srcdir)/build/debug/upx
|
||||
run-testsuite-debug: $(top_srcdir)/build/debug PHONY
|
||||
run-testsuite-debug: build/debug PHONY
|
||||
time -p bash $(top_srcdir)/misc/testsuite/upx_testsuite_1.sh
|
||||
run-testsuite-release: export upx_testsuite_BUILDDIR := ./tmp-upx-testsuite-release
|
||||
run-testsuite-release: export upx_exe := $(top_srcdir)/build/release/upx
|
||||
run-testsuite-release: $(top_srcdir)/build/release PHONY
|
||||
run-testsuite-release: build/release PHONY
|
||||
time -p bash $(top_srcdir)/misc/testsuite/upx_testsuite_1.sh
|
||||
endif
|
||||
endif
|
||||
|
@ -25,38 +25,12 @@
|
||||
*/
|
||||
|
||||
#include "../util/system_defs.h"
|
||||
#include "../util/system_features.h"
|
||||
|
||||
/*************************************************************************
|
||||
// doctest support code implementation
|
||||
**************************************************************************/
|
||||
|
||||
#if 0 // TODO later
|
||||
// libc++ hardenining
|
||||
#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ + 0 >= 18)
|
||||
#if DEBUG
|
||||
#define _LIBCPP_HARDENING_MODE _LIBCPP_HARDENING_MODE_DEBUG
|
||||
#else
|
||||
#define _LIBCPP_HARDENING_MODE _LIBCPP_HARDENING_MODE_EXTENSIVE
|
||||
#endif
|
||||
#endif
|
||||
#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ + 0 < 18)
|
||||
#if DEBUG
|
||||
#define _LIBCPP_ENABLE_ASSERTIONS 1
|
||||
#endif
|
||||
#endif
|
||||
#endif // TODO later
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<features.h>)
|
||||
#include <features.h> // for __GLIBC__
|
||||
#endif
|
||||
#endif
|
||||
// aligned_alloc() was added in glibc-2.16
|
||||
#if defined(__ELF__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ + 0 == 2) && \
|
||||
(__GLIBC_MINOR__ + 0 < 16)
|
||||
#define _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
|
||||
#endif
|
||||
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
#define DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
|
||||
|
||||
|
@ -235,7 +235,8 @@ void PackW32PeI386::defineSymbols(unsigned ncsection, unsigned upxsection, unsig
|
||||
|
||||
void PackW32PeI386::addNewRelocations(Reloc &rel, unsigned upxsection) {
|
||||
if (use_stub_relocs)
|
||||
rel.add(upxsection + linker->getSymbolOffset("PESOCREL") + 1, 3);
|
||||
rel.add_reloc(upxsection + linker->getSymbolOffset("PESOCREL") + 1,
|
||||
IMAGE_REL_BASED_HIGHLOW);
|
||||
}
|
||||
|
||||
void PackW32PeI386::setOhDataBase(const pe_section_t *osection) { oh.database = osection[2].vaddr; }
|
||||
|
@ -190,7 +190,7 @@ void PackWinCeArm::addNewRelocations(Reloc &rel, unsigned upxsection) {
|
||||
for (unsigned s2r = 0; s2r < TABLESIZE(symbols_to_relocate); s2r++) {
|
||||
unsigned off = linker->getSymbolOffset(symbols_to_relocate[s2r]);
|
||||
if (off != 0xdeaddead)
|
||||
rel.add(off + upxsection, 3);
|
||||
rel.add_reloc(off + upxsection, IMAGE_REL_BASED_HIGHLOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
157
src/pefile.cpp
157
src/pefile.cpp
@ -213,18 +213,10 @@ int PeFile::readFileHeader() {
|
||||
// interval handling
|
||||
**************************************************************************/
|
||||
|
||||
PeFile::Interval::Interval(void *b) : capacity(0), base(b), ivarr(nullptr), ivnum(0) {}
|
||||
PeFile::Interval::Interval(SPAN_P(byte) b) : base(b) {}
|
||||
|
||||
PeFile::Interval::~Interval() noexcept { free(ivarr); }
|
||||
|
||||
void PeFile::Interval::add(const void *start, unsigned len) {
|
||||
add(ptr_diff_bytes(start, base), len);
|
||||
}
|
||||
|
||||
void PeFile::Interval::add(const void *start, const void *end) {
|
||||
add(ptr_diff_bytes(start, base), ptr_diff_bytes(end, start));
|
||||
}
|
||||
|
||||
int __acc_cdecl_qsort PeFile::Interval::compare(const void *p1, const void *p2) {
|
||||
const interval *i1 = (const interval *) p1;
|
||||
const interval *i2 = (const interval *) p2;
|
||||
@ -239,16 +231,28 @@ int __acc_cdecl_qsort PeFile::Interval::compare(const void *p1, const void *p2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PeFile::Interval::add(unsigned start, unsigned len) {
|
||||
if (ivnum == capacity)
|
||||
ivarr = (interval *) realloc(ivarr, (capacity += 15) * sizeof(interval));
|
||||
void PeFile::Interval::add_interval(unsigned start, unsigned len) {
|
||||
if (ivnum == ivcapacity) {
|
||||
ivcapacity += 15;
|
||||
ivarr = (interval *) realloc(ivarr, mem_size(sizeof(interval), ivcapacity));
|
||||
assert(ivarr != nullptr);
|
||||
}
|
||||
ivarr[ivnum].start = start;
|
||||
ivarr[ivnum++].len = len;
|
||||
ivarr[ivnum].len = len;
|
||||
ivnum += 1;
|
||||
}
|
||||
|
||||
void PeFile::Interval::add(const Interval *iv) {
|
||||
for (unsigned ic = 0; ic < iv->ivnum; ic++)
|
||||
add(iv->ivarr[ic].start, iv->ivarr[ic].len);
|
||||
void PeFile::Interval::add_interval(const void *start, unsigned len) {
|
||||
add_interval(ptr_udiff_bytes(start, base), len);
|
||||
}
|
||||
|
||||
void PeFile::Interval::add_interval(const void *start, const void *end) {
|
||||
add_interval(ptr_udiff_bytes(start, base), ptr_udiff_bytes(end, start));
|
||||
}
|
||||
|
||||
void PeFile::Interval::add_interval(const Interval *other) {
|
||||
for (unsigned ic = 0; ic < other->ivnum; ic++)
|
||||
add_interval(other->ivarr[ic].start, other->ivarr[ic].len);
|
||||
}
|
||||
|
||||
void PeFile::Interval::flatten() {
|
||||
@ -269,7 +273,7 @@ void PeFile::Interval::flatten() {
|
||||
|
||||
void PeFile::Interval::clear() {
|
||||
for (unsigned ic = 0; ic < ivnum; ic++)
|
||||
memset((char *) base + ivarr[ic].start, 0, ivarr[ic].len);
|
||||
memset(base + ivarr[ic].start, 0, ivarr[ic].len);
|
||||
}
|
||||
|
||||
void PeFile::Interval::dump() const {
|
||||
@ -293,7 +297,7 @@ static constexpr size_t RELOC_INPLACE_OFFSET = 64 * 1024;
|
||||
|
||||
static constexpr size_t RELOC_ENTRY_SIZE = 5; // encoded size in bytes; actual encoding is private
|
||||
static void reloc_entry_encode(SPAN_P(byte) buf, unsigned pos, unsigned reloc_type) {
|
||||
if (reloc_type >= 16)
|
||||
if (reloc_type == 0 || reloc_type >= 16)
|
||||
throwCantPack("bad reloc_type %u %u", pos, reloc_type);
|
||||
set_ne32(buf, pos);
|
||||
buf[4] = (upx_uint8_t) reloc_type;
|
||||
@ -301,6 +305,7 @@ static void reloc_entry_encode(SPAN_P(byte) buf, unsigned pos, unsigned reloc_ty
|
||||
static void reloc_entry_decode(SPAN_P(const byte) buf, unsigned *pos, unsigned *reloc_type) {
|
||||
*pos = get_ne32(buf);
|
||||
*reloc_type = buf[4];
|
||||
assert(*reloc_type > 0 && *reloc_type < 16);
|
||||
}
|
||||
static int __acc_cdecl_qsort reloc_entry_compare(const void *a, const void *b) {
|
||||
const unsigned pos1 = get_ne32(a);
|
||||
@ -322,9 +327,10 @@ PeFile::Reloc::~Reloc() noexcept {
|
||||
}
|
||||
|
||||
// constructor for compression only
|
||||
PeFile::Reloc::Reloc(byte *ptr, unsigned bytes) : start(ptr) {
|
||||
PeFile::Reloc::Reloc(byte *ptr, unsigned bytes) {
|
||||
assert(opt->cmd == CMD_COMPRESS);
|
||||
start_size_in_bytes = mem_size(1, bytes);
|
||||
start = ptr;
|
||||
initSpans();
|
||||
// fill counts
|
||||
unsigned pos, reloc_type;
|
||||
@ -346,7 +352,7 @@ void PeFile::Reloc::initSpans() {
|
||||
rb.reset();
|
||||
}
|
||||
|
||||
// check values so that we have better error messages (instead of getting a cryptic SPAN failure)
|
||||
// explicitly check values so that we get better error messages (instead of a cryptic SPAN failure)
|
||||
bool PeFile::Reloc::readFromRelocationBlock(byte *next_rb) { // set rb
|
||||
assert(!start_did_alloc);
|
||||
const unsigned off = ptr_udiff_bytes(next_rb, start);
|
||||
@ -399,8 +405,10 @@ bool PeFile::Reloc::next(unsigned &result_pos, unsigned &result_type) {
|
||||
}
|
||||
}
|
||||
|
||||
void PeFile::Reloc::add(unsigned pos, unsigned reloc_type) {
|
||||
void PeFile::Reloc::add_reloc(unsigned pos, unsigned reloc_type) {
|
||||
assert(start_did_alloc);
|
||||
if (reloc_type == IMAGE_REL_BASED_IGNORE)
|
||||
return;
|
||||
auto entry_ptr = start_buf + (RELOC_INPLACE_OFFSET + RELOC_ENTRY_SIZE * counts[0]);
|
||||
reloc_entry_encode(entry_ptr, pos, reloc_type);
|
||||
counts[0] += 1;
|
||||
@ -436,11 +444,11 @@ void PeFile::Reloc::finish(byte *(&result_ptr), unsigned &result_size) {
|
||||
throwCantPack("duplicate relocs (try --force)");
|
||||
prev_pos = pos;
|
||||
if (ic == 0 || pos - current_page >= 0x1000) {
|
||||
current_page = pos & ~0xfff; // page start
|
||||
// prepare next block for writing
|
||||
byte *next_rb = (rb.rel == nullptr) ? start : finish_block(rb.rel);
|
||||
rb.rel = (BaseReloc *) next_rb;
|
||||
rb.rel1 = (LE16 *) (next_rb + 8);
|
||||
current_page = pos & ~0xfff; // page start
|
||||
rb.rel->virtual_address = current_page;
|
||||
rb.rel->size_of_block = 8;
|
||||
}
|
||||
@ -449,7 +457,7 @@ void PeFile::Reloc::finish(byte *(&result_ptr), unsigned &result_size) {
|
||||
// info: if this is indeed a valid file we must increase RELOC_INPLACE_OFFSET
|
||||
throwCantPack("too many inplace relocs");
|
||||
}
|
||||
// write entry
|
||||
// write IMAGE_BASE_RELOCATION relocation
|
||||
*rb.rel1++ = (reloc_type << 12) | (pos & 0xfff);
|
||||
rb.rel->size_of_block += 2;
|
||||
}
|
||||
@ -747,7 +755,7 @@ class PeFile::ImportLinker final : public ElfLinkerAMD64 {
|
||||
|
||||
unsigned thunk_size; // 4 or 8 bytes
|
||||
|
||||
void add(const char *dll, const char *proc, unsigned ordinal) {
|
||||
void add_import(const char *dll, const char *proc, unsigned ordinal) {
|
||||
TStr sdll(name_for_dll(dll, dll_name_id));
|
||||
TStr desc_name(name_for_dll(dll, descriptor_id));
|
||||
|
||||
@ -826,20 +834,20 @@ public:
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
void add(const C *dll, unsigned ordinal) {
|
||||
void add_import(const C *dll, unsigned ordinal) {
|
||||
ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte"
|
||||
assert(ordinal < 0x10000);
|
||||
char ord[1 + 5 + 1];
|
||||
upx_safe_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal);
|
||||
add((const char *) dll, ordinal ? ord : nullptr, ordinal);
|
||||
add_import((const char *) dll, ordinal ? ord : nullptr, ordinal);
|
||||
}
|
||||
|
||||
template <typename C1, typename C2>
|
||||
void add(const C1 *dll, const C2 *proc) {
|
||||
void add_import(const C1 *dll, const C2 *proc) {
|
||||
ACC_COMPILE_TIME_ASSERT(sizeof(C1) == 1) // "char" or "byte"
|
||||
ACC_COMPILE_TIME_ASSERT(sizeof(C2) == 1) // "char" or "byte"
|
||||
assert(proc);
|
||||
add((const char *) dll, (const char *) proc, 0);
|
||||
add_import((const char *) dll, (const char *) proc, 0);
|
||||
}
|
||||
|
||||
unsigned build() {
|
||||
@ -910,7 +918,7 @@ public:
|
||||
};
|
||||
/*static*/ const char PeFile::ImportLinker::zeros[sizeof(import_desc)] = {0};
|
||||
|
||||
void PeFile::addKernelImport(const char *name) { ilinker->add(kernelDll(), name); }
|
||||
void PeFile::addKernelImport(const char *name) { ilinker->add_import(kernelDll(), name); }
|
||||
|
||||
void PeFile::addStubImports() {
|
||||
addKernelImport("LoadLibraryA");
|
||||
@ -1055,14 +1063,14 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
|
||||
if (idlls[ic]->ordinal)
|
||||
for (const LEXX *tarr = idlls[ic]->lookupt; *tarr; tarr++)
|
||||
if (*tarr & ord_mask) {
|
||||
ilinker->add(kernelDll(), *tarr & 0xffff);
|
||||
ilinker->add_import(kernelDll(), *tarr & 0xffff);
|
||||
kernel32ordinal = true;
|
||||
}
|
||||
} else if (!ilinker->hasDll(idlls[ic]->name)) {
|
||||
if (idlls[ic]->shname && !idlls[ic]->ordinal)
|
||||
ilinker->add(idlls[ic]->name, idlls[ic]->shname);
|
||||
ilinker->add_import(idlls[ic]->name, idlls[ic]->shname);
|
||||
else
|
||||
ilinker->add(idlls[ic]->name, idlls[ic]->ordinal);
|
||||
ilinker->add_import(idlls[ic]->name, idlls[ic]->ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1095,19 +1103,19 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
|
||||
const unsigned take2 = 1 + strlen(ibuf.subref("bad import name %#x", skip2, 1));
|
||||
memcpy(ppi, ibuf.subref("bad import name %#x", skip2, take2), take2);
|
||||
ppi += take2;
|
||||
names.add(*tarr, 2 + take2);
|
||||
names.add_interval(*tarr, 2 + take2);
|
||||
}
|
||||
ppi++;
|
||||
|
||||
const unsigned esize = ptr_udiff_bytes(tarr, idlls[ic]->lookupt);
|
||||
lookups.add(idlls[ic]->lookupt, esize);
|
||||
lookups.add_interval(idlls[ic]->lookupt, esize);
|
||||
if (ptr_diff_bytes(ibuf.subref("bad import name %#x", idlls[ic]->iat, 1),
|
||||
idlls[ic]->lookupt) != 0) {
|
||||
memcpy(ibuf.subref("bad import name %#x", idlls[ic]->iat, esize), idlls[ic]->lookupt,
|
||||
esize);
|
||||
iats.add(idlls[ic]->iat, esize);
|
||||
iats.add_interval(idlls[ic]->iat, esize);
|
||||
}
|
||||
names.add(idlls[ic]->name, strlen(idlls[ic]->name) + 1 + 1);
|
||||
names.add_interval(idlls[ic]->name, strlen(idlls[ic]->name) + 1 + 1);
|
||||
}
|
||||
ppi += 4;
|
||||
assert(ppi < oimport + soimport);
|
||||
@ -1137,15 +1145,15 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
|
||||
im->dllname = ptr_udiff_bytes(dlls[idlls[ic]->original_position].name, ibuf);
|
||||
}
|
||||
} else {
|
||||
iats.add(im_start, sizeof(import_desc) * dllnum);
|
||||
iats.add_interval(im_start, sizeof(import_desc) * dllnum);
|
||||
// zero unneeded data
|
||||
iats.clear();
|
||||
lookups.clear();
|
||||
}
|
||||
names.clear();
|
||||
|
||||
iats.add(&names);
|
||||
iats.add(&lookups);
|
||||
iats.add_interval(&names);
|
||||
iats.add_interval(&lookups);
|
||||
iats.flatten();
|
||||
for (unsigned ic = 0; ic < iats.ivnum; ic++)
|
||||
ilen += iats.ivarr[ic].len;
|
||||
@ -1158,7 +1166,7 @@ unsigned PeFile::processImports0(ord_mask_t ord_mask) // pass 1
|
||||
// export handling
|
||||
**************************************************************************/
|
||||
|
||||
PeFile::Export::Export(char *_base) : base(_base), iv(_base) {
|
||||
PeFile::Export::Export(char *_base) : base(_base), iv((byte *) _base) {
|
||||
COMPILE_TIME_ASSERT(sizeof(export_dir_t) == 40)
|
||||
COMPILE_TIME_ASSERT_ALIGNED1(export_dir_t)
|
||||
ename = functionptrs = ordinals = nullptr;
|
||||
@ -1183,7 +1191,7 @@ PeFile::Export::~Export() noexcept {
|
||||
void PeFile::Export::convert(unsigned eoffs, unsigned esize) {
|
||||
memcpy(&edir, base + eoffs, sizeof(export_dir_t));
|
||||
size = sizeof(export_dir_t);
|
||||
iv.add(eoffs, size);
|
||||
iv.add_interval(eoffs, size);
|
||||
|
||||
if (!edir.name || eoffs + esize <= (unsigned) edir.name) {
|
||||
char msg[50];
|
||||
@ -1193,13 +1201,13 @@ void PeFile::Export::convert(unsigned eoffs, unsigned esize) {
|
||||
unsigned len = strlen(base + edir.name) + 1;
|
||||
ename = strdup(base + edir.name);
|
||||
size += len;
|
||||
iv.add(edir.name, len);
|
||||
iv.add_interval(edir.name, len);
|
||||
|
||||
len = 4 * edir.functions;
|
||||
functionptrs = New(char, len + 1);
|
||||
memcpy(functionptrs, base + edir.addrtable, len);
|
||||
size += len;
|
||||
iv.add(edir.addrtable, len);
|
||||
iv.add_interval(edir.addrtable, len);
|
||||
|
||||
unsigned ic;
|
||||
names = New(char *, edir.names + edir.functions + 1);
|
||||
@ -1208,9 +1216,9 @@ void PeFile::Export::convert(unsigned eoffs, unsigned esize) {
|
||||
len = strlen(n) + 1;
|
||||
names[ic] = strdup(n);
|
||||
size += len;
|
||||
iv.add(get_le32(base + edir.nameptrtable + ic * 4), len);
|
||||
iv.add_interval(get_le32(base + edir.nameptrtable + ic * 4), len);
|
||||
}
|
||||
iv.add(edir.nameptrtable, 4 * edir.names);
|
||||
iv.add_interval(edir.nameptrtable, 4 * edir.names);
|
||||
size += 4 * edir.names;
|
||||
|
||||
LE32 *fp = (LE32 *) functionptrs;
|
||||
@ -1219,7 +1227,7 @@ void PeFile::Export::convert(unsigned eoffs, unsigned esize) {
|
||||
if (fp[ic] >= eoffs && fp[ic] < eoffs + esize) {
|
||||
char *forw = base + fp[ic];
|
||||
len = strlen(forw) + 1;
|
||||
iv.add(forw, len);
|
||||
iv.add_interval(forw, len);
|
||||
size += len;
|
||||
names[ic + edir.names] = strdup(forw);
|
||||
} else
|
||||
@ -1229,7 +1237,7 @@ void PeFile::Export::convert(unsigned eoffs, unsigned esize) {
|
||||
ordinals = New(char, len + 1);
|
||||
memcpy(ordinals, base + edir.ordinaltable, len);
|
||||
size += len;
|
||||
iv.add(edir.ordinaltable, len);
|
||||
iv.add_interval(edir.ordinaltable, len);
|
||||
iv.flatten();
|
||||
if (iv.ivnum == 1)
|
||||
iv.clear();
|
||||
@ -1319,11 +1327,11 @@ struct PeFile::tls_traits<LE32> final {
|
||||
byte _[8]; // zero init, characteristics
|
||||
};
|
||||
|
||||
static const unsigned sotls = 24;
|
||||
static const unsigned cb_size = 4;
|
||||
static constexpr unsigned sotls = 24;
|
||||
static constexpr unsigned cb_size = 4;
|
||||
typedef unsigned cb_value_t;
|
||||
static const unsigned reloc_type = IMAGE_REL_BASED_HIGHLOW;
|
||||
static const int tls_handler_offset_reloc = 4;
|
||||
static constexpr unsigned reloc_type = IMAGE_REL_BASED_HIGHLOW;
|
||||
static constexpr int tls_handler_offset_reloc = 4;
|
||||
};
|
||||
|
||||
template <>
|
||||
@ -1336,11 +1344,11 @@ struct PeFile::tls_traits<LE64> final {
|
||||
byte _[8]; // zero init, characteristics
|
||||
};
|
||||
|
||||
static const unsigned sotls = 40;
|
||||
static const unsigned cb_size = 8;
|
||||
static constexpr unsigned sotls = 40;
|
||||
static constexpr unsigned cb_size = 8;
|
||||
typedef upx_uint64_t cb_value_t;
|
||||
static const unsigned reloc_type = IMAGE_REL_BASED_DIR64;
|
||||
static const int tls_handler_offset_reloc = -1; // no need to relocate
|
||||
static constexpr unsigned reloc_type = IMAGE_REL_BASED_DIR64;
|
||||
static constexpr int tls_handler_offset_reloc = -1; // no need to relocate
|
||||
};
|
||||
|
||||
template <typename LEXX>
|
||||
@ -1349,7 +1357,7 @@ void PeFile::processTls1(Interval *iv, typename tls_traits<LEXX>::cb_value_t ima
|
||||
{
|
||||
typedef typename tls_traits<LEXX>::tls tls;
|
||||
typedef typename tls_traits<LEXX>::cb_value_t cb_value_t;
|
||||
const unsigned cb_size = tls_traits<LEXX>::cb_size;
|
||||
constexpr unsigned cb_size = tls_traits<LEXX>::cb_size;
|
||||
|
||||
COMPILE_TIME_ASSERT(sizeof(tls) == tls_traits<LEXX>::sotls)
|
||||
COMPILE_TIME_ASSERT_ALIGNED1(tls)
|
||||
@ -1401,7 +1409,7 @@ void PeFile::processTls1(Interval *iv, typename tls_traits<LEXX>::cb_value_t ima
|
||||
unsigned pos, type;
|
||||
while (rel.next(pos, type))
|
||||
if (pos >= tlsdatastart && pos < tlsdataend)
|
||||
iv->add(pos, type);
|
||||
iv->add_interval(pos, type);
|
||||
|
||||
sotls = sizeof(tls) + tlsdataend - tlsdatastart;
|
||||
// if TLS callbacks are used, we need two more {D|Q}WORDS at the end of the TLS
|
||||
@ -1436,21 +1444,22 @@ void PeFile::processTls2(Reloc *const rel, const Interval *const iv, unsigned ne
|
||||
{
|
||||
typedef typename tls_traits<LEXX>::tls tls;
|
||||
typedef typename tls_traits<LEXX>::cb_value_t cb_value_t;
|
||||
const unsigned cb_size = tls_traits<LEXX>::cb_size;
|
||||
const unsigned reloc_type = tls_traits<LEXX>::reloc_type;
|
||||
const int tls_handler_offset_reloc = tls_traits<LEXX>::tls_handler_offset_reloc;
|
||||
constexpr unsigned cb_size = tls_traits<LEXX>::cb_size;
|
||||
constexpr unsigned reloc_type = tls_traits<LEXX>::reloc_type;
|
||||
static_assert(reloc_type > IMAGE_REL_BASED_IGNORE && reloc_type < 16);
|
||||
constexpr int tls_handler_offset_reloc = tls_traits<LEXX>::tls_handler_offset_reloc;
|
||||
|
||||
if (sotls == 0)
|
||||
return;
|
||||
// add new relocation entries
|
||||
|
||||
if (tls_handler_offset > 0 && tls_handler_offset_reloc > 0)
|
||||
rel->add(tls_handler_offset + tls_handler_offset_reloc, reloc_type);
|
||||
rel->add_reloc(tls_handler_offset + tls_handler_offset_reloc, reloc_type);
|
||||
|
||||
unsigned ic;
|
||||
// NEW: if TLS callbacks are used, relocate the VA of the callback chain, too - Stefan Widmann
|
||||
for (ic = 0; ic < (use_tls_callbacks ? 4 * cb_size : 3 * cb_size); ic += cb_size)
|
||||
rel->add(newaddr + ic, reloc_type);
|
||||
rel->add_reloc(newaddr + ic, reloc_type);
|
||||
|
||||
SPAN_S_VAR(tls, const tlsp, mb_otls);
|
||||
// now the relocation entries in the tls data area
|
||||
@ -1462,9 +1471,9 @@ void PeFile::processTls2(Reloc *const rel, const Interval *const iv, unsigned ne
|
||||
if (kc < tlsp->dataend && kc >= tlsp->datastart) {
|
||||
kc += newaddr + sizeof(tls) - tlsp->datastart;
|
||||
*p = kc + imagebase;
|
||||
rel->add(kc, iv->ivarr[ic].len);
|
||||
rel->add_reloc(kc, iv->ivarr[ic].len);
|
||||
} else
|
||||
rel->add(kc - imagebase, iv->ivarr[ic].len);
|
||||
rel->add_reloc(kc - imagebase, iv->ivarr[ic].len);
|
||||
}
|
||||
|
||||
const unsigned tls_data_size = tlsp->dataend - tlsp->datastart;
|
||||
@ -1483,7 +1492,7 @@ void PeFile::processTls2(Reloc *const rel, const Interval *const iv, unsigned ne
|
||||
pp = otls + (sotls - 1 * cb_size);
|
||||
*(LEXX *) raw_bytes(pp, sizeof(LEXX)) = 0; // end of one-item list
|
||||
// add relocation for TLS handler offset
|
||||
rel->add(newaddr + sotls - 2 * cb_size, reloc_type);
|
||||
rel->add_reloc(newaddr + sotls - 2 * cb_size, reloc_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1501,7 +1510,7 @@ void PeFile::processLoadConf(Interval *iv) // pass 1
|
||||
soloadconf = get_le32(loadconf);
|
||||
if (soloadconf == 0)
|
||||
return;
|
||||
static const unsigned MAX_SOLOADCONF = 256; // XXX FIXME: Why?
|
||||
static constexpr unsigned MAX_SOLOADCONF = 256; // XXX FIXME: Why?
|
||||
if (soloadconf > MAX_SOLOADCONF)
|
||||
info("Load Configuration directory %u > %u", soloadconf, MAX_SOLOADCONF);
|
||||
|
||||
@ -1513,7 +1522,7 @@ void PeFile::processLoadConf(Interval *iv) // pass 1
|
||||
unsigned pos, type;
|
||||
while (rel.next(pos, type))
|
||||
if (pos >= lcaddr && pos < lcaddr + soloadconf) {
|
||||
iv->add(pos - lcaddr, type);
|
||||
iv->add_interval(pos - lcaddr, type);
|
||||
NO_printf("loadconf reloc detected: %x\n", pos);
|
||||
}
|
||||
|
||||
@ -1528,7 +1537,7 @@ void PeFile::processLoadConf(Reloc *rel, const Interval *iv,
|
||||
// now we have the address of the new load config table
|
||||
// so we can create the new relocation entries
|
||||
for (unsigned ic = 0; ic < iv->ivnum; ic++) {
|
||||
rel->add(iv->ivarr[ic].start + newaddr, iv->ivarr[ic].len);
|
||||
rel->add_reloc(iv->ivarr[ic].start + newaddr, iv->ivarr[ic].len);
|
||||
NO_printf("loadconf reloc added: %x %d\n", iv->ivarr[ic].start + newaddr,
|
||||
iv->ivarr[ic].len);
|
||||
}
|
||||
@ -1812,14 +1821,14 @@ void PeFile::Resource::dump(const upx_rnode *node, unsigned level) const {
|
||||
|
||||
void PeFile::Resource::clear(byte *node, unsigned level, Interval *iv) {
|
||||
if (level == 3)
|
||||
iv->add(node, sizeof(res_data));
|
||||
iv->add_interval(node, sizeof(res_data));
|
||||
else {
|
||||
const res_dir *const rd = (res_dir *) node;
|
||||
const unsigned n = rd->identr + rd->namedentr;
|
||||
const res_dir_entry *rde = rd->entries;
|
||||
for (unsigned ic = 0; ic < n; ic++, rde++)
|
||||
clear(newstart + (rde->child & 0x7fffffff), level + 1, iv);
|
||||
iv->add(rd, rd->Sizeof());
|
||||
iv->add_interval(rd, rd->Sizeof());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2194,7 +2203,7 @@ unsigned PeFile::readSections(unsigned objs, unsigned usize, unsigned ih_fileali
|
||||
isection[ic].vsize = isection[ic].size;
|
||||
if ((isection[ic].flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) ||
|
||||
isection[ic].rawdataptr == 0 || (isection[ic].flags & IMAGE_SCN_LNK_INFO)) {
|
||||
// holes.add(isection[ic].vaddr,isection[ic].vsize);
|
||||
// holes.add_interval(isection[ic].vaddr, isection[ic].vsize);
|
||||
continue;
|
||||
}
|
||||
if (isection[ic].vaddr + isection[ic].size > usize)
|
||||
@ -2722,10 +2731,10 @@ void PeFile::rebuildRelocs(SPAN_S(byte) & extra_info, unsigned bits, unsigned fl
|
||||
if (big & 6) { // add 16-bit relocations
|
||||
SPAN_S_VAR(const LE32, q, SPAN_TYPE_CAST(const LE32, rdata));
|
||||
while (*q)
|
||||
rel.add(*q++ + rvamin, (big & 4) ? 2 : 1);
|
||||
rel.add_reloc(*q++ + rvamin, (big & 4) ? IMAGE_REL_BASED_LOW : IMAGE_REL_BASED_HIGH);
|
||||
if ((big & 6) == 6)
|
||||
while (*++q)
|
||||
rel.add(*q + rvamin, 1);
|
||||
rel.add_reloc(*q + rvamin, IMAGE_REL_BASED_HIGH);
|
||||
// rdata = (const byte *) raw_bytes(q, 0); // advance rdata
|
||||
}
|
||||
|
||||
@ -2736,7 +2745,7 @@ void PeFile::rebuildRelocs(SPAN_S(byte) & extra_info, unsigned bits, unsigned fl
|
||||
set_le32(p, get_le32(p) + imagebase + rvamin);
|
||||
else
|
||||
set_le64(p, get_le64(p) + imagebase + rvamin);
|
||||
rel.add(rvamin + get_le32(wrkmem + 4 * ic),
|
||||
rel.add_reloc(rvamin + get_le32(wrkmem + 4 * ic),
|
||||
bits == 32 ? IMAGE_REL_BASED_HIGHLOW : IMAGE_REL_BASED_DIR64);
|
||||
}
|
||||
rel.finish(oxrelocs, soxrelocs);
|
||||
|
22
src/pefile.h
22
src/pefile.h
@ -356,9 +356,9 @@ protected:
|
||||
RT_LAST
|
||||
};
|
||||
|
||||
enum {
|
||||
enum { // 4-bit reloc_type in IMAGE_BASE_RELOCATION relocations
|
||||
IMAGE_REL_BASED_ABSOLUTE = 0, // this relocation is ignored
|
||||
IMAGE_REL_BASED_IGNORE = 0, // (unofficial name)
|
||||
IMAGE_REL_BASED_IGNORE = 0, // (unofficial UPX name)
|
||||
IMAGE_REL_BASED_HIGH = 1,
|
||||
IMAGE_REL_BASED_LOW = 2,
|
||||
IMAGE_REL_BASED_HIGHLOW = 3,
|
||||
@ -372,8 +372,8 @@ protected:
|
||||
};
|
||||
|
||||
class Interval final : private noncopyable {
|
||||
unsigned capacity = 0;
|
||||
void *base = nullptr;
|
||||
SPAN_0(byte) base = nullptr;
|
||||
unsigned ivcapacity = 0; // for ivarr
|
||||
public:
|
||||
struct interval {
|
||||
unsigned start, len;
|
||||
@ -381,13 +381,13 @@ protected:
|
||||
struct interval *ivarr = nullptr;
|
||||
unsigned ivnum = 0;
|
||||
|
||||
explicit Interval(void *b);
|
||||
explicit Interval(SPAN_P(byte));
|
||||
~Interval() noexcept;
|
||||
|
||||
void add(unsigned start, unsigned len);
|
||||
void add(const void *start, unsigned len);
|
||||
void add(const void *start, const void *end);
|
||||
void add(const Interval *iv);
|
||||
void add_interval(unsigned start, unsigned len);
|
||||
void add_interval(const void *start, unsigned len);
|
||||
void add_interval(const void *start, const void *end);
|
||||
void add_interval(const Interval *other);
|
||||
void flatten();
|
||||
|
||||
void clear();
|
||||
@ -407,7 +407,7 @@ protected:
|
||||
struct alignas(1) BaseReloc { // IMAGE_BASE_RELOCATION
|
||||
LE32 virtual_address;
|
||||
LE32 size_of_block;
|
||||
// LE16 rel1[COUNT]; // COUNT == (size_of_block - 8) / 2
|
||||
// LE16 rel1[COUNT]; // actual relocations; COUNT == (size_of_block - 8) / 2
|
||||
};
|
||||
struct RelocationBlock {
|
||||
SPAN_0(BaseReloc) rel = nullptr;
|
||||
@ -429,7 +429,7 @@ protected:
|
||||
bool next(unsigned &result_pos, unsigned &result_type);
|
||||
const unsigned *getcounts() const { return counts; }
|
||||
//
|
||||
void add(unsigned pos, unsigned type);
|
||||
void add_reloc(unsigned pos, unsigned type);
|
||||
void finish(byte *(&result_ptr), unsigned &result_size); // => transfer ownership
|
||||
};
|
||||
|
||||
|
@ -133,6 +133,13 @@ private:
|
||||
UPX_CXX_DISABLE_NEW_DELETE_IMPL_CSUDF_B__(Klass) \
|
||||
UPX_CXX_DISABLE_NEW_DELETE_IMPL_CSUDDF_B__(Klass)
|
||||
|
||||
#if defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION) // do not use std::align_val_t
|
||||
#undef UPX_CXX_DISABLE_NEW_DELETE
|
||||
#undef UPX_CXX_DISABLE_NEW_DELETE_NO_VIRTUAL
|
||||
#define UPX_CXX_DISABLE_NEW_DELETE(Klass) private:
|
||||
#define UPX_CXX_DISABLE_NEW_DELETE_NO_VIRTUAL(Klass) private:
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
// type_traits
|
||||
**************************************************************************/
|
||||
|
61
src/util/system_features.h
Normal file
61
src/util/system_features.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* system_features.h -- libc and libc++ features
|
||||
|
||||
This file is part of the UPX executable compressor.
|
||||
|
||||
Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer
|
||||
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
|
||||
<markus@oberhumer.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "system_defs.h"
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<features.h>)
|
||||
#include <features.h> // for __GLIBC__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// aligned_alloc() was added in glibc-2.16
|
||||
#if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION) && defined(__cplusplus)
|
||||
#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ + 0 == 2) && \
|
||||
(__GLIBC_MINOR__ + 0 > 0) && (__GLIBC_MINOR__ + 0 < 16)
|
||||
#define _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
|
||||
#endif
|
||||
#endif // _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
|
||||
|
||||
#if 0 // TODO later
|
||||
// libc++ hardenining
|
||||
#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ + 0 >= 18)
|
||||
#if DEBUG
|
||||
#define _LIBCPP_HARDENING_MODE _LIBCPP_HARDENING_MODE_DEBUG
|
||||
#else
|
||||
#define _LIBCPP_HARDENING_MODE _LIBCPP_HARDENING_MODE_EXTENSIVE
|
||||
#endif
|
||||
#endif
|
||||
#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ + 0 < 18)
|
||||
#if DEBUG
|
||||
#define _LIBCPP_ENABLE_ASSERTIONS 1
|
||||
#endif
|
||||
#endif
|
||||
#endif // TODO later
|
||||
|
||||
/* vim:set ts=4 sw=4 et: */
|
@ -90,6 +90,7 @@ static_assert(sizeof(void *) == sizeof(long));
|
||||
#endif
|
||||
|
||||
// ACC and C system headers
|
||||
#include "system_features.h"
|
||||
#ifndef ACC_CFG_USE_NEW_STYLE_CASTS
|
||||
#define ACC_CFG_USE_NEW_STYLE_CASTS 1
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user