From bb2fc7e4bbe354f16a39b47c5fca77da0198acc9 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Tue, 3 Jun 2014 12:15:43 +0000 Subject: [PATCH] [sancov] Fix map update logic on Android. dlopen()/dlclose() are not interceptable on Android, so we update .sancov.map in module constructor callbacks. llvm-svn: 210098 --- .../sanitizer_common/sanitizer_coverage_libcdep.cc | 6 +++ .../sanitizer_coverage_mapping_libcdep.cc | 59 ++++++++++++---------- .../test/asan/TestCases/Android/coverage.cc | 41 +++++++++++++++ .../test/asan/TestCases/Android/lit.local.cfg | 11 ++++ 4 files changed, 89 insertions(+), 28 deletions(-) create mode 100644 compiler-rt/test/asan/TestCases/Android/coverage.cc create mode 100644 compiler-rt/test/asan/TestCases/Android/lit.local.cfg diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index e1fa05d..0094c61 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -146,6 +146,12 @@ void CoverageData::Extend(uptr npcs) { } atomic_store(&pc_array_size, size, memory_order_release); + + if (SANITIZER_ANDROID) { + // dlopen/dlclose interceptors do not work on Android, so we rely on + // Extend() calls to update .sancov.map. + CovUpdateMapping(); + } } // Simply add the pc into the vector under lock. If the function is called more diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index e814c2e..b3c8dc8 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -37,22 +37,16 @@ namespace __sanitizer { static const uptr kMaxNumberOfModules = 1 << 14; +static char *last_mapping; +static StaticSpinMutex mapping_mu; + void CovUpdateMapping() { if (!common_flags()->coverage || !common_flags()->coverage_direct) return; - int err; - InternalScopedString tmp_path(64 + - internal_strlen(common_flags()->coverage_dir)); - uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(), - "%s/%zd.sancov.map.tmp", common_flags()->coverage_dir, - internal_getpid()); - CHECK_LE(res, tmp_path.size()); - uptr map_fd = OpenFile(tmp_path.data(), true); - if (internal_iserror(map_fd)) { - Report(" Coverage: failed to open %s for writing\n", tmp_path.data()); - Die(); - } + SpinMutexLock l(&mapping_mu); + const uptr kMaxTextSize = 64 * 1024; + InternalScopedString text(kMaxTextSize); InternalScopedBuffer modules_data(kMaxNumberOfModules * sizeof(LoadedModule)); LoadedModule *modules = (LoadedModule *)modules_data.data(); @@ -60,31 +54,40 @@ void CovUpdateMapping() { int n_modules = GetListOfModules(modules, kMaxNumberOfModules, /* filter */ 0); - InternalScopedString line(4096); - line.append("%d\n", sizeof(uptr) * 8); - res = internal_write(map_fd, line.data(), line.length()); - if (internal_iserror(res, &err)) { - Printf("sancov.map write failed: %d\n", err); - Die(); - } - line.clear(); - + text.append("%d\n", sizeof(uptr) * 8); for (int i = 0; i < n_modules; ++i) { char *module_name = StripModuleName(modules[i].full_name()); for (unsigned j = 0; j < modules[i].n_ranges(); ++j) { - line.append("%zx %zx %zx %s\n", modules[i].address_range_start(j), + text.append("%zx %zx %zx %s\n", modules[i].address_range_start(j), modules[i].address_range_end(j), modules[i].base_address(), module_name); - res = internal_write(map_fd, line.data(), line.length()); - if (internal_iserror(res, &err)) { - Printf("sancov.map write failed: %d\n", err); - Die(); - } - line.clear(); } InternalFree(module_name); } + // Do not write mapping if it is the same as the one we've wrote last time. + if (last_mapping && (internal_strcmp(last_mapping, text.data()) == 0)) return; + if (!last_mapping) last_mapping = (char *)InternalAlloc(kMaxTextSize); + internal_strncpy(last_mapping, text.data(), kMaxTextSize); + + int err; + InternalScopedString tmp_path(64 + + internal_strlen(common_flags()->coverage_dir)); + uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(), + "%s/%zd.sancov.map.tmp", common_flags()->coverage_dir, + internal_getpid()); + CHECK_LE(res, tmp_path.size()); + uptr map_fd = OpenFile(tmp_path.data(), true); + if (internal_iserror(map_fd)) { + Report(" Coverage: failed to open %s for writing\n", tmp_path.data()); + Die(); + } + + res = internal_write(map_fd, text.data(), text.length()); + if (internal_iserror(res, &err)) { + Printf("sancov.map write failed: %d\n", err); + Die(); + } internal_close(map_fd); InternalScopedString path(64 + internal_strlen(common_flags()->coverage_dir)); diff --git a/compiler-rt/test/asan/TestCases/Android/coverage.cc b/compiler-rt/test/asan/TestCases/Android/coverage.cc new file mode 100644 index 0000000..97f122e --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Android/coverage.cc @@ -0,0 +1,41 @@ +// Test for direct coverage writing with dlopen. +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSO_DIR=\"%device\" %s -o %t + +// RUN: adb shell rm -rf %device/coverage-direct +// RUN: rm -rf %T/coverage-direct + +// RUN: adb shell mkdir -p %device/coverage-direct/direct +// RUN: mkdir -p %T/coverage-direct/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-direct/direct:verbosity=1 %run %t +// RUN: adb pull %device/coverage-direct/direct %T/coverage-direct/direct +// RUN: ls; pwd +// RUN: cd %T/coverage-direct/direct +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov |& FileCheck %s + +#include +#include +#include +#include + +#ifdef SHARED +extern "C" { +void bar() { printf("bar\n"); } +} +#else + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + void *handle1 = + dlopen(SO_DIR "/libcoverage_direct_test_1.so", RTLD_LAZY); + assert(handle1); + void (*bar1)() = (void (*)())dlsym(handle1, "bar"); + assert(bar1); + bar1(); + + return 0; +} +#endif + +// CHECK: 2 PCs total diff --git a/compiler-rt/test/asan/TestCases/Android/lit.local.cfg b/compiler-rt/test/asan/TestCases/Android/lit.local.cfg new file mode 100644 index 0000000..42513dd --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Android/lit.local.cfg @@ -0,0 +1,11 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.android != "TRUE": + config.unsupported = True + +config.substitutions.append( ("%device", "/data/local/tmp/Output") ) -- 2.7.4