From 3d82f0cfe18efe9948a82ce2a70b2a90f1b03d31 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Tue, 7 May 2024 10:05:44 +0200 Subject: [PATCH] src: introduce upx::atomic_exchange; cleanups --- Makefile | 8 +-- misc/cmake/print_info.cmake | 2 +- misc/podman/rebuild-stubs/Dockerfile | 7 ++- misc/podman/rebuild-stubs/packages.txt | 10 +++- misc/testsuite/mimic_ctest.sh | 9 +-- src/check/dt_cxxlib.cpp | 62 +++++++++++++++++++-- src/linker.cpp | 56 ++++++++++--------- src/pefile.cpp | 6 +- src/util/cxxlib.h | 76 ++++++++++++++++++++------ src/util/system_headers.h | 8 +-- 10 files changed, 174 insertions(+), 70 deletions(-) diff --git a/Makefile b/Makefile index ab7dc87a..d04d0282 100644 --- a/Makefile +++ b/Makefile @@ -59,10 +59,10 @@ CTEST = ctest CTEST_JOBS ?= 8 CTEST_FLAGS = --output-on-failure --parallel $(CTEST_JOBS) -build/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) -C Debug $(CTEST_FLAGS) -build/%/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) -C Debug $(CTEST_FLAGS) -build/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) -C Release $(CTEST_FLAGS) -build/%/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) -C Release $(CTEST_FLAGS) +build/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) $(CTEST_FLAGS) -C Debug +build/%/debug+test: $$(dir $$@)debug PHONY; cd "$(dir $@)debug" && $(CTEST) $(CTEST_FLAGS) -C Debug +build/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) $(CTEST_FLAGS) -C Release +build/%/release+test: $$(dir $$@)release PHONY; cd "$(dir $@)release" && $(CTEST) $(CTEST_FLAGS) -C Release build/%/all+test: $$(dir $$@)debug+test $$(dir $$@)release+test PHONY ; # shortcuts diff --git a/misc/cmake/print_info.cmake b/misc/cmake/print_info.cmake index cd56a912..58e63f0a 100644 --- a/misc/cmake/print_info.cmake +++ b/misc/cmake/print_info.cmake @@ -24,7 +24,7 @@ upx_print_var(CMAKE_APPLE_SILICON_PROCESSOR CMAKE_OSX_DEPLOYMENT_TARGET CMAKE_OS upx_print_Var(CMAKE_CROSSCOMPILING CMAKE_CROSSCOMPILING_EMULATOR) # binutils -upx_print_var(CMAKE_AR CMAKE_OBJCOPY CMAKE_RANLIB) +upx_print_var(CMAKE_AR CMAKE_RANLIB) # compilers foreach(lang IN ITEMS ASM C CXX) diff --git a/misc/podman/rebuild-stubs/Dockerfile b/misc/podman/rebuild-stubs/Dockerfile index bd938335..5f235fdc 100644 --- a/misc/podman/rebuild-stubs/Dockerfile +++ b/misc/podman/rebuild-stubs/Dockerfile @@ -19,7 +19,7 @@ RUN dpkg --add-architecture i386 \ libzstd-dev lsb-release lz4 lzip lzop minify mksh moreutils musl neovim ninja-build \ p7zip parallel patch patchelf patchutils pax-utils proot \ python3 python3-pyasn1 python3-pycryptodome python3-pycurl python3-toml python3-yaml python3-zstd \ - re2c ripgrep rsync screen universal-ctags unzip yash yq \ + re2c ripgrep rsync screen strace universal-ctags unzip valgrind yash yq \ zip zlib1g-dev zoxide zsh zstd \ # extra packages for compiling with "gcc -m32" and "gcc -mx32": g++-multilib gcc-multilib \ @@ -83,8 +83,11 @@ RUN useradd upx -U --uid 2000 --shell /bin/bash -m \ && echo "alias rd=rmdir" >> .bashrc \ && echo 'mdd() { mkdir "$1" && cd "$1"; }' >> .bashrc \ && echo 'm() { make "$@"; }' >> .bashrc \ + && echo 'tn() { time nice "$@"; }' >> .bashrc \ + && echo 'tnm() { time nice make "$@"; }' >> .bashrc \ + && echo 'tnmm() { time nice make -j "$@"; }' >> .bashrc \ && echo 'source /usr/share/bash-completion/completions/make' >> .bashrc \ - && echo 'complete -F _make m' >> .bashrc \ + && echo 'complete -F _make m tnm tnmm' >> .bashrc \ && echo 'eval "$(zoxide init bash)"' >> .bashrc \ && echo "alias zz=zi\nalias y=z\nalias yy=zi" >> .bashrc \ && echo "alias cd..='cd ..'" >> .bashrc \ diff --git a/misc/podman/rebuild-stubs/packages.txt b/misc/podman/rebuild-stubs/packages.txt index b04cef02..185e9ce4 100644 --- a/misc/podman/rebuild-stubs/packages.txt +++ b/misc/podman/rebuild-stubs/packages.txt @@ -109,6 +109,7 @@ ii libbz2-1.0:amd64 1.0.8-5.1 amd64 ii libbzip3-0:amd64 1.4.0-1 amd64 better, faster and stronger spiritual successor to bzip2 - runtime ii libc-bin 2.39-0ubuntu8.1 amd64 GNU C Library: Binaries ii libc-dev-bin 2.39-0ubuntu8.1 amd64 GNU C Library: Development binaries +ii libc6-dbg:amd64 2.39-0ubuntu8.1 amd64 GNU C Library: detached debugging symbols ii libc6-dev-i386 2.39-0ubuntu8.1 amd64 GNU C Library: 32-bit development libraries for AMD64 ii libc6-dev-x32 2.39-0ubuntu8.1 amd64 GNU C Library: X32 ABI Development Libraries for AMD64 ii libc6-dev:amd64 2.39-0ubuntu8.1 amd64 GNU C Library: Development Libraries and Header Files @@ -246,6 +247,7 @@ ii libuchardet0:amd64 0.0.8-1build1 amd64 ii libudev1:amd64 255.4-1ubuntu8 amd64 libudev shared library ii libunibilium4:amd64 2.1.0-3 amd64 simple, self-contained terminfo library ii libunistring5:amd64 1.1-2build1 amd64 Unicode string library for C +ii libunwind8:amd64 1.6.2-3build1 amd64 library to determine the call-chain of a program - runtime ii liburing2:amd64 2.5-1build1 amd64 Linux kernel io_uring access library - shared library ii libutempter0:amd64 1.2.1-3build1 amd64 privileged helper for utmp/wtmp updates (runtime) ii libuuid1:amd64 2.39.3-9ubuntu6 amd64 Universally Unique ID library @@ -330,6 +332,7 @@ ii rsync 3.2.7-1ubuntu1 amd64 ii screen 4.9.1-1build1 amd64 terminal multiplexer with VT100/ANSI terminal emulation ii sed 4.9-2build1 amd64 GNU stream editor for filtering/transforming text ii sensible-utils 0.0.22 all Utilities for sensible alternative selection +ii strace 6.8-0ubuntu2 amd64 System call tracer ii sysstat 12.6.1-2 amd64 system performance tools for Linux ii sysvinit-utils 3.08-6ubuntu3 amd64 System-V-like utilities ii tar 1.35+dfsg-3build1 amd64 GNU version of the tar archiving utility @@ -340,6 +343,7 @@ ii ucf 3.0043+nmu1 all ii universal-ctags 5.9.20210829.0-1 amd64 build tag file indexes of source code definitions ii unzip 6.0-28ubuntu4 amd64 De-archiver for .zip files ii util-linux 2.39.3-9ubuntu6 amd64 miscellaneous system utilities +ii valgrind 1:3.22.0-0ubuntu3 amd64 instrumentation framework for building dynamic analysis tools ii wget 1.21.4-1ubuntu4 amd64 retrieves files from the web ii xz-utils 5.6.1+really5.4.5-1 amd64 XZ-format compression utilities ii yash 2.55-2 amd64 yet another shell @@ -357,7 +361,8 @@ ii zstd 1.5.5+dfsg2-2build1 amd64 ||/ Name Version Architecture Description Packages sorted by Installed-Size: - 874252 ===== TOTAL (351 packages) + 964659 ===== TOTAL (355 packages) + 76943 valgrind amd64 72249 gcc-13-x86-64-linux-gnu amd64 37841 g++-13-x86-64-linux-gnu amd64 36493 cmake amd64 @@ -378,6 +383,7 @@ Packages sorted by Installed-Size: 12003 libc6-x32 amd64 11992 libc6-i386 amd64 11569 binutils-x86-64-linux-gnu amd64 + 11174 libc6-dbg amd64 10984 cmake-data all 10764 libasan8 amd64 9665 libx32asan8 amd64 @@ -438,6 +444,7 @@ Packages sorted by Installed-Size: 2191 ht amd64 2182 libc-bin amd64 2161 git-man all + 2103 strace amd64 2078 libxml2 amd64 2032 libisl23 amd64 2009 universal-ctags amd64 @@ -586,6 +593,7 @@ Packages sorted by Installed-Size: 197 libgcc-s1 amd64 196 libxcb1 amd64 196 libelf1t64 amd64 + 187 libunwind8 amd64 187 dash amd64 185 libgpg-error0 amd64 184 libx32gcc-s1 amd64 diff --git a/misc/testsuite/mimic_ctest.sh b/misc/testsuite/mimic_ctest.sh index dfde17f2..4d2d2d16 100755 --- a/misc/testsuite/mimic_ctest.sh +++ b/misc/testsuite/mimic_ctest.sh @@ -66,13 +66,14 @@ set_cmake_bool_vars() { } set -x -if [[ "${emu[0]}" == *valgrind* ]]; then true; # valgrind is SLOW - [[ -n $UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS ]] || UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS=ON -fi set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_SELF_PACK_TEST -set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_RUN_UNPACKED_TEST set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_RUN_PACKED_TEST +if [[ "${emu[0]}" == *valgrind* ]]; then # valgrind is SLOW + set_cmake_bool_vars ON UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS +else + set_cmake_bool_vars OFF UPX_CONFIG_DISABLE_EXHAUSTIVE_TESTS +fi export UPX="--prefer-ucl --no-color --no-progress" export UPX_DEBUG_DISABLE_GITREV_WARNING=1 diff --git a/src/check/dt_cxxlib.cpp b/src/check/dt_cxxlib.cpp index 252022ad..7ff4547f 100644 --- a/src/check/dt_cxxlib.cpp +++ b/src/check/dt_cxxlib.cpp @@ -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 o_deleter{&o, 1}; + auto o_deleter = upx::ObjectDeleter(&o, 1); o = new LE16; assert(o != nullptr); - upx::ArrayDeleter 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 o_deleter{&o, 1}; + const auto o_deleter = upx::ObjectDeleter(&o, 1); o = new LE16; assert(o != nullptr); - const upx::ArrayDeleter 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 o_deleter{o, 0}; - upx::ArrayDeleter 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); } } diff --git a/src/linker.cpp b/src/linker.cpp index 6c3263da..4206d6e5 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -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 -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(p); } template -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; diff --git a/src/pefile.cpp b/src/pefile.cpp index 4df6be30..d08d3f34 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -512,7 +512,7 @@ void PeFile32::processRelocs() // pass1 infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]); LE32 *fix[4]; - upx::ArrayDeleter 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 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 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 diff --git a/src/util/cxxlib.h b/src/util/cxxlib.h index e114908f..77d712f2 100644 --- a/src/util/cxxlib.h +++ b/src/util/cxxlib.h @@ -26,6 +26,7 @@ #pragma once +// #include // #include // #include // #include @@ -186,37 +187,76 @@ forceinline Result ptr_static_cast(const From *ptr) noexcept { return static_cast(static_cast(ptr)); } +#if WITH_THREADS +// cast "T *" to "std::atomic *" +template +forceinline std::atomic *ptr_std_atomic_cast(T *ptr) noexcept { + // TODO later: make sure that this cast is indeed legal + std::atomic *result = ptr_static_cast *>(ptr); + static_assert(sizeof(*result) == sizeof(*ptr)); + static_assert(alignof(*result) == alignof(*ptr)); + return result; +} +#endif // WITH_THREADS + +// atomic_exchange +template +forceinline T atomic_exchange(T *ptr, T new_value) noexcept { + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); +#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 // T is "SomeType **" +template struct ObjectDeleter final { - static_assert(std::is_pointer_v); - static_assert(std::is_pointer_v >); - 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 // T is "SomeType **" +template struct ArrayDeleter final { - static_assert(std::is_pointer_v); - static_assert(std::is_pointer_v >); - 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 +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 diff --git a/src/util/system_headers.h b/src/util/system_headers.h index a15d9fe6..887da7c0 100644 --- a/src/util/system_headers.h +++ b/src/util/system_headers.h @@ -136,19 +136,19 @@ static_assert(sizeof(void *) == sizeof(long)); #include #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