Add support for custom loaders to the sanitizer symbolizer

Summary:
Adds a fallback mode to procmaps when the symbolizer
fails to locate a module for a given address by using
dl_iterate_phdr.

Reviewers: kubamracek, rnk, vitalybuka, eugenis

Reviewed By: eugenis

Subscribers: srhines, llvm-commits

Differential Revision: https://reviews.llvm.org/D37269

llvm-svn: 314431
This commit is contained in:
Francis Ricci 2017-09-28 16:58:35 +00:00
parent fd6b8a67fb
commit b9a32d470a
9 changed files with 80 additions and 33 deletions

View File

@ -727,9 +727,10 @@ class LoadedModule {
// filling this information.
class ListOfModules {
public:
ListOfModules() : modules_(kInitialCapacity) {}
ListOfModules() : initialized(false) {}
~ListOfModules() { clear(); }
void init();
void fallbackInit(); // Uses fallback init if available, otherwise clears
const LoadedModule *begin() const { return modules_.begin(); }
LoadedModule *begin() { return modules_.begin(); }
const LoadedModule *end() const { return modules_.end(); }
@ -745,10 +746,15 @@ class ListOfModules {
for (auto &module : modules_) module.clear();
modules_.clear();
}
void clearOrInit() {
initialized ? clear() : modules_.Initialize(kInitialCapacity);
initialized = true;
}
InternalMmapVector<LoadedModule> modules_;
InternalMmapVectorNoCtor<LoadedModule> modules_;
// We rarely have more than 16K loaded modules.
static const uptr kInitialCapacity = 1 << 14;
bool initialized;
};
// Callback type for iterating over a set of memory ranges.

View File

@ -26,6 +26,7 @@
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
#include <dlfcn.h> // for dlsym()
#include <link.h>
@ -424,7 +425,7 @@ typedef ElfW(Phdr) Elf_Phdr;
# endif
struct DlIteratePhdrData {
InternalMmapVector<LoadedModule> *modules;
InternalMmapVectorNoCtor<LoadedModule> *modules;
bool first;
};
@ -462,21 +463,37 @@ extern "C" __attribute__((weak)) int dl_iterate_phdr(
int (*)(struct dl_phdr_info *, size_t, void *), void *);
#endif
void ListOfModules::init() {
clear();
static bool requiresProcmaps() {
#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
u32 api_level = AndroidGetApiLevel();
// Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
// The runtime check allows the same library to work with
// both K and L (and future) Android releases.
if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier
MemoryMappingLayout memory_mapping(false);
memory_mapping.DumpListOfModules(&modules_);
return;
}
return AndroidGetApiLevel() <= ANDROID_LOLLIPOP_MR1;
#else
return false;
#endif
DlIteratePhdrData data = {&modules_, true};
dl_iterate_phdr(dl_iterate_phdr_cb, &data);
}
static void procmapsInit(InternalMmapVectorNoCtor<LoadedModule> *modules) {
MemoryMappingLayout memory_mapping(false);
memory_mapping.DumpListOfModules(modules);
}
void ListOfModules::init() {
clearOrInit();
if (requiresProcmaps()) {
procmapsInit(&modules_);
} else {
DlIteratePhdrData data = {&modules_, true};
dl_iterate_phdr(dl_iterate_phdr_cb, &data);
}
}
// When a custom loader is used, dl_iterate_phdr may not contain the full
// list of modules. Allow callers to fall back to using procmaps.
void ListOfModules::fallbackInit() {
clearOrInit();
if (!requiresProcmaps()) procmapsInit(&modules_);
}
// getrusage does not give us the current RSS, only the max RSS.

View File

@ -411,11 +411,13 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
}
void ListOfModules::init() {
clear();
clearOrInit();
MemoryMappingLayout memory_mapping(false);
memory_mapping.DumpListOfModules(&modules_);
}
void ListOfModules::fallbackInit() { clear(); }
static HandleSignalMode GetHandleSignalModeImpl(int signum) {
switch (signum) {
case SIGABRT:

View File

@ -76,7 +76,7 @@ class MemoryMappingLayout {
static void CacheMemoryMappings();
// Adds all mapped objects into a vector.
void DumpListOfModules(InternalMmapVector<LoadedModule> *modules);
void DumpListOfModules(InternalMmapVectorNoCtor<LoadedModule> *modules);
private:
void LoadFromCache();

View File

@ -120,7 +120,7 @@ void MemoryMappingLayout::LoadFromCache() {
}
void MemoryMappingLayout::DumpListOfModules(
InternalMmapVector<LoadedModule> *modules) {
InternalMmapVectorNoCtor<LoadedModule> *modules) {
Reset();
InternalScopedString module_name(kMaxPathLength);
MemoryMappedSegment segment(module_name.data(), module_name.size());

View File

@ -353,7 +353,7 @@ bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
}
void MemoryMappingLayout::DumpListOfModules(
InternalMmapVector<LoadedModule> *modules) {
InternalMmapVectorNoCtor<LoadedModule> *modules) {
Reset();
InternalScopedString module_name(kMaxPathLength);
MemoryMappedSegment segment(module_name.data(), kMaxPathLength);

View File

@ -119,6 +119,7 @@ class Symbolizer final {
void AddHooks(StartSymbolizationHook start_hook,
EndSymbolizationHook end_hook);
void RefreshModules();
const LoadedModule *FindModuleForAddress(uptr address);
void InvalidateModuleList();
@ -151,6 +152,7 @@ class Symbolizer final {
uptr *module_offset,
ModuleArch *module_arch);
ListOfModules modules_;
ListOfModules fallback_modules_;
// If stale, need to reload the modules before looking up addresses.
bool modules_fresh_;

View File

@ -163,29 +163,47 @@ bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
return true;
}
void Symbolizer::RefreshModules() {
modules_.init();
fallback_modules_.fallbackInit();
RAW_CHECK(modules_.size() > 0);
modules_fresh_ = true;
}
static const LoadedModule *SearchForModule(const ListOfModules &modules,
uptr address) {
for (uptr i = 0; i < modules.size(); i++) {
if (modules[i].containsAddress(address)) {
return &modules[i];
}
}
return nullptr;
}
const LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
bool modules_were_reloaded = false;
if (!modules_fresh_) {
modules_.init();
RAW_CHECK(modules_.size() > 0);
modules_fresh_ = true;
RefreshModules();
modules_were_reloaded = true;
}
for (uptr i = 0; i < modules_.size(); i++) {
if (modules_[i].containsAddress(address)) {
return &modules_[i];
}
}
// dlopen/dlclose interceptors invalidate the module list, but when
// interception is disabled, we need to retry if the lookup fails in
// case the module list changed.
const LoadedModule *module = SearchForModule(modules_, address);
if (module) return module;
// dlopen/dlclose interceptors invalidate the module list, but when
// interception is disabled, we need to retry if the lookup fails in
// case the module list changed.
#if !SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
if (!modules_were_reloaded) {
modules_fresh_ = false;
return FindModuleForAddress(address);
RefreshModules();
module = SearchForModule(modules_, address);
if (module) return module;
}
#endif
return 0;
if (fallback_modules_.size()) {
module = SearchForModule(fallback_modules_, address);
}
return module;
}
// For now we assume the following protocol:

View File

@ -524,7 +524,7 @@ static uptr GetPreferredBase(const char *modname) {
}
void ListOfModules::init() {
clear();
clearOrInit();
HANDLE cur_process = GetCurrentProcess();
// Query the list of modules. Start by assuming there are no more than 256
@ -583,7 +583,9 @@ void ListOfModules::init() {
modules_.push_back(cur_module);
}
UnmapOrDie(hmodules, modules_buffer_size);
};
}
void ListOfModules::fallbackInit() { clear(); }
// We can't use atexit() directly at __asan_init time as the CRT is not fully
// initialized at this point. Place the functions into a vector and use