blob: 18faaf15258987e73c6cadadd2d58f160e952c5e [file] [log] [blame]
Kostya Serebryany019b76f2011-11-30 01:07:021//===-- 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 Serebryany5dfa4da2011-12-01 21:40:5215#ifdef __APPLE__
Kostya Serebryany019b76f2011-11-30 01:07:0216
17#include "asan_mac.h"
18
19#include "asan_internal.h"
Alexander Potapenko42573862012-01-18 11:16:0520#include "asan_procmaps.h"
Kostya Serebryany019b76f2011-11-30 01:07:0221#include "asan_stack.h"
22#include "asan_thread.h"
23#include "asan_thread_registry.h"
24
Alexander Potapenko553c2082012-01-13 12:59:4825#include <crt_externs.h> // for _NSGetEnviron
Alexander Potapenko42573862012-01-18 11:16:0526#include <mach-o/dyld.h>
Kostya Serebryany019b76f2011-11-30 01:07:0227#include <sys/mman.h>
Kostya Serebryany2b087182012-01-06 02:12:2528#include <sys/resource.h>
Kostya Serebryany25d6c1b2012-01-06 19:11:0929#include <sys/ucontext.h>
Kostya Serebryany78d87d32012-01-05 01:07:2730#include <pthread.h>
Kostya Serebryanya7720962011-12-28 23:28:5431#include <fcntl.h>
Kostya Serebryany019b76f2011-11-30 01:07:0232#include <unistd.h>
Kostya Serebryanya82f0d42012-01-10 21:24:4033#include <libkern/OSAtomic.h>
Kostya Serebryany019b76f2011-11-30 01:07:0234
35namespace __asan {
36
Alexander Potapenkobd53f592012-01-13 16:13:5837void *island_allocator_pos = NULL;
38
Kostya Serebryany019b76f2011-11-30 01:07:0239extern dispatch_async_f_f real_dispatch_async_f;
40extern dispatch_sync_f_f real_dispatch_sync_f;
41extern dispatch_after_f_f real_dispatch_after_f;
42extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
43extern dispatch_group_async_f_f real_dispatch_group_async_f;
44extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
Alexander Potapenko046ecc02012-01-23 10:09:5445extern CFStringCreateCopy_f real_CFStringCreateCopy;
Kostya Serebryany019b76f2011-11-30 01:07:0246
Kostya Serebryany25d6c1b2012-01-06 19:11:0947void 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 Serebryany019b76f2011-11-30 01:07:0260// No-op. Mac does not support static linkage anyway.
61void *AsanDoesNotSupportStaticLinkage() {
62 return NULL;
63}
64
Kostya Serebryany9fd01e52012-01-09 18:53:1565bool AsanInterceptsSignal(int signum) {
66 return (signum == SIGSEGV || signum == SIGBUS) && FLAG_handle_segv;
67}
68
Kostya Serebryanya7720962011-12-28 23:28:5469static void *asan_mmap(void *addr, size_t length, int prot, int flags,
Kostya Serebryany019b76f2011-11-30 01:07:0270 int fd, uint64_t offset) {
71 return mmap(addr, length, prot, flags, fd, offset);
72}
73
Kostya Serebryanyedb4a8a2012-01-09 23:11:2674size_t AsanWrite(int fd, const void *buf, size_t count) {
Kostya Serebryany019b76f2011-11-30 01:07:0275 return write(fd, buf, count);
76}
77
Kostya Serebryany6c4bd802011-12-28 22:58:0178void *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 Serebryanya7720962011-12-28 23:28:5489void *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
96void *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
103void *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 Serebryany6c4bd802011-12-28 22:58:01110void 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 Serebryanyedb4a8a2012-01-09 23:11:26115 AsanDie();
Kostya Serebryany6c4bd802011-12-28 22:58:01116 }
117}
118
119int AsanOpenReadonly(const char* filename) {
120 return open(filename, O_RDONLY);
121}
122
Alexander Potapenko553c2082012-01-13 12:59:48123const 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 Samsonov50bf9562012-01-16 12:38:09135 return *environ + name_len + 1; // String starting after =.
Alexander Potapenko553c2082012-01-13 12:59:48136 }
137 }
138 environ++;
139 }
140 return NULL;
141}
142
Kostya Serebryanyedb4a8a2012-01-09 23:11:26143size_t AsanRead(int fd, void *buf, size_t count) {
Kostya Serebryany6c4bd802011-12-28 22:58:01144 return read(fd, buf, count);
145}
146
147int AsanClose(int fd) {
148 return close(fd);
149}
150
Alexander Potapenko42573862012-01-18 11:16:05151AsanProcMaps::AsanProcMaps() {
152 Reset();
153}
154
155AsanProcMaps::~AsanProcMaps() {
156}
157
Alexander Potapenko3825e972012-01-19 12:44:06158// 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 Potapenko42573862012-01-18 11:16:05172void AsanProcMaps::Reset() {
173 // Count down from the top.
174 // TODO(glider): as per man 3 dyld, iterating over the headers with
Alexander Potapenko3825e972012-01-19 12:44:06175 // _dyld_image_count is thread-unsafe. We need to register callbacks for
176 // adding and removing images which will invalidate the AsanProcMaps state.
Alexander Potapenko42573862012-01-18 11:16:05177 current_image_ = _dyld_image_count();
Alexander Potapenko3825e972012-01-19 12:44:06178 current_load_cmd_count_ = -1;
179 current_load_cmd_addr_ = NULL;
Alexander Potapenko98f0c712012-01-26 17:01:20180 current_magic_ = 0;
Alexander Potapenko42573862012-01-18 11:16:05181}
182
Alexander Potapenko3825e972012-01-19 12:44:06183// 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.
190template<uint32_t kLCSegment, typename SegmentCommand>
191bool 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 Potapenko42573862012-01-18 11:16:05196 if (((const load_command *)lc)->cmd == kLCSegment) {
Alexander Potapenko3825e972012-01-19 12:44:06197 const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image_);
Alexander Potapenko42573862012-01-18 11:16:05198 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 Potapenko3825e972012-01-19 12:44:06203 real_strncpy(filename, _dyld_get_image_name(current_image_),
Alexander Potapenko42573862012-01-18 11:16:05204 filename_size);
205 }
Alexander Potapenko98f0c712012-01-26 17:01:20206 if (FLAG_v >= 4)
207 Report("LC_SEGMENT: %p--%p %s+%p\n", *start, *end, filename, *offset);
Alexander Potapenko42573862012-01-18 11:16:05208 return true;
209 }
210 return false;
211}
212
213bool AsanProcMaps::Next(uintptr_t *start, uintptr_t *end,
214 uintptr_t *offset, char filename[],
215 size_t filename_size) {
Alexander Potapenko42573862012-01-18 11:16:05216 for (; current_image_ >= 0; current_image_--) {
217 const mach_header* hdr = _dyld_get_image_header(current_image_);
218 if (!hdr) continue;
Alexander Potapenko3825e972012-01-19 12:44:06219 if (current_load_cmd_count_ < 0) {
220 // Set up for this image;
221 current_load_cmd_count_ = hdr->ncmds;
Alexander Potapenko98f0c712012-01-26 17:01:20222 current_magic_ = hdr->magic;
223 switch (current_magic_) {
Alexander Potapenko3825e972012-01-19 12:44:06224#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 Potapenko42573862012-01-18 11:16:05239
Alexander Potapenko98f0c712012-01-26 17:01:20240 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 Potapenko42573862012-01-18 11:16:05243#ifdef MH_MAGIC_64
Alexander Potapenko98f0c712012-01-26 17:01:20244 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 Potapenko42573862012-01-18 11:16:05250#endif
Alexander Potapenko98f0c712012-01-26 17:01:20251 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 Potapenko42573862012-01-18 11:16:05258 }
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 Potapenko42573862012-01-18 11:16:05262 return false;
263}
264
265bool 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 Serebryany78d87d32012-01-05 01:07:27271void 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 Serebryanya82f0d42012-01-10 21:24:40280AsanLock::AsanLock(LinkerInitialized) {
281 // We assume that OS_SPINLOCK_INIT is zero
282}
283
284void 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
293void AsanLock::Unlock() {
294 CHECK(owner_ == (uintptr_t)pthread_self());
295 owner_ = 0;
296 OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
297}
298
Evgeniy Stepanov84c44a82012-01-19 11:34:18299void 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 Potapenkobd53f592012-01-13 16:13:58308// 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 Potapenko2b2e0722012-01-17 09:38:54312// 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 Potapenkobd53f592012-01-13 16:13:58319#define kIslandEnd (0x7fffffdf0000 - kPageSize)
320#define kIslandBeg (kIslandEnd - 256 * kPageSize)
Alexander Potapenko2b2e0722012-01-17 09:38:54321#endif
Alexander Potapenkobd53f592012-01-13 16:13:58322
323extern "C"
324mach_error_t __asan_allocate_island(void **ptr,
325 size_t unused_size,
326 void *unused_hint) {
327 if (!island_allocator_pos) {
Alexander Potapenko2b2e0722012-01-17 09:38:54328 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 Potapenkobd53f592012-01-13 16:13:58334 return KERN_NO_SPACE;
335 }
Alexander Potapenkobd53f592012-01-13 16:13:58336 };
337 *ptr = island_allocator_pos;
338 island_allocator_pos = (char*)island_allocator_pos + kPageSize;
339 return err_none;
340}
341
342extern "C"
343mach_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 Serebryanya82f0d42012-01-10 21:24:40348
Kostya Serebryany019b76f2011-11-30 01:07:02349// 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
378extern "C"
379void asan_dispatch_call_block_and_release(void *block) {
Evgeniy Stepanov84c44a82012-01-19 11:34:18380 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02381 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 Serebryany332923b2012-01-11 02:03:16388 if (!t) {
Alexey Samsonov2d3a67b2012-01-17 06:35:31389 t = AsanThread::Create(context->parent_tid, NULL, NULL, &stack);
390 asanThreadRegistry().RegisterThread(t);
Kostya Serebryany6bb2f1d2011-12-16 19:13:35391 t->Init();
Kostya Serebryany019b76f2011-11-30 01:07:02392 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
401using namespace __asan; // NOLINT
402
403// Wrap |ctxt| and |func| into an asan_block_context_t.
404// The caller retains control of the allocated context.
405extern "C"
406asan_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 Serebryany019b76f2011-11-30 01:07:02412 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
413 return asan_ctxt;
414}
415
416// TODO(glider): can we reduce code duplication by introducing a macro?
417extern "C"
418int WRAP(dispatch_async_f)(dispatch_queue_t dq,
419 void *ctxt,
420 dispatch_function_t func) {
Evgeniy Stepanov84c44a82012-01-19 11:34:18421 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02422 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
432extern "C"
433int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
434 void *ctxt,
435 dispatch_function_t func) {
Evgeniy Stepanov84c44a82012-01-19 11:34:18436 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02437 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
447extern "C"
448int WRAP(dispatch_after_f)(dispatch_time_t when,
449 dispatch_queue_t dq,
450 void *ctxt,
451 dispatch_function_t func) {
Evgeniy Stepanov84c44a82012-01-19 11:34:18452 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02453 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
462extern "C"
463void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
464 void *ctxt, dispatch_function_t func) {
Evgeniy Stepanov84c44a82012-01-19 11:34:18465 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02466 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
476extern "C"
477void WRAP(dispatch_group_async_f)(dispatch_group_t group,
478 dispatch_queue_t dq,
479 void *ctxt, dispatch_function_t func) {
Evgeniy Stepanov84c44a82012-01-19 11:34:18480 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02481 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.
497extern "C"
498void *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 Stepanov84c44a82012-01-19 11:34:18505 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02506 asan_free(arg, &stack);
507 return result;
508}
509
510extern "C"
511int 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 Stepanov84c44a82012-01-19 11:34:18514 GET_STACK_TRACE_HERE(kStackTraceMax);
Kostya Serebryany019b76f2011-11-30 01:07:02515 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 Serebryany5dfa4da2011-12-01 21:40:52527
Alexander Potapenko046ecc02012-01-23 10:09:54528// 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
542typedef 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
551int __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
560extern "C"
561CFStringRef 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 Serebryany5dfa4da2011-12-01 21:40:52569#endif // __APPLE__