Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 1 | //===-- asan_mac.cc -------------------------------------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file is a part of AddressSanitizer, an address sanity checker. |
| 11 | // |
| 12 | // Mac-specific details. |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
Kostya Serebryany | 5dfa4da | 2011-12-01 21:40:52 | [diff] [blame] | 15 | #ifdef __APPLE__ |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 16 | |
| 17 | #include "asan_mac.h" |
| 18 | |
| 19 | #include "asan_internal.h" |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 20 | #include "asan_procmaps.h" |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 21 | #include "asan_stack.h" |
| 22 | #include "asan_thread.h" |
| 23 | #include "asan_thread_registry.h" |
| 24 | |
Alexander Potapenko | 553c208 | 2012-01-13 12:59:48 | [diff] [blame] | 25 | #include <crt_externs.h> // for _NSGetEnviron |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 26 | #include <mach-o/dyld.h> |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 27 | #include <sys/mman.h> |
Kostya Serebryany | 2b08718 | 2012-01-06 02:12:25 | [diff] [blame] | 28 | #include <sys/resource.h> |
Kostya Serebryany | 25d6c1b | 2012-01-06 19:11:09 | [diff] [blame] | 29 | #include <sys/ucontext.h> |
Kostya Serebryany | 78d87d3 | 2012-01-05 01:07:27 | [diff] [blame] | 30 | #include <pthread.h> |
Kostya Serebryany | a772096 | 2011-12-28 23:28:54 | [diff] [blame] | 31 | #include <fcntl.h> |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 32 | #include <unistd.h> |
Kostya Serebryany | a82f0d4 | 2012-01-10 21:24:40 | [diff] [blame] | 33 | #include <libkern/OSAtomic.h> |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 34 | |
| 35 | namespace __asan { |
| 36 | |
Alexander Potapenko | bd53f59 | 2012-01-13 16:13:58 | [diff] [blame] | 37 | void *island_allocator_pos = NULL; |
| 38 | |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 39 | extern dispatch_async_f_f real_dispatch_async_f; |
| 40 | extern dispatch_sync_f_f real_dispatch_sync_f; |
| 41 | extern dispatch_after_f_f real_dispatch_after_f; |
| 42 | extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f; |
| 43 | extern dispatch_group_async_f_f real_dispatch_group_async_f; |
| 44 | extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np; |
Alexander Potapenko | 046ecc0 | 2012-01-23 10:09:54 | [diff] [blame] | 45 | extern CFStringCreateCopy_f real_CFStringCreateCopy; |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 46 | |
Kostya Serebryany | 25d6c1b | 2012-01-06 19:11:09 | [diff] [blame] | 47 | void GetPcSpBp(void *context, uintptr_t *pc, uintptr_t *sp, uintptr_t *bp) { |
| 48 | ucontext_t *ucontext = (ucontext_t*)context; |
| 49 | # if __WORDSIZE == 64 |
| 50 | *pc = ucontext->uc_mcontext->__ss.__rip; |
| 51 | *bp = ucontext->uc_mcontext->__ss.__rbp; |
| 52 | *sp = ucontext->uc_mcontext->__ss.__rsp; |
| 53 | # else |
| 54 | *pc = ucontext->uc_mcontext->__ss.__eip; |
| 55 | *bp = ucontext->uc_mcontext->__ss.__ebp; |
| 56 | *sp = ucontext->uc_mcontext->__ss.__esp; |
| 57 | # endif // __WORDSIZE |
| 58 | } |
| 59 | |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 60 | // No-op. Mac does not support static linkage anyway. |
| 61 | void *AsanDoesNotSupportStaticLinkage() { |
| 62 | return NULL; |
| 63 | } |
| 64 | |
Kostya Serebryany | 9fd01e5 | 2012-01-09 18:53:15 | [diff] [blame] | 65 | bool AsanInterceptsSignal(int signum) { |
| 66 | return (signum == SIGSEGV || signum == SIGBUS) && FLAG_handle_segv; |
| 67 | } |
| 68 | |
Kostya Serebryany | a772096 | 2011-12-28 23:28:54 | [diff] [blame] | 69 | static void *asan_mmap(void *addr, size_t length, int prot, int flags, |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 70 | int fd, uint64_t offset) { |
| 71 | return mmap(addr, length, prot, flags, fd, offset); |
| 72 | } |
| 73 | |
Kostya Serebryany | edb4a8a | 2012-01-09 23:11:26 | [diff] [blame] | 74 | size_t AsanWrite(int fd, const void *buf, size_t count) { |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 75 | return write(fd, buf, count); |
| 76 | } |
| 77 | |
Kostya Serebryany | 6c4bd80 | 2011-12-28 22:58:01 | [diff] [blame] | 78 | void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) { |
| 79 | size = RoundUpTo(size, kPageSize); |
| 80 | void *res = asan_mmap(0, size, |
| 81 | PROT_READ | PROT_WRITE, |
| 82 | MAP_PRIVATE | MAP_ANON, -1, 0); |
| 83 | if (res == (void*)-1) { |
| 84 | OutOfMemoryMessageAndDie(mem_type, size); |
| 85 | } |
| 86 | return res; |
| 87 | } |
| 88 | |
Kostya Serebryany | a772096 | 2011-12-28 23:28:54 | [diff] [blame] | 89 | void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) { |
| 90 | return asan_mmap((void*)fixed_addr, size, |
| 91 | PROT_READ | PROT_WRITE, |
| 92 | MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, |
| 93 | 0, 0); |
| 94 | } |
| 95 | |
| 96 | void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) { |
| 97 | return asan_mmap((void*)fixed_addr, size, |
| 98 | PROT_READ | PROT_WRITE, |
| 99 | MAP_PRIVATE | MAP_ANON | MAP_FIXED, |
| 100 | 0, 0); |
| 101 | } |
| 102 | |
| 103 | void *AsanMprotect(uintptr_t fixed_addr, size_t size) { |
| 104 | return asan_mmap((void*)fixed_addr, size, |
| 105 | PROT_NONE, |
| 106 | MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, |
| 107 | 0, 0); |
| 108 | } |
| 109 | |
Kostya Serebryany | 6c4bd80 | 2011-12-28 22:58:01 | [diff] [blame] | 110 | void AsanUnmapOrDie(void *addr, size_t size) { |
| 111 | if (!addr || !size) return; |
| 112 | int res = munmap(addr, size); |
| 113 | if (res != 0) { |
| 114 | Report("Failed to unmap\n"); |
Kostya Serebryany | edb4a8a | 2012-01-09 23:11:26 | [diff] [blame] | 115 | AsanDie(); |
Kostya Serebryany | 6c4bd80 | 2011-12-28 22:58:01 | [diff] [blame] | 116 | } |
| 117 | } |
| 118 | |
| 119 | int AsanOpenReadonly(const char* filename) { |
| 120 | return open(filename, O_RDONLY); |
| 121 | } |
| 122 | |
Alexander Potapenko | 553c208 | 2012-01-13 12:59:48 | [diff] [blame] | 123 | const char *AsanGetEnv(const char *name) { |
| 124 | char ***env_ptr = _NSGetEnviron(); |
| 125 | CHECK(env_ptr); |
| 126 | char **environ = *env_ptr; |
| 127 | CHECK(environ); |
| 128 | size_t name_len = internal_strlen(name); |
| 129 | while (*environ != NULL) { |
| 130 | size_t len = internal_strlen(*environ); |
| 131 | if (len > name_len) { |
| 132 | const char *p = *environ; |
| 133 | if (!internal_memcmp(p, name, name_len) && |
| 134 | p[name_len] == '=') { // Match. |
Alexey Samsonov | 50bf956 | 2012-01-16 12:38:09 | [diff] [blame] | 135 | return *environ + name_len + 1; // String starting after =. |
Alexander Potapenko | 553c208 | 2012-01-13 12:59:48 | [diff] [blame] | 136 | } |
| 137 | } |
| 138 | environ++; |
| 139 | } |
| 140 | return NULL; |
| 141 | } |
| 142 | |
Kostya Serebryany | edb4a8a | 2012-01-09 23:11:26 | [diff] [blame] | 143 | size_t AsanRead(int fd, void *buf, size_t count) { |
Kostya Serebryany | 6c4bd80 | 2011-12-28 22:58:01 | [diff] [blame] | 144 | return read(fd, buf, count); |
| 145 | } |
| 146 | |
| 147 | int AsanClose(int fd) { |
| 148 | return close(fd); |
| 149 | } |
| 150 | |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 151 | AsanProcMaps::AsanProcMaps() { |
| 152 | Reset(); |
| 153 | } |
| 154 | |
| 155 | AsanProcMaps::~AsanProcMaps() { |
| 156 | } |
| 157 | |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 158 | // More information about Mach-O headers can be found in mach-o/loader.h |
| 159 | // Each Mach-O image has a header (mach_header or mach_header_64) starting with |
| 160 | // a magic number, and a list of linker load commands directly following the |
| 161 | // header. |
| 162 | // A load command is at least two 32-bit words: the command type and the |
| 163 | // command size in bytes. We're interested only in segment load commands |
| 164 | // (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped |
| 165 | // into the task's address space. |
| 166 | // The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or |
| 167 | // segment_command_64 correspond to the memory address, memory size and the |
| 168 | // file offset of the current memory segment. |
| 169 | // Because these fields are taken from the images as is, one needs to add |
| 170 | // _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime. |
| 171 | |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 172 | void AsanProcMaps::Reset() { |
| 173 | // Count down from the top. |
| 174 | // TODO(glider): as per man 3 dyld, iterating over the headers with |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 175 | // _dyld_image_count is thread-unsafe. We need to register callbacks for |
| 176 | // adding and removing images which will invalidate the AsanProcMaps state. |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 177 | current_image_ = _dyld_image_count(); |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 178 | current_load_cmd_count_ = -1; |
| 179 | current_load_cmd_addr_ = NULL; |
Alexander Potapenko | 98f0c71 | 2012-01-26 17:01:20 | [diff] [blame^] | 180 | current_magic_ = 0; |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 181 | } |
| 182 | |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 183 | // Next and NextSegmentLoad were inspired by base/sysinfo.cc in |
| 184 | // Google Perftools, http://code.google.com/p/google-perftools. |
| 185 | |
| 186 | // NextSegmentLoad scans the current image for the next segment load command |
| 187 | // and returns the start and end addresses and file offset of the corresponding |
| 188 | // segment. |
| 189 | // Note that the segment addresses are not necessarily sorted. |
| 190 | template<uint32_t kLCSegment, typename SegmentCommand> |
| 191 | bool AsanProcMaps::NextSegmentLoad( |
| 192 | uintptr_t *start, uintptr_t *end, uintptr_t *offset, |
| 193 | char filename[], size_t filename_size) { |
| 194 | const char* lc = current_load_cmd_addr_; |
| 195 | current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize; |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 196 | if (((const load_command *)lc)->cmd == kLCSegment) { |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 197 | const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image_); |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 198 | const SegmentCommand* sc = (const SegmentCommand *)lc; |
| 199 | if (start) *start = sc->vmaddr + dlloff; |
| 200 | if (end) *end = sc->vmaddr + sc->vmsize + dlloff; |
| 201 | if (offset) *offset = sc->fileoff; |
| 202 | if (filename) { |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 203 | real_strncpy(filename, _dyld_get_image_name(current_image_), |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 204 | filename_size); |
| 205 | } |
Alexander Potapenko | 98f0c71 | 2012-01-26 17:01:20 | [diff] [blame^] | 206 | if (FLAG_v >= 4) |
| 207 | Report("LC_SEGMENT: %p--%p %s+%p\n", *start, *end, filename, *offset); |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 208 | return true; |
| 209 | } |
| 210 | return false; |
| 211 | } |
| 212 | |
| 213 | bool AsanProcMaps::Next(uintptr_t *start, uintptr_t *end, |
| 214 | uintptr_t *offset, char filename[], |
| 215 | size_t filename_size) { |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 216 | for (; current_image_ >= 0; current_image_--) { |
| 217 | const mach_header* hdr = _dyld_get_image_header(current_image_); |
| 218 | if (!hdr) continue; |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 219 | if (current_load_cmd_count_ < 0) { |
| 220 | // Set up for this image; |
| 221 | current_load_cmd_count_ = hdr->ncmds; |
Alexander Potapenko | 98f0c71 | 2012-01-26 17:01:20 | [diff] [blame^] | 222 | current_magic_ = hdr->magic; |
| 223 | switch (current_magic_) { |
Alexander Potapenko | 3825e97 | 2012-01-19 12:44:06 | [diff] [blame] | 224 | #ifdef MH_MAGIC_64 |
| 225 | case MH_MAGIC_64: { |
| 226 | current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64); |
| 227 | break; |
| 228 | } |
| 229 | #endif |
| 230 | case MH_MAGIC: { |
| 231 | current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header); |
| 232 | break; |
| 233 | } |
| 234 | default: { |
| 235 | continue; |
| 236 | } |
| 237 | } |
| 238 | } |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 239 | |
Alexander Potapenko | 98f0c71 | 2012-01-26 17:01:20 | [diff] [blame^] | 240 | for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) { |
| 241 | switch (current_magic_) { |
| 242 | // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64. |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 243 | #ifdef MH_MAGIC_64 |
Alexander Potapenko | 98f0c71 | 2012-01-26 17:01:20 | [diff] [blame^] | 244 | case MH_MAGIC_64: { |
| 245 | if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>( |
| 246 | start, end, offset, filename, filename_size)) |
| 247 | return true; |
| 248 | break; |
| 249 | } |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 250 | #endif |
Alexander Potapenko | 98f0c71 | 2012-01-26 17:01:20 | [diff] [blame^] | 251 | case MH_MAGIC: { |
| 252 | if (NextSegmentLoad<LC_SEGMENT, struct segment_command>( |
| 253 | start, end, offset, filename, filename_size)) |
| 254 | return true; |
| 255 | break; |
| 256 | } |
| 257 | } |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 258 | } |
| 259 | // If we get here, no more load_cmd's in this image talk about |
| 260 | // segments. Go on to the next image. |
| 261 | } |
Alexander Potapenko | 4257386 | 2012-01-18 11:16:05 | [diff] [blame] | 262 | return false; |
| 263 | } |
| 264 | |
| 265 | bool AsanProcMaps::GetObjectNameAndOffset(uintptr_t addr, uintptr_t *offset, |
| 266 | char filename[], |
| 267 | size_t filename_size) { |
| 268 | return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); |
| 269 | } |
| 270 | |
Kostya Serebryany | 78d87d3 | 2012-01-05 01:07:27 | [diff] [blame] | 271 | void AsanThread::SetThreadStackTopAndBottom() { |
| 272 | size_t stacksize = pthread_get_stacksize_np(pthread_self()); |
| 273 | void *stackaddr = pthread_get_stackaddr_np(pthread_self()); |
| 274 | stack_top_ = (uintptr_t)stackaddr; |
| 275 | stack_bottom_ = stack_top_ - stacksize; |
| 276 | int local; |
| 277 | CHECK(AddrIsInStack((uintptr_t)&local)); |
| 278 | } |
| 279 | |
Kostya Serebryany | a82f0d4 | 2012-01-10 21:24:40 | [diff] [blame] | 280 | AsanLock::AsanLock(LinkerInitialized) { |
| 281 | // We assume that OS_SPINLOCK_INIT is zero |
| 282 | } |
| 283 | |
| 284 | void AsanLock::Lock() { |
| 285 | CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); |
| 286 | CHECK(OS_SPINLOCK_INIT == 0); |
| 287 | CHECK(owner_ != (uintptr_t)pthread_self()); |
| 288 | OSSpinLockLock((OSSpinLock*)&opaque_storage_); |
| 289 | CHECK(!owner_); |
| 290 | owner_ = (uintptr_t)pthread_self(); |
| 291 | } |
| 292 | |
| 293 | void AsanLock::Unlock() { |
| 294 | CHECK(owner_ == (uintptr_t)pthread_self()); |
| 295 | owner_ = 0; |
| 296 | OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); |
| 297 | } |
| 298 | |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 299 | void AsanStackTrace::GetStackTrace(size_t max_s, uintptr_t pc, uintptr_t bp) { |
| 300 | size = 0; |
| 301 | trace[0] = pc; |
| 302 | if ((max_s) > 1) { |
| 303 | max_size = max_s; |
| 304 | FastUnwindStack(pc, bp); |
| 305 | } |
| 306 | } |
| 307 | |
Alexander Potapenko | bd53f59 | 2012-01-13 16:13:58 | [diff] [blame] | 308 | // The range of pages to be used by __asan_mach_override_ptr for escape |
| 309 | // islands. |
| 310 | // TODO(glider): instead of mapping a fixed range we must find a range of |
| 311 | // unmapped pages in vmmap and take them. |
Alexander Potapenko | 2b2e072 | 2012-01-17 09:38:54 | [diff] [blame] | 312 | // These constants were chosen empirically and may not work if the shadow |
| 313 | // memory layout changes. Unfortunately they do necessarily depend on |
| 314 | // kHighMemBeg or kHighMemEnd. |
| 315 | #if __WORDSIZE == 32 |
| 316 | #define kIslandEnd (0xffdf0000 - kPageSize) |
| 317 | #define kIslandBeg (kIslandEnd - 256 * kPageSize) |
| 318 | #else |
Alexander Potapenko | bd53f59 | 2012-01-13 16:13:58 | [diff] [blame] | 319 | #define kIslandEnd (0x7fffffdf0000 - kPageSize) |
| 320 | #define kIslandBeg (kIslandEnd - 256 * kPageSize) |
Alexander Potapenko | 2b2e072 | 2012-01-17 09:38:54 | [diff] [blame] | 321 | #endif |
Alexander Potapenko | bd53f59 | 2012-01-13 16:13:58 | [diff] [blame] | 322 | |
| 323 | extern "C" |
| 324 | mach_error_t __asan_allocate_island(void **ptr, |
| 325 | size_t unused_size, |
| 326 | void *unused_hint) { |
| 327 | if (!island_allocator_pos) { |
Alexander Potapenko | 2b2e072 | 2012-01-17 09:38:54 | [diff] [blame] | 328 | island_allocator_pos = |
| 329 | asan_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg, |
| 330 | PROT_READ | PROT_WRITE | PROT_EXEC, |
| 331 | MAP_PRIVATE | MAP_ANON | MAP_FIXED, |
| 332 | -1, 0); |
| 333 | if (island_allocator_pos != (void*)kIslandBeg) { |
Alexander Potapenko | bd53f59 | 2012-01-13 16:13:58 | [diff] [blame] | 334 | return KERN_NO_SPACE; |
| 335 | } |
Alexander Potapenko | bd53f59 | 2012-01-13 16:13:58 | [diff] [blame] | 336 | }; |
| 337 | *ptr = island_allocator_pos; |
| 338 | island_allocator_pos = (char*)island_allocator_pos + kPageSize; |
| 339 | return err_none; |
| 340 | } |
| 341 | |
| 342 | extern "C" |
| 343 | mach_error_t __asan_deallocate_island(void *ptr) { |
| 344 | // Do nothing. |
| 345 | // TODO(glider): allow to free and reuse the island memory. |
| 346 | return err_none; |
| 347 | } |
Kostya Serebryany | a82f0d4 | 2012-01-10 21:24:40 | [diff] [blame] | 348 | |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 349 | // Support for the following functions from libdispatch on Mac OS: |
| 350 | // dispatch_async_f() |
| 351 | // dispatch_async() |
| 352 | // dispatch_sync_f() |
| 353 | // dispatch_sync() |
| 354 | // dispatch_after_f() |
| 355 | // dispatch_after() |
| 356 | // dispatch_group_async_f() |
| 357 | // dispatch_group_async() |
| 358 | // TODO(glider): libdispatch API contains other functions that we don't support |
| 359 | // yet. |
| 360 | // |
| 361 | // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are |
| 362 | // they can cause jobs to run on a thread different from the current one. |
| 363 | // TODO(glider): if so, we need a test for this (otherwise we should remove |
| 364 | // them). |
| 365 | // |
| 366 | // The following functions use dispatch_barrier_async_f() (which isn't a library |
| 367 | // function but is exported) and are thus supported: |
| 368 | // dispatch_source_set_cancel_handler_f() |
| 369 | // dispatch_source_set_cancel_handler() |
| 370 | // dispatch_source_set_event_handler_f() |
| 371 | // dispatch_source_set_event_handler() |
| 372 | // |
| 373 | // The reference manual for Grand Central Dispatch is available at |
| 374 | // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html |
| 375 | // The implementation details are at |
| 376 | // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c |
| 377 | |
| 378 | extern "C" |
| 379 | void asan_dispatch_call_block_and_release(void *block) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 380 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 381 | asan_block_context_t *context = (asan_block_context_t*)block; |
| 382 | if (FLAG_v >= 2) { |
| 383 | Report("asan_dispatch_call_block_and_release(): " |
| 384 | "context: %p, pthread_self: %p\n", |
| 385 | block, pthread_self()); |
| 386 | } |
| 387 | AsanThread *t = asanThreadRegistry().GetCurrent(); |
Kostya Serebryany | 332923b | 2012-01-11 02:03:16 | [diff] [blame] | 388 | if (!t) { |
Alexey Samsonov | 2d3a67b | 2012-01-17 06:35:31 | [diff] [blame] | 389 | t = AsanThread::Create(context->parent_tid, NULL, NULL, &stack); |
| 390 | asanThreadRegistry().RegisterThread(t); |
Kostya Serebryany | 6bb2f1d | 2011-12-16 19:13:35 | [diff] [blame] | 391 | t->Init(); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 392 | asanThreadRegistry().SetCurrent(t); |
| 393 | } |
| 394 | // Call the original dispatcher for the block. |
| 395 | context->func(context->block); |
| 396 | asan_free(context, &stack); |
| 397 | } |
| 398 | |
| 399 | } // namespace __asan |
| 400 | |
| 401 | using namespace __asan; // NOLINT |
| 402 | |
| 403 | // Wrap |ctxt| and |func| into an asan_block_context_t. |
| 404 | // The caller retains control of the allocated context. |
| 405 | extern "C" |
| 406 | asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, |
| 407 | AsanStackTrace *stack) { |
| 408 | asan_block_context_t *asan_ctxt = |
| 409 | (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); |
| 410 | asan_ctxt->block = ctxt; |
| 411 | asan_ctxt->func = func; |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 412 | asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); |
| 413 | return asan_ctxt; |
| 414 | } |
| 415 | |
| 416 | // TODO(glider): can we reduce code duplication by introducing a macro? |
| 417 | extern "C" |
| 418 | int WRAP(dispatch_async_f)(dispatch_queue_t dq, |
| 419 | void *ctxt, |
| 420 | dispatch_function_t func) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 421 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 422 | asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); |
| 423 | if (FLAG_v >= 2) { |
| 424 | Report("dispatch_async_f(): context: %p, pthread_self: %p\n", |
| 425 | asan_ctxt, pthread_self()); |
| 426 | PRINT_CURRENT_STACK(); |
| 427 | } |
| 428 | return real_dispatch_async_f(dq, (void*)asan_ctxt, |
| 429 | asan_dispatch_call_block_and_release); |
| 430 | } |
| 431 | |
| 432 | extern "C" |
| 433 | int WRAP(dispatch_sync_f)(dispatch_queue_t dq, |
| 434 | void *ctxt, |
| 435 | dispatch_function_t func) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 436 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 437 | asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); |
| 438 | if (FLAG_v >= 2) { |
| 439 | Report("dispatch_sync_f(): context: %p, pthread_self: %p\n", |
| 440 | asan_ctxt, pthread_self()); |
| 441 | PRINT_CURRENT_STACK(); |
| 442 | } |
| 443 | return real_dispatch_sync_f(dq, (void*)asan_ctxt, |
| 444 | asan_dispatch_call_block_and_release); |
| 445 | } |
| 446 | |
| 447 | extern "C" |
| 448 | int WRAP(dispatch_after_f)(dispatch_time_t when, |
| 449 | dispatch_queue_t dq, |
| 450 | void *ctxt, |
| 451 | dispatch_function_t func) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 452 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 453 | asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); |
| 454 | if (FLAG_v >= 2) { |
| 455 | Report("dispatch_after_f: %p\n", asan_ctxt); |
| 456 | PRINT_CURRENT_STACK(); |
| 457 | } |
| 458 | return real_dispatch_after_f(when, dq, (void*)asan_ctxt, |
| 459 | asan_dispatch_call_block_and_release); |
| 460 | } |
| 461 | |
| 462 | extern "C" |
| 463 | void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq, |
| 464 | void *ctxt, dispatch_function_t func) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 465 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 466 | asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); |
| 467 | if (FLAG_v >= 2) { |
| 468 | Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n", |
| 469 | asan_ctxt, pthread_self()); |
| 470 | PRINT_CURRENT_STACK(); |
| 471 | } |
| 472 | real_dispatch_barrier_async_f(dq, (void*)asan_ctxt, |
| 473 | asan_dispatch_call_block_and_release); |
| 474 | } |
| 475 | |
| 476 | extern "C" |
| 477 | void WRAP(dispatch_group_async_f)(dispatch_group_t group, |
| 478 | dispatch_queue_t dq, |
| 479 | void *ctxt, dispatch_function_t func) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 480 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 481 | asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); |
| 482 | if (FLAG_v >= 2) { |
| 483 | Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", |
| 484 | asan_ctxt, pthread_self()); |
| 485 | PRINT_CURRENT_STACK(); |
| 486 | } |
| 487 | real_dispatch_group_async_f(group, dq, (void*)asan_ctxt, |
| 488 | asan_dispatch_call_block_and_release); |
| 489 | } |
| 490 | |
| 491 | // The following stuff has been extremely helpful while looking for the |
| 492 | // unhandled functions that spawned jobs on Chromium shutdown. If the verbosity |
| 493 | // level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to |
| 494 | // find the points of worker thread creation (each of such threads may be used |
| 495 | // to run several tasks, that's why this is not enough to support the whole |
| 496 | // libdispatch API. |
| 497 | extern "C" |
| 498 | void *wrap_workitem_func(void *arg) { |
| 499 | if (FLAG_v >= 2) { |
| 500 | Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self()); |
| 501 | } |
| 502 | asan_block_context_t *ctxt = (asan_block_context_t*)arg; |
| 503 | worker_t fn = (worker_t)(ctxt->func); |
| 504 | void *result = fn(ctxt->block); |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 505 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 506 | asan_free(arg, &stack); |
| 507 | return result; |
| 508 | } |
| 509 | |
| 510 | extern "C" |
| 511 | int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq, |
| 512 | void *(*workitem_func)(void *), void * workitem_arg, |
| 513 | pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) { |
Evgeniy Stepanov | 84c44a8 | 2012-01-19 11:34:18 | [diff] [blame] | 514 | GET_STACK_TRACE_HERE(kStackTraceMax); |
Kostya Serebryany | 019b76f | 2011-11-30 01:07:02 | [diff] [blame] | 515 | asan_block_context_t *asan_ctxt = |
| 516 | (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack); |
| 517 | asan_ctxt->block = workitem_arg; |
| 518 | asan_ctxt->func = (dispatch_function_t)workitem_func; |
| 519 | asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); |
| 520 | if (FLAG_v >= 2) { |
| 521 | Report("pthread_workqueue_additem_np: %p\n", asan_ctxt); |
| 522 | PRINT_CURRENT_STACK(); |
| 523 | } |
| 524 | return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt, |
| 525 | itemhandlep, gencountp); |
| 526 | } |
Kostya Serebryany | 5dfa4da | 2011-12-01 21:40:52 | [diff] [blame] | 527 | |
Alexander Potapenko | 046ecc0 | 2012-01-23 10:09:54 | [diff] [blame] | 528 | // CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal |
| 529 | // and subject to change in further CoreFoundation versions. Apple does not |
| 530 | // guarantee any binary compatibility from release to release. |
| 531 | |
| 532 | // See http://opensource.apple.com/source/CF/CF-635.15/CFInternal.h |
| 533 | #if defined(__BIG_ENDIAN__) |
| 534 | #define CF_RC_BITS 0 |
| 535 | #endif |
| 536 | |
| 537 | #if defined(__LITTLE_ENDIAN__) |
| 538 | #define CF_RC_BITS 3 |
| 539 | #endif |
| 540 | |
| 541 | // See http://opensource.apple.com/source/CF/CF-635.15/CFRuntime.h |
| 542 | typedef struct __CFRuntimeBase { |
| 543 | uintptr_t _cfisa; |
| 544 | uint8_t _cfinfo[4]; |
| 545 | #if __LP64__ |
| 546 | uint32_t _rc; |
| 547 | #endif |
| 548 | } CFRuntimeBase; |
| 549 | |
| 550 | // See http://opensource.apple.com/source/CF/CF-635.15/CFString.c |
| 551 | int __CFStrIsConstant(CFStringRef str) { |
| 552 | CFRuntimeBase *base = (CFRuntimeBase*)str; |
| 553 | #if __LP64__ |
| 554 | return base->_rc == 0; |
| 555 | #else |
| 556 | return (base->_cfinfo[CF_RC_BITS]) == 0; |
| 557 | #endif |
| 558 | } |
| 559 | |
| 560 | extern "C" |
| 561 | CFStringRef WRAP(CFStringCreateCopy)(CFAllocatorRef alloc, CFStringRef str) { |
| 562 | if (__CFStrIsConstant(str)) { |
| 563 | return str; |
| 564 | } else { |
| 565 | return real_CFStringCreateCopy(alloc, str); |
| 566 | } |
| 567 | } |
| 568 | |
Kostya Serebryany | 5dfa4da | 2011-12-01 21:40:52 | [diff] [blame] | 569 | #endif // __APPLE__ |