src: introduce upx::atomic_exchange; cleanups

This commit is contained in:
Markus F.X.J. Oberhumer
2024-05-07 10:05:44 +02:00
parent e5546bc8b0
commit 3d82f0cfe1
10 changed files with 174 additions and 70 deletions
+56 -6
View File
@@ -250,39 +250,85 @@ struct Z2_X2 : public X2 {
// util
**************************************************************************/
TEST_CASE("upx::atomic_exchange") {
{
upx_int8_t x = -1;
upx_int8_t y = upx::atomic_exchange(&x, (upx_int8_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, -1);
}
{
upx_int16_t x = -1;
upx_int16_t y = upx::atomic_exchange(&x, (upx_int16_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, -1);
}
{
upx_int32_t x = -1;
upx_int32_t y = upx::atomic_exchange(&x, (upx_int32_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, -1);
}
{
size_t x = (size_t) 0 - 1;
size_t y = upx::atomic_exchange(&x, (size_t) 2);
CHECK_EQ(x, 2);
CHECK_EQ(y, (size_t) 0 - 1);
}
{
int dummy = -1;
int *x = &dummy;
int *y = upx::atomic_exchange(&x, (int *) nullptr);
CHECK_EQ(x, nullptr);
CHECK_EQ(y, &dummy);
CHECK_EQ(dummy, -1);
}
}
TEST_CASE("upx::ObjectDeleter 1") {
LE16 *o = nullptr; // object
LE32 *a = nullptr; // array
LE64 *m = nullptr; // malloc
{
upx::ObjectDeleter<LE16 **> o_deleter{&o, 1};
auto o_deleter = upx::ObjectDeleter(&o, 1);
o = new LE16;
assert(o != nullptr);
upx::ArrayDeleter<LE32 **> a_deleter{&a, 1};
auto a_deleter = upx::ArrayDeleter(&a, 1);
a = New(LE32, 1);
assert(a != nullptr);
auto m_deleter = upx::MallocDeleter(&m, 1);
m = (LE64 *) ::malloc(sizeof(LE64));
assert(m != nullptr);
}
assert(o == nullptr);
assert(a == nullptr);
assert(m == nullptr);
// test "const" versions
{
const upx::ObjectDeleter<LE16 **const> o_deleter{&o, 1};
const auto o_deleter = upx::ObjectDeleter(&o, 1);
o = new LE16;
assert(o != nullptr);
const upx::ArrayDeleter<LE32 **const> a_deleter{&a, 1};
const auto a_deleter = upx::ArrayDeleter(&a, 1);
a = New(LE32, 1);
assert(a != nullptr);
const auto m_deleter = upx::MallocDeleter(&m, 1);
m = (LE64 *) ::malloc(sizeof(LE64));
assert(m != nullptr);
}
assert(o == nullptr);
assert(a == nullptr);
assert(m == nullptr);
}
TEST_CASE("upx::ObjectDeleter 2") {
constexpr size_t N = 2;
BE16 *o[N]; // multiple objects
BE32 *a[N]; // multiple arrays
BE64 *m[N]; // multiple mallocs
{
upx::ObjectDeleter<BE16 **> o_deleter{o, 0};
upx::ArrayDeleter<BE32 **> a_deleter{a, 0};
auto o_deleter = upx::ObjectDeleter(o, 0);
auto a_deleter = upx::ArrayDeleter(a, 0);
auto m_deleter = upx::MallocDeleter(m, 0);
for (size_t i = 0; i < N; i++) {
o[i] = new BE16;
assert(o[i] != nullptr);
@@ -290,11 +336,15 @@ TEST_CASE("upx::ObjectDeleter 2") {
a[i] = New(BE32, 1 + i);
assert(a[i] != nullptr);
a_deleter.count += 1;
m[i] = (BE64 *) ::malloc(sizeof(BE64));
assert(m[i] != nullptr);
m_deleter.count += 1;
}
}
for (size_t i = 0; i < N; i++) {
assert(o[i] == nullptr);
assert(a[i] == nullptr);
assert(m[i] == nullptr);
}
}
+29 -27
View File
@@ -30,7 +30,7 @@
static unsigned hex(uchar c) { return (c & 0xf) + (c > '9' ? 9 : 0); }
static bool update_capacity(unsigned size, unsigned *capacity) {
static bool grow_capacity(unsigned size, unsigned *capacity) {
if (size < *capacity)
return false;
if (*capacity == 0)
@@ -41,16 +41,20 @@ static bool update_capacity(unsigned size, unsigned *capacity) {
}
template <class T>
static noinline T **realloc_array(T **array, size_t capacity) {
static T **realloc_array(T **array, size_t capacity) may_throw {
assert_noexcept(capacity > 0);
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
void *p = realloc(array, mem_size(sizeof(T *), capacity));
assert_noexcept(p != nullptr);
return static_cast<T **>(p);
}
template <class T>
static noinline void free_array(T **array, size_t count) {
for (size_t i = 0; i < count; i++)
delete array[i];
static void free_array(T **array, size_t count) noexcept {
for (size_t i = 0; i < count; i++) {
T *item = upx::atomic_exchange(&array[i], (T *) nullptr);
delete item;
}
::free(array); // NOLINT(bugprone-multi-level-implicit-pointer-conversion)
}
@@ -61,11 +65,11 @@ static noinline void free_array(T **array, size_t count) {
ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a)
: name(nullptr), output(nullptr), size(s), offset(0), p2align(a), next(nullptr) {
name = strdup(n);
assert(name != nullptr);
assert_noexcept(name != nullptr);
input = ::malloc(s + 1);
assert(input != nullptr);
assert_noexcept(input != nullptr);
if (s != 0) {
assert(i != nullptr);
assert_noexcept(i != nullptr);
memcpy(input, i, s);
}
((char *) input)[s] = 0;
@@ -83,8 +87,8 @@ ElfLinker::Section::~Section() noexcept {
ElfLinker::Symbol::Symbol(const char *n, Section *s, upx_uint64_t o)
: name(nullptr), section(s), offset(o) {
name = strdup(n);
assert(name != nullptr);
assert(section != nullptr);
assert_noexcept(name != nullptr);
assert_noexcept(section != nullptr);
}
ElfLinker::Symbol::~Symbol() noexcept { ::free(name); }
@@ -96,7 +100,7 @@ ElfLinker::Symbol::~Symbol() noexcept { ::free(name); }
ElfLinker::Relocation::Relocation(const Section *s, unsigned o, const char *t, const Symbol *v,
upx_uint64_t a)
: section(s), offset(o), type(t), value(v), add(a) {
assert(section != nullptr);
assert_noexcept(section != nullptr);
}
/*************************************************************************
@@ -150,7 +154,7 @@ void ElfLinker::init(const void *pdata_v, int plen, unsigned pxtra) {
input[inputlen] = 0; // NUL terminate
output_capacity = (inputlen ? (inputlen + pxtra) : 0x4000);
assert(output_capacity <= (1 << 16)); // LE16 l_info.l_size
assert(output_capacity < (1 << 16)); // LE16 l_info.l_size
output = New(byte, output_capacity);
outputlen = 0;
NO_printf("\nElfLinker::init %d @%p\n", output_capacity, output);
@@ -180,8 +184,9 @@ void ElfLinker::init(const void *pdata_v, int plen, unsigned pxtra) {
}
void ElfLinker::preprocessSections(char *start, char const *end) {
assert_noexcept(nsections == 0);
char *nextl;
for (nsections = 0; start < end; start = 1 + nextl) {
for (; start < end; start = 1 + nextl) {
nextl = strchr(start, '\n');
assert(nextl != nullptr);
*nextl = '\0'; // a record is a line
@@ -201,8 +206,9 @@ void ElfLinker::preprocessSections(char *start, char const *end) {
}
void ElfLinker::preprocessSymbols(char *start, char const *end) {
assert_noexcept(nsymbols == 0);
char *nextl;
for (nsymbols = 0; start < end; start = 1 + nextl) {
for (; start < end; start = 1 + nextl) {
nextl = strchr(start, '\n');
assert(nextl != nullptr);
*nextl = '\0'; // a record is a line
@@ -237,9 +243,10 @@ void ElfLinker::preprocessSymbols(char *start, char const *end) {
}
void ElfLinker::preprocessRelocations(char *start, char const *end) {
assert_noexcept(nrelocations == 0);
Section *section = nullptr;
char *nextl;
for (nrelocations = 0; start < end; start = 1 + nextl) {
for (; start < end; start = 1 + nextl) {
nextl = strchr(start, '\n');
assert(nextl != nullptr);
*nextl = '\0'; // a record is a line
@@ -314,13 +321,11 @@ ElfLinker::Section *ElfLinker::addSection(const char *sname, const void *sdata,
NO_printf("addSection: %s len=%d align=%d\n", sname, slen, p2align);
if (!sdata && (!strcmp("ABS*", sname) || !strcmp("UND*", sname)))
return nullptr;
if (update_capacity(nsections, &nsections_capacity))
sections = realloc_array(sections, nsections_capacity);
assert(sections);
assert(sname);
assert(sname[0]);
assert(sname && sname[0]);
assert(sname[strlen(sname) - 1] != ':');
assert(findSection(sname, false) == nullptr);
if (grow_capacity(nsections, &nsections_capacity))
sections = realloc_array(sections, nsections_capacity);
Section *sec = new Section(sname, sdata, slen, p2align);
sec->sort_id = nsections;
sections[nsections++] = sec;
@@ -330,13 +335,11 @@ ElfLinker::Section *ElfLinker::addSection(const char *sname, const void *sdata,
ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section,
upx_uint64_t offset) {
NO_printf("addSymbol: %s %s 0x%llx\n", name, section, offset);
if (update_capacity(nsymbols, &nsymbols_capacity))
symbols = realloc_array(symbols, nsymbols_capacity);
assert(symbols != nullptr);
assert(name);
assert(name[0]);
assert(name && name[0]);
assert(name[strlen(name) - 1] != ':');
assert(findSymbol(name, false) == nullptr);
if (grow_capacity(nsymbols, &nsymbols_capacity))
symbols = realloc_array(symbols, nsymbols_capacity);
Symbol *sym = new Symbol(name, findSection(section), offset);
symbols[nsymbols++] = sym;
return sym;
@@ -344,9 +347,8 @@ ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section,
ElfLinker::Relocation *ElfLinker::addRelocation(const char *section, unsigned off, const char *type,
const char *symbol, upx_uint64_t add) {
if (update_capacity(nrelocations, &nrelocations_capacity))
if (grow_capacity(nrelocations, &nrelocations_capacity))
relocations = realloc_array(relocations, nrelocations_capacity);
assert(relocations != nullptr);
Relocation *rel = new Relocation(findSection(section), off, type, findSymbol(symbol), add);
relocations[nrelocations++] = rel;
return rel;
+3 -3
View File
@@ -512,7 +512,7 @@ void PeFile32::processRelocs() // pass1
infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]);
LE32 *fix[4];
upx::ArrayDeleter<LE32 **const> fix_deleter{fix, 0}; // don't leak memory
auto fix_deleter = upx::ArrayDeleter(fix, 0); // don't leak memory
for (unsigned ic = 0; ic <= IMAGE_REL_BASED_HIGHLOW; ic++) {
fix[ic] = New(LE32, counts[ic]);
fix_deleter.count += 1;
@@ -614,7 +614,7 @@ void PeFile64::processRelocs() // pass1
infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]);
LE32 *fix[16];
upx::ArrayDeleter<LE32 **const> fix_deleter{fix, 0}; // don't leak memory
auto fix_deleter = upx::ArrayDeleter(fix, 0); // don't leak memory
for (unsigned ic = 0; ic < 16; ic++) {
fix[ic] = New(LE32, counts[ic]);
fix_deleter.count += 1;
@@ -1926,7 +1926,7 @@ void PeFile::processResources(Resource *res) {
SPAN_S_VAR(byte, ores, oresources + res->dirsize());
char *keep_icons = nullptr; // icon ids in the first icon group
upx::ArrayDeleter<char **const> keep_icons_deleter{&keep_icons, 1}; // don't leak memory
const auto keep_icons_deleter = upx::ArrayDeleter(&keep_icons, 1); // don't leak memory
unsigned iconsin1stdir = 0;
if (opt->win32_pe.compress_icons == 2)
while (res->next()) // there is no rewind() in Resource
+58 -18
View File
@@ -26,6 +26,7 @@
#pragma once
// #include <atomic>
// #include <cstddef>
// #include <new>
// #include <type_traits>
@@ -186,37 +187,76 @@ forceinline Result ptr_static_cast(const From *ptr) noexcept {
return static_cast<Result>(static_cast<const void *>(ptr));
}
#if WITH_THREADS
// cast "T *" to "std::atomic<T> *"
template <class T>
forceinline std::atomic<T> *ptr_std_atomic_cast(T *ptr) noexcept {
// TODO later: make sure that this cast is indeed legal
std::atomic<T> *result = ptr_static_cast<std::atomic<T> *>(ptr);
static_assert(sizeof(*result) == sizeof(*ptr));
static_assert(alignof(*result) == alignof(*ptr));
return result;
}
#endif // WITH_THREADS
// atomic_exchange
template <class T>
forceinline T atomic_exchange(T *ptr, T new_value) noexcept {
static_assert(std::is_standard_layout_v<T>);
static_assert(std::is_trivially_copyable_v<T>);
#if __has_builtin(__atomic_exchange_n) && defined(__ATOMIC_SEQ_CST)
return __atomic_exchange_n(ptr, new_value, __ATOMIC_SEQ_CST);
#elif __has_builtin(__sync_swap)
return __sync_swap(ptr, new_value);
#elif WITH_THREADS
return std::atomic_exchange(ptr_std_atomic_cast(ptr), new_value);
#else
T old_value = *ptr;
*ptr = new_value;
return old_value;
#endif
}
// helper classes so we don't leak memory on exceptions
template <class T> // T is "SomeType **"
template <class T>
struct ObjectDeleter final {
static_assert(std::is_pointer_v<T>);
static_assert(std::is_pointer_v<std::remove_pointer_t<T> >);
T items; // public
size_t count; // public
T **items; // public
std::size_t count; // public
explicit ObjectDeleter(T **p, std::size_t n) noexcept : items(p), count(n) {}
~ObjectDeleter() noexcept { delete_items(); }
void delete_items() noexcept {
for (size_t i = 0; i < count; i++) {
auto item = items[i];
items[i] = nullptr;
for (std::size_t i = 0; i < count; i++) {
T *item = atomic_exchange(&items[i], (T *) nullptr);
delete item; // single object delete
}
}
};
template <class T> // T is "SomeType **"
template <class T>
struct ArrayDeleter final {
static_assert(std::is_pointer_v<T>);
static_assert(std::is_pointer_v<std::remove_pointer_t<T> >);
T items; // public
size_t count; // public
T **items; // public
std::size_t count; // public
explicit ArrayDeleter(T **p, std::size_t n) noexcept : items(p), count(n) {}
~ArrayDeleter() noexcept { delete_items(); }
void delete_items() noexcept {
for (size_t i = 0; i < count; i++) {
auto item = items[i];
items[i] = nullptr;
for (std::size_t i = 0; i < count; i++) {
T *item = atomic_exchange(&items[i], (T *) nullptr);
delete[] item; // array delete
}
}
};
template <class T>
struct MallocDeleter final {
T **items; // public
std::size_t count; // public
explicit MallocDeleter(T **p, std::size_t n) noexcept : items(p), count(n) {}
~MallocDeleter() noexcept { delete_items(); }
void delete_items() noexcept {
for (std::size_t i = 0; i < count; i++) {
T *item = atomic_exchange(&items[i], (T *) nullptr);
::free(item); // free memory from malloc()
}
}
};
class noncopyable {
protected:
@@ -242,10 +282,10 @@ constexpr bool string_gt(const char *a, const char *b) { return string_lt(b, a);
constexpr bool string_le(const char *a, const char *b) { return !string_lt(b, a); }
constexpr bool string_ge(const char *a, const char *b) { return !string_lt(a, b); }
constexpr bool mem_eq(const char *a, const char *b, size_t n) {
constexpr bool mem_eq(const char *a, const char *b, std::size_t n) {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
constexpr bool mem_eq(const unsigned char *a, const unsigned char *b, size_t n) {
constexpr bool mem_eq(const unsigned char *a, const unsigned char *b, std::size_t n) {
return n == 0 || (*a == *b && mem_eq(a + 1, b + 1, n - 1));
}
} // namespace compile_time
+4 -4
View File
@@ -136,19 +136,19 @@ static_assert(sizeof(void *) == sizeof(long));
#include <mutex>
#endif
// sanitizers
// sanitizers: ASAN, MSAN, UBSAN
#if !defined(__SANITIZE_ADDRESS__) && defined(__has_feature)
#if __has_feature(address_sanitizer)
#if __has_feature(address_sanitizer) // ASAN
#define __SANITIZE_ADDRESS__ 1
#endif
#endif
#if !defined(__SANITIZE_MEMORY__) && defined(__has_feature)
#if __has_feature(memory_sanitizer)
#if __has_feature(memory_sanitizer) // MSAN
#define __SANITIZE_MEMORY__ 1
#endif
#endif
#if !defined(__SANITIZE_UNDEFINED_BEHAVIOR__) && defined(__has_feature)
#if __has_feature(undefined_behavior_sanitizer)
#if __has_feature(undefined_behavior_sanitizer) // UBSAN
#define __SANITIZE_UNDEFINED_BEHAVIOR__ 1
#endif
#endif