File indexing completed on 2024-03-03 05:55:51

0001 /*
0002     SPDX-FileCopyrightText: 2018 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
0008 #include "3rdparty/doctest.h"
0009 
0010 #include "tempfile.h"
0011 #include "tst_config.h"
0012 
0013 #include <benchutil.h>
0014 
0015 #include <dlfcn.h>
0016 
0017 #include <iostream>
0018 
0019 static_assert(RTLD_NOW == 0x2, "RTLD_NOW needs to equal 0x2");
0020 
0021 using heaptrack_inject_t = void (*)(const char*);
0022 using heaptrack_stop_t = void (*)();
0023 
0024 namespace {
0025 template <typename T>
0026 T resolveSymbol(void* handle, const char* symbol)
0027 {
0028     return reinterpret_cast<T>(dlsym(handle, symbol));
0029 }
0030 
0031 heaptrack_inject_t resolveHeaptrackInject(void* handle)
0032 {
0033     return resolveSymbol<heaptrack_inject_t>(handle, "heaptrack_inject");
0034 }
0035 
0036 heaptrack_stop_t resolveHeaptrackStop(void* handle)
0037 {
0038     return resolveSymbol<heaptrack_stop_t>(handle, "heaptrack_stop");
0039 }
0040 
0041 template <typename Load, typename Unload>
0042 void runInjectTest(Load load, Unload unload)
0043 {
0044     REQUIRE(!resolveHeaptrackInject(RTLD_DEFAULT));
0045     REQUIRE(!resolveHeaptrackStop(RTLD_DEFAULT));
0046 
0047     auto* handle = load();
0048     REQUIRE(handle);
0049 
0050     auto* heaptrack_inject = resolveHeaptrackInject(handle);
0051     REQUIRE(heaptrack_inject);
0052 
0053     auto* heaptrack_stop = resolveHeaptrackStop(handle);
0054     REQUIRE(heaptrack_stop);
0055 
0056     TempFile file;
0057 
0058     heaptrack_inject(file.fileName.c_str());
0059 
0060     auto* p = malloc(100);
0061     escape(p);
0062     free(p);
0063 
0064     heaptrack_stop();
0065 
0066     unload(handle);
0067 
0068     REQUIRE(!resolveHeaptrackInject(RTLD_DEFAULT));
0069     REQUIRE(!resolveHeaptrackStop(RTLD_DEFAULT));
0070 
0071     const auto contents = file.readContents();
0072     REQUIRE(!contents.empty());
0073     REQUIRE(contents.find("\nA\n") != std::string::npos);
0074     REQUIRE(contents.find("\n+") != std::string::npos);
0075     REQUIRE(contents.find("\n-") != std::string::npos);
0076 }
0077 }
0078 
0079 TEST_CASE ("inject via dlopen") {
0080     runInjectTest(
0081         []() -> void* {
0082             dlerror(); // clear error
0083             auto* handle = dlopen(HEAPTRACK_LIB_INJECT_SO, RTLD_NOW);
0084             if (!handle) {
0085                 std::cerr << "DLOPEN FAILED: " << dlerror() << std::endl;
0086             }
0087             return handle;
0088         },
0089         [](void* handle) { dlclose(handle); });
0090 }
0091 
0092 #ifdef __USE_GNU
0093 TEST_CASE ("inject via dlmopen") {
0094     runInjectTest(
0095         []() -> void* {
0096             dlerror(); // clear error
0097             auto* handle = dlmopen(LM_ID_BASE, HEAPTRACK_LIB_INJECT_SO, RTLD_NOW);
0098             if (!handle) {
0099                 std::cerr << "DLMOPEN FAILED: " << dlerror() << std::endl;
0100             }
0101             return handle;
0102         },
0103         [](void* handle) { dlclose(handle); });
0104 }
0105 #endif
0106 
0107 extern "C" {
0108 __attribute__((weak)) void* __libc_dlopen_mode(const char* filename, int flag);
0109 __attribute__((weak)) int __libc_dlclose(void* handle);
0110 }
0111 
0112 TEST_CASE ("inject via libc") {
0113     if (!__libc_dlopen_mode) {
0114         INFO("__libc_dlopen_mode symbol not available");
0115         return;
0116     }
0117 
0118     runInjectTest([]() { return __libc_dlopen_mode(HEAPTRACK_LIB_INJECT_SO, 0x80000000 | 0x002); },
0119                   [](void* handle) { __libc_dlclose(handle); });
0120 }