Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -146,6 +146,8 @@ } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename) CovUpdateMapping() +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping() #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -5,6 +5,7 @@ sanitizer_allocator.cc sanitizer_common.cc sanitizer_coverage.cc + sanitizer_coverage_direct.cc sanitizer_deadlock_detector1.cc sanitizer_deadlock_detector2.cc sanitizer_flags.cc @@ -55,6 +56,7 @@ sanitizer_common_interceptors_ioctl.inc sanitizer_common_interceptors_format.inc sanitizer_common_syscalls.inc + sanitizer_coverage_direct.h sanitizer_deadlock_detector.h sanitizer_deadlock_detector_interface.h sanitizer_flags.h Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -161,6 +161,7 @@ // (or NULL if the mapping failes). Stores the size of mmaped region // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); +void *MapWritableFileToMemory(uptr fd, uptr offset, uptr size); // Error report formatting. const char *StripPathPrefix(const char *filepath, @@ -187,6 +188,8 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args); void SetSandboxingCallback(void (*f)()); +void CovUpdateMapping(); + void InitTlsSize(); uptr GetTlsSize(); @@ -479,6 +482,10 @@ const char *full_name() const { return full_name_; } uptr base_address() const { return base_address_; } + uptr n_ranges() const { return n_ranges_; } + uptr address_range_start(int i) const { return ranges_[i].beg; } + uptr address_range_end(int i) const { return ranges_[i].end; } + private: struct AddressRange { uptr beg; Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -75,6 +75,14 @@ #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) {} #endif +#ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename) {} +#endif + +#ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() {} +#endif + struct FileMetadata { // For open_memstream(). char **addr; @@ -4036,6 +4044,29 @@ #define INIT_FCLOSE #endif +#if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE +INTERCEPTOR(void*, dlopen, const char *filename, int flag) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, dlopen, filename, flag); + void *res = REAL(dlopen)(filename, flag); + COMMON_INTERCEPTOR_LIBRARY_LOADED(filename); + return res; +} + +INTERCEPTOR(int, dlclose, void *handle) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, dlclose, handle); + int res = REAL(dlclose)(handle); + COMMON_INTERCEPTOR_LIBRARY_UNLOADED(); + return res; +} +#define INIT_DLOPEN_DLCLOSE \ + COMMON_INTERCEPT_FUNCTION(dlopen); \ + COMMON_INTERCEPT_FUNCTION(dlclose); +#else +#define INIT_DLOPEN_DLCLOSE +#endif + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); @@ -4177,4 +4208,5 @@ INIT_OBSTACK; INIT_FFLUSH; INIT_FCLOSE; + INIT_DLOPEN_DLCLOSE; } Index: lib/sanitizer_common/sanitizer_coverage.cc =================================================================== --- lib/sanitizer_common/sanitizer_coverage.cc +++ lib/sanitizer_common/sanitizer_coverage.cc @@ -34,6 +34,7 @@ #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" +#include "sanitizer_coverage_direct.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_procmaps.h" @@ -60,6 +61,10 @@ // Simply add the pc into the vector under lock. If the function is called more // than once for a given PC it will be inserted multiple times, which is fine. static void CovAdd(uptr pc) { + if (common_flags()->coverage_direct) { + CovDirectAdd(pc); + return; + } if (!pc_array) return; uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); CHECK_LT(idx, kPcArraySize); @@ -67,6 +72,10 @@ } void CovInit() { + if (common_flags()->coverage_direct) { + CovDirectInit(); + return; + } pc_array = reinterpret_cast( MmapNoReserveOrDie(sizeof(uptr) * kPcArraySize, "CovInit")); } @@ -124,7 +133,7 @@ // Dump the coverage on disk. static void CovDump() { - if (!common_flags()->coverage) return; + if (!common_flags()->coverage || common_flags()->coverage_direct) return; #if !SANITIZER_WINDOWS if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) return; @@ -205,6 +214,12 @@ OpenPackedFileForWriting(); } +void CovUpdateMapping() { + if (common_flags()->coverage && common_flags()->coverage_direct) + CovDirectUpdateMapping(); +} + + } // namespace __sanitizer extern "C" { Index: lib/sanitizer_common/sanitizer_coverage_direct.h =================================================================== --- lib/sanitizer_common/sanitizer_coverage_direct.h +++ lib/sanitizer_common/sanitizer_coverage_direct.h @@ -0,0 +1,26 @@ +//===-- sanitizer_coverage_direct.h ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Mmap-based implementation of sanitizer coverage. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_COVERAGE_DIRECT_H +#define SANITIZER_COVERAGE_DIRECT_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +void CovDirectAdd(uptr pc); +void CovDirectInit(); +void CovDirectUpdateMapping(); + +} // namespace __sanitizer + +#endif // SANITIZER_COVERAGE_DIRECT_H Index: lib/sanitizer_common/sanitizer_coverage_direct.cc =================================================================== --- lib/sanitizer_common/sanitizer_coverage_direct.cc +++ lib/sanitizer_common/sanitizer_coverage_direct.cc @@ -0,0 +1,143 @@ +//===-- sanitizer_coverage_direct.cc --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Mmap-based implementation of sanitizer coverage. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_coverage_direct.h" + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" + +namespace __sanitizer { + +class CoverageMap { + public: + void Init(); + void Add(uptr pc); + void UpdateMapping(); + + private: + uptr raw_fd; // .sancov.raw handle + atomic_uintptr_t pc_next; // next pc store location + atomic_uintptr_t pc_end; // end of the mapped area + uptr next_file_offset; // file offset of the end of the mapped area + StaticSpinMutex mu; + + static const uptr kMmapSize = 64*1024; + static const uptr kMaxNumberOfModules = 1 << 14; + + bool TryAdd(uptr pc); + uptr Extend(); +}; + +static CoverageMap coverage_map; + +uptr CoverageMap::Extend() { + uptr res = internal_ftruncate(raw_fd, next_file_offset + kMmapSize); + CHECK_EQ(res, 0); + uptr p = (uptr)MapWritableFileToMemory(raw_fd, next_file_offset, kMmapSize); + CHECK_NE(p, (uptr)-1); + CHECK(p); + next_file_offset += kMmapSize; + return p; +} + +bool CoverageMap::TryAdd(uptr pc) { + for (;;) { + uptr cmp = atomic_load(&pc_next, memory_order_acquire); + uptr end = atomic_load(&pc_end, memory_order_acquire); + if (cmp == 0 || cmp >= end) return false; + if (atomic_compare_exchange_weak(&pc_next, &cmp, cmp + sizeof(uptr), + memory_order_acquire)) { + *(uptr *)cmp = pc; + return true; + } + } +} + +void CoverageMap::Add(uptr pc) { + if (TryAdd(pc)) return; + SpinMutexLock l(&mu); + for (;;) { + if (TryAdd(pc)) return; + atomic_store(&pc_next, 0, memory_order_release); + uptr p = Extend(); + atomic_store(&pc_end, p + kMmapSize, memory_order_release); + atomic_store(&pc_next, p, memory_order_release); + } +} + +void CoverageMap::Init() { + InternalScopedString path(1024); + internal_snprintf((char *)path.data(), path.size(), "%zd.sancov.raw", + internal_getpid()); + raw_fd = OpenFile(path.data(), true); + if (internal_iserror(raw_fd)) { + Report(" Coverage: failed to open %s for writing\n", path.data()); + Die(); + } + + UpdateMapping(); +} + +void CoverageMap::UpdateMapping() { + InternalScopedString path(1024); + internal_snprintf((char *)path.data(), path.size(), "%zd.sancov.map", + internal_getpid()); + uptr map_fd = OpenFile(path.data(), true); + if (internal_iserror(map_fd)) { + Report(" Coverage: failed to open %s for writing\n", path.data()); + Die(); + } + + InternalScopedBuffer modules_data(kMaxNumberOfModules * + sizeof(LoadedModule)); + LoadedModule *modules = (LoadedModule *)modules_data.data(); + CHECK(modules); + int n_modules = GetListOfModules(modules, kMaxNumberOfModules, + /* filter */ 0); + + InternalScopedString line(4096); + line.append("%d\n", sizeof(uptr) * 8); + uptr res = internal_write(map_fd, line.data(), line.length()); + CHECK_EQ(res, line.length()); + line.clear(); + + 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), + modules[i].address_range_end(j), modules[i].base_address(), + module_name); + uptr res = internal_write(map_fd, line.data(), line.length()); + CHECK_EQ(res, line.length()); + line.clear(); + } + InternalFree(module_name); + } + + internal_close(map_fd); +} + +void CovDirectUpdateMapping() { + coverage_map.UpdateMapping(); +} + +void CovDirectAdd(uptr pc) { + coverage_map.Add(pc); +} + +void CovDirectInit() { + coverage_map.Init(); +} + +} // namespace __sanitizer Index: lib/sanitizer_common/sanitizer_flags.h =================================================================== --- lib/sanitizer_common/sanitizer_flags.h +++ lib/sanitizer_common/sanitizer_flags.h @@ -54,6 +54,7 @@ bool help; uptr mmap_limit_mb; bool coverage; + bool coverage_direct; bool full_address_space; }; Index: lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- lib/sanitizer_common/sanitizer_flags.cc +++ lib/sanitizer_common/sanitizer_flags.cc @@ -55,6 +55,7 @@ f->legacy_pthread_cond = false; f->intercept_tls_get_addr = false; f->coverage = false; + f->coverage_direct = false; f->full_address_space = false; } @@ -127,6 +128,9 @@ ParseFlag(str, &f->coverage, "coverage", "If set, coverage information will be dumped at program shutdown (if the " "coverage instrumentation was enabled at compile time)."); + ParseFlag(str, &f->coverage_direct, "coverage_direct", + "If set, coverage information will be dumped directly to a memory " + "mapped file."); ParseFlag(str, &f->full_address_space, "full_address_space", "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized"); Index: lib/sanitizer_common/sanitizer_libc.h =================================================================== --- lib/sanitizer_common/sanitizer_libc.h +++ lib/sanitizer_common/sanitizer_libc.h @@ -74,6 +74,7 @@ uptr internal_read(fd_t fd, void *buf, uptr count); uptr internal_write(fd_t fd, const void *buf, uptr count); +uptr internal_ftruncate(fd_t fd, uptr size); // OS uptr internal_filesize(fd_t fd); // -1 on error. Index: lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- lib/sanitizer_common/sanitizer_linux.cc +++ lib/sanitizer_common/sanitizer_linux.cc @@ -133,8 +133,8 @@ } uptr OpenFile(const char *filename, bool write) { - return internal_open(filename, - write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); + return internal_open( + filename, write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); } uptr internal_read(fd_t fd, void *buf, uptr count) { @@ -151,6 +151,12 @@ return res; } +uptr internal_ftruncate(fd_t fd, uptr size) { + sptr res; + HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size)); + return res; +} + #if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD static void stat64_to_stat(struct stat64 *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -203,5 +203,6 @@ #define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE SI_NOT_WINDOWS #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H Index: lib/sanitizer_common/sanitizer_posix.cc =================================================================== --- lib/sanitizer_common/sanitizer_posix.cc +++ lib/sanitizer_common/sanitizer_posix.cc @@ -206,6 +206,16 @@ return internal_iserror(map) ? 0 : (void *)map; } +void *MapWritableFileToMemory(uptr fd, uptr offset, uptr size) { + uptr p = + internal_mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); + if (internal_iserror(p)) { + Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset, + size, p); + return 0; + } + return (void *)p; +} static inline bool IntervalsAreSeparate(uptr start1, uptr end1, uptr start2, uptr end2) { Index: lib/sanitizer_common/scripts/sancov.py =================================================================== --- lib/sanitizer_common/scripts/sancov.py +++ lib/sanitizer_common/scripts/sancov.py @@ -6,6 +6,8 @@ import array import struct import sys +import bisect +import os.path prog_name = ""; @@ -74,6 +76,63 @@ for f in files: UnpackOneFile(f) +def UnpackOneRawFile(path, map_path): + mem_map = [] + with open(map_path, mode="rt") as f_map: + print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path) + bits = int(f_map.readline()) + for line in f_map: + parts = line.rstrip().split() + assert len(parts) == 4 + mem_map.append((int(parts[0], 16), + int(parts[1], 16), + int(parts[2], 16), + parts[3])) + mem_map.sort(key=lambda m : m[0]) + mem_map_keys = [m[0] for m in mem_map] + + print mem_map + with open(path, mode="rb") as f: + print >> sys.stderr, "%s: unpacking %s" % (prog_name, path) + + f.seek(0, 2) + size = f.tell() + f.seek(0, 0) + if bits == 64: + typecode = 'L' + else: + typecode = 'I' + pcs = array.array(typecode, f.read(size)) + mem_map_pcs = [[] for i in range(0, len(mem_map))] + + for pc in pcs: + if pc == 0: continue + map_idx = bisect.bisect(mem_map_keys, pc) - 1 + (start, end, base, module_path) = mem_map[map_idx] + print pc + print start, end, base, module_path + assert pc >= start + if pc >= end: + print >> sys.stderr, "warning: %s: pc %x outside of any known mapping" % (prog_name, pc) + continue + mem_map_pcs[map_idx].append(pc - base) + + for ((start, end, base, module_path), pc_list) in zip(mem_map, mem_map_pcs): + if len(pc_list) == 0: continue + assert path.endswith('.sancov.raw') + dst_path = module_path + '.' + os.path.basename(path)[:-4] + print "writing %d PCs to %s" % (len(pc_list), dst_path) + arr = array.array('I') + arr.fromlist(sorted(pc_list)) + with open(dst_path, 'ab') as f2: + arr.tofile(f2) + +def RawUnpack(files): + for f in files: + if not f.endswith('.sancov.raw'): + raise Exception('Unexpected raw file name %s' % f) + f_map = f[:-3] + 'map' + UnpackOneRawFile(f, f_map) if __name__ == '__main__': prog_name = sys.argv[0] @@ -85,5 +144,7 @@ MergeAndPrint(sys.argv[2:]) elif sys.argv[1] == "unpack": Unpack(sys.argv[2:]) + elif sys.argv[1] == "rawunpack": + RawUnpack(sys.argv[2:]) else: Usage() Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -259,20 +259,6 @@ return res; } -TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { - SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); - void *res = REAL(dlopen)(filename, flag); - libignore()->OnLibraryLoaded(filename); - return res; -} - -TSAN_INTERCEPTOR(int, dlclose, void *handle) { - SCOPED_INTERCEPTOR_RAW(dlclose, handle); - int res = REAL(dlclose)(handle); - libignore()->OnLibraryUnloaded(); - return res; -} - class AtExitContext { public: AtExitContext() @@ -2034,6 +2020,12 @@ if (fd >= 0) FdClose(thr, pc, fd); \ } +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename) \ + libignore()->OnLibraryLoaded(filename) + +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \ + libignore()->OnLibraryUnloaded() + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) @@ -2376,8 +2368,6 @@ TSAN_INTERCEPT(fork); TSAN_INTERCEPT(vfork); - TSAN_INTERCEPT(dlopen); - TSAN_INTERCEPT(dlclose); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); Index: test/asan/TestCases/Linux/coverage-direct-large.cc =================================================================== --- test/asan/TestCases/Linux/coverage-direct-large.cc +++ test/asan/TestCases/Linux/coverage-direct-large.cc @@ -0,0 +1,45 @@ +// Test for direct coverage writing with lots of data. +// Current implementation maps output file in chunks of 64K. This test overflows +// 1 chunk. +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -O0 %s -o %t + +// RUN: rm -rf %T/coverage-direct-large + +// RUN: mkdir -p %T/coverage-direct-large/normal && cd %T/coverage-direct-large/normal +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1 %run %t +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: mkdir -p %T/coverage-direct-large/direct && cd %T/coverage-direct-large/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: diff -u coverage-direct-large/normal/out.txt coverage-direct-large/direct/out.txt +// +// XFAIL: android + +#define F0(Q, x) Q(x) +#define F1(Q, x) \ + F0(Q, x##0) F0(Q, x##1) F0(Q, x##2) F0(Q, x##3) F0(Q, x##4) F0(Q, x##5) \ + F0(Q, x##6) F0(Q, x##7) F0(Q, x##8) F0(Q, x##9) +#define F2(Q, x) \ + F1(Q, x##0) F1(Q, x##1) F1(Q, x##2) F1(Q, x##3) F1(Q, x##4) F1(Q, x##5) \ + F1(Q, x##6) F1(Q, x##7) F1(Q, x##8) F1(Q, x##9) +#define F3(Q, x) \ + F2(Q, x##0) F2(Q, x##1) F2(Q, x##2) F2(Q, x##3) F2(Q, x##4) F2(Q, x##5) \ + F2(Q, x##6) F2(Q, x##7) F2(Q, x##8) F2(Q, x##9) +#define F4(Q, x) \ + F3(Q, x##0) F3(Q, x##1) F3(Q, x##2) F3(Q, x##3) F3(Q, x##4) F3(Q, x##5) \ + F3(Q, x##6) F3(Q, x##7) F3(Q, x##8) F3(Q, x##9) + +#define DECL(x) __attribute__((noinline)) void x() {} +#define CALL(x) x(); + +F4(DECL, f) + +int main(void) { + F4(CALL, f) + return 0; +} Index: test/asan/TestCases/Linux/coverage-direct.cc =================================================================== --- test/asan/TestCases/Linux/coverage-direct.cc +++ test/asan/TestCases/Linux/coverage-direct.cc @@ -0,0 +1,44 @@ +// 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=\"%T\" %s -o %t + +// RUN: rm -rf %T/coverage-direct + +// RUN: mkdir -p %T/coverage-direct/normal && cd %T/coverage-direct/normal +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1 %run %t +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: mkdir -p %T/coverage-direct/direct && cd %T/coverage-direct/direct +// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t +// RUN: %sancov rawunpack *.sancov.raw +// RUN: %sancov print *.sancov >out.txt +// RUN: cd ../.. + +// RUN: diff -u coverage-direct/normal/out.txt coverage-direct/direct/out.txt +// +// XFAIL: android + +#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