ClickHouse/base/base/phdr_cache.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

127 lines
3.2 KiB
C++
Raw Normal View History

#pragma clang diagnostic ignored "-Wreserved-identifier"
2022-04-15 22:28:56 +00:00
/// This code was based on the code by Fedor Korotkiy https://www.linkedin.com/in/fedor-korotkiy-659a1838/
2021-10-02 07:13:14 +00:00
#include <base/defines.h>
2019-07-25 19:54:16 +00:00
#if defined(OS_LINUX) && !defined(THREAD_SANITIZER) && !defined(USE_MUSL)
2019-07-25 22:35:47 +00:00
#define USE_PHDR_CACHE 1
2019-07-25 19:54:16 +00:00
#endif
2020-05-10 03:08:56 +00:00
/// Thread Sanitizer uses dl_iterate_phdr function on initialization and fails if we provide our own.
#ifdef USE_PHDR_CACHE
2024-03-03 23:11:55 +00:00
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#pragma clang diagnostic ignored "-Wunused-macros"
2020-09-18 23:04:36 +00:00
#define __msan_unpoison(X, Y) // NOLINT
#if defined(ch_has_feature)
# if ch_has_feature(memory_sanitizer)
# undef __msan_unpoison
# include <sanitizer/msan_interface.h>
# endif
2019-09-12 11:40:01 +00:00
#endif
2019-07-25 19:54:16 +00:00
2019-07-25 22:35:47 +00:00
#include <link.h>
#include <dlfcn.h>
#include <vector>
#include <atomic>
#include <cstddef>
#include <stdexcept>
2019-07-25 22:18:27 +00:00
namespace
{
// This is adapted from
// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.hh
// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.cc
using DLIterateFunction = int (*) (int (*callback) (dl_phdr_info * info, size_t size, void * data), void * data);
DLIterateFunction getOriginalDLIteratePHDR()
{
void * func = dlsym(RTLD_NEXT, "dl_iterate_phdr");
if (!func)
throw std::runtime_error("Cannot find dl_iterate_phdr function with dlsym");
return reinterpret_cast<DLIterateFunction>(func);
}
2019-07-24 15:35:19 +00:00
2019-07-25 19:54:16 +00:00
using PHDRCache = std::vector<dl_phdr_info>;
std::atomic<PHDRCache *> phdr_cache {};
}
extern "C"
int dl_iterate_phdr(int (*callback) (dl_phdr_info * info, size_t size, void * data), void * data)
{
2020-04-21 22:04:19 +00:00
auto * current_phdr_cache = phdr_cache.load();
2019-07-25 19:54:16 +00:00
if (!current_phdr_cache)
{
// Cache is not yet populated, pass through to the original function.
return getOriginalDLIteratePHDR()(callback, data);
}
int result = 0;
2019-07-25 19:54:16 +00:00
for (auto & entry : *current_phdr_cache)
{
2019-07-25 19:54:16 +00:00
result = callback(&entry, offsetof(dl_phdr_info, dlpi_adds), data);
if (result != 0)
break;
}
return result;
}
2019-07-25 22:35:47 +00:00
extern "C"
{
#ifdef ADDRESS_SANITIZER
void __lsan_ignore_object(const void *);
2019-07-25 22:35:47 +00:00
#else
void __lsan_ignore_object(const void *) {} // NOLINT
2019-07-25 22:18:27 +00:00
#endif
2019-07-25 22:35:47 +00:00
}
2019-07-25 22:18:27 +00:00
2019-07-25 19:54:16 +00:00
void updatePHDRCache()
{
// Fill out ELF header cache for access without locking.
// This assumes no dynamic object loading/unloading after this point
2019-07-25 19:54:16 +00:00
PHDRCache * new_phdr_cache = new PHDRCache;
getOriginalDLIteratePHDR()([] (dl_phdr_info * info, size_t /*size*/, void * data)
{
2019-09-12 11:40:01 +00:00
// `info` is created by dl_iterate_phdr, which is a non-instrumented
// libc function, so we have to unpoison it manually.
__msan_unpoison(info, sizeof(*info));
2019-07-25 19:54:16 +00:00
reinterpret_cast<PHDRCache *>(data)->push_back(*info);
return 0;
2019-07-25 19:54:16 +00:00
}, new_phdr_cache);
phdr_cache.store(new_phdr_cache);
/// Memory is intentionally leaked.
__lsan_ignore_object(new_phdr_cache);
2019-07-25 22:35:47 +00:00
}
2019-07-25 19:54:16 +00:00
2019-07-25 22:35:47 +00:00
bool hasPHDRCache()
{
return phdr_cache.load() != nullptr;
}
2019-07-25 22:35:47 +00:00
#else
void updatePHDRCache() {}
2021-12-24 04:36:23 +00:00
#if defined(USE_MUSL)
/// With statically linked with musl, dl_iterate_phdr is immutable.
bool hasPHDRCache() { return true; }
#else
bool hasPHDRCache() { return false; }
#endif
2019-07-25 22:35:47 +00:00
#endif