summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.mk11
-rw-r--r--lib/abi.h23
-rw-r--r--lib/absvdi2.c2
-rw-r--r--lib/absvsi2.c2
-rw-r--r--lib/absvti2.c1
-rw-r--r--lib/adddf3.c2
-rw-r--r--lib/addsf3.c2
-rw-r--r--lib/addvdi3.c2
-rw-r--r--lib/addvsi3.c2
-rw-r--r--lib/addvti3.c1
-rw-r--r--lib/arm/Makefile.mk1
-rw-r--r--lib/arm/adddf3vfp.S9
-rw-r--r--lib/arm/addsf3vfp.S9
-rw-r--r--lib/arm/divdf3vfp.S9
-rw-r--r--lib/arm/divsf3vfp.S9
-rw-r--r--lib/arm/eqdf2vfp.S9
-rw-r--r--lib/arm/eqsf2vfp.S9
-rw-r--r--lib/arm/extendsfdf2vfp.S7
-rw-r--r--lib/arm/fixdfsivfp.S7
-rw-r--r--lib/arm/fixsfsivfp.S7
-rw-r--r--lib/arm/fixunsdfsivfp.S7
-rw-r--r--lib/arm/fixunssfsivfp.S7
-rw-r--r--lib/arm/floatsidfvfp.S7
-rw-r--r--lib/arm/floatsisfvfp.S7
-rw-r--r--lib/arm/floatunssidfvfp.S7
-rw-r--r--lib/arm/floatunssisfvfp.S7
-rw-r--r--lib/arm/gedf2vfp.S9
-rw-r--r--lib/arm/gesf2vfp.S9
-rw-r--r--lib/arm/gtdf2vfp.S9
-rw-r--r--lib/arm/gtsf2vfp.S9
-rw-r--r--lib/arm/ledf2vfp.S9
-rw-r--r--lib/arm/lesf2vfp.S9
-rw-r--r--lib/arm/ltdf2vfp.S9
-rw-r--r--lib/arm/ltsf2vfp.S9
-rw-r--r--lib/arm/muldf3vfp.S9
-rw-r--r--lib/arm/mulsf3vfp.S9
-rw-r--r--lib/arm/nedf2vfp.S9
-rw-r--r--lib/arm/negdf2vfp.S1
-rw-r--r--lib/arm/negsf2vfp.S1
-rw-r--r--lib/arm/nesf2vfp.S9
-rw-r--r--lib/arm/subdf3vfp.S9
-rw-r--r--lib/arm/subsf3vfp.S9
-rw-r--r--lib/arm/truncdfsf2vfp.S7
-rw-r--r--lib/arm/unorddf2vfp.S9
-rw-r--r--lib/arm/unordsf2vfp.S9
-rw-r--r--lib/asan/Makefile.mk22
-rw-r--r--lib/asan/Makefile.old352
-rw-r--r--lib/asan/README.txt26
-rw-r--r--lib/asan/asan_allocator.cc1020
-rw-r--r--lib/asan/asan_allocator.h157
-rw-r--r--lib/asan/asan_globals.cc171
-rw-r--r--lib/asan/asan_interceptors.cc391
-rw-r--r--lib/asan/asan_interceptors.h134
-rw-r--r--lib/asan/asan_interface.h135
-rw-r--r--lib/asan/asan_internal.h239
-rw-r--r--lib/asan/asan_linux.cc101
-rw-r--r--lib/asan/asan_lock.h100
-rw-r--r--lib/asan/asan_mac.cc311
-rw-r--r--lib/asan/asan_mac.h87
-rw-r--r--lib/asan/asan_malloc_linux.cc142
-rw-r--r--lib/asan/asan_malloc_mac.cc390
-rw-r--r--lib/asan/asan_mapping.h100
-rw-r--r--lib/asan/asan_poisoning.cc159
-rw-r--r--lib/asan/asan_printf.cc181
-rw-r--r--lib/asan/asan_rtl.cc810
-rw-r--r--lib/asan/asan_stack.cc280
-rw-r--r--lib/asan/asan_stack.h94
-rw-r--r--lib/asan/asan_stats.cc88
-rw-r--r--lib/asan/asan_stats.h59
-rw-r--r--lib/asan/asan_thread.cc178
-rw-r--r--lib/asan/asan_thread.h108
-rw-r--r--lib/asan/asan_thread_registry.cc227
-rw-r--r--lib/asan/asan_thread_registry.h88
-rw-r--r--lib/asan/mach_override/LICENSE.TXT3
-rw-r--r--lib/asan/mach_override/Makefile.mk22
-rw-r--r--lib/asan/mach_override/README.txt9
-rw-r--r--lib/asan/mach_override/mach_override.c862
-rw-r--r--lib/asan/mach_override/mach_override.h127
-rwxr-xr-xlib/asan/scripts/asan_symbolize.py101
-rw-r--r--lib/asan/sysinfo/LICENSE.TXT29
-rw-r--r--lib/asan/sysinfo/Makefile.mk22
-rw-r--r--lib/asan/sysinfo/basictypes.h321
-rw-r--r--lib/asan/sysinfo/sysinfo.cc617
-rw-r--r--lib/asan/sysinfo/sysinfo.h234
-rw-r--r--lib/asan/tests/asan_benchmarks_test.cc86
-rw-r--r--lib/asan/tests/asan_break_optimization.cc18
-rw-r--r--lib/asan/tests/asan_exceptions_test.cc27
-rw-r--r--lib/asan/tests/asan_globals_test.cc24
-rw-r--r--lib/asan/tests/asan_interface_test.cc334
-rw-r--r--lib/asan/tests/asan_mac_test.h16
-rw-r--r--lib/asan/tests/asan_mac_test.mm203
-rw-r--r--lib/asan/tests/asan_noinst_test.cc329
-rw-r--r--lib/asan/tests/asan_test.cc2022
-rw-r--r--lib/asan/tests/asan_test.ignore2
-rw-r--r--lib/asan/tests/asan_test_config.h44
-rw-r--r--lib/asan/tests/asan_test_utils.h30
-rw-r--r--lib/asan/tests/dlclose-test-so.cc33
-rw-r--r--lib/asan/tests/dlclose-test.cc73
-rw-r--r--lib/asan/tests/dlclose-test.tmpl1
-rw-r--r--lib/asan/tests/global-overflow.cc12
-rw-r--r--lib/asan/tests/global-overflow.tmpl3
-rw-r--r--lib/asan/tests/heap-overflow.cc9
-rw-r--r--lib/asan/tests/heap-overflow.tmpl6
-rw-r--r--lib/asan/tests/heap-overflow.tmpl.Darwin8
-rw-r--r--lib/asan/tests/large_func_test.cc33
-rw-r--r--lib/asan/tests/large_func_test.tmpl8
-rwxr-xr-xlib/asan/tests/match_output.py35
-rw-r--r--lib/asan/tests/null_deref.cc7
-rw-r--r--lib/asan/tests/null_deref.tmpl4
-rw-r--r--lib/asan/tests/shared-lib-test-so.cc21
-rw-r--r--lib/asan/tests/shared-lib-test.cc37
-rw-r--r--lib/asan/tests/shared-lib-test.tmpl7
-rw-r--r--lib/asan/tests/stack-overflow.cc7
-rw-r--r--lib/asan/tests/stack-overflow.tmpl3
-rw-r--r--lib/asan/tests/stack-use-after-return.cc24
-rw-r--r--lib/asan/tests/stack-use-after-return.disabled3
-rw-r--r--lib/asan/tests/strncpy-overflow.cc9
-rw-r--r--lib/asan/tests/strncpy-overflow.tmpl7
-rwxr-xr-xlib/asan/tests/test_output.sh47
-rw-r--r--lib/asan/tests/use-after-free.c6
-rw-r--r--lib/asan/tests/use-after-free.cc6
-rw-r--r--lib/asan/tests/use-after-free.tmpl10
-rw-r--r--lib/ashldi3.c1
-rw-r--r--lib/ashrdi3.c1
-rw-r--r--lib/assembly.h11
-rw-r--r--lib/clear_cache.c2
-rw-r--r--lib/clzdi2.c1
-rw-r--r--lib/clzsi2.c1
-rw-r--r--lib/cmpdi2.c1
-rw-r--r--lib/ctzdi2.c1
-rw-r--r--lib/ctzsi2.c1
-rw-r--r--lib/divdc3.c41
-rw-r--r--lib/divdf3.c1
-rw-r--r--lib/divdi3.c1
-rw-r--r--lib/divmoddi4.c1
-rw-r--r--lib/divmodsi4.c1
-rw-r--r--lib/divsc3.c41
-rw-r--r--lib/divsf3.c1
-rw-r--r--lib/divsi3.c1
-rw-r--r--lib/divxc3.c41
-rw-r--r--lib/enable_execute_stack.c3
-rw-r--r--lib/eprintf.c1
-rw-r--r--lib/extendsfdf2.c5
-rw-r--r--lib/ffsdi2.c1
-rw-r--r--lib/fixdfdi.c1
-rw-r--r--lib/fixdfsi.c1
-rw-r--r--lib/fixsfdi.c1
-rw-r--r--lib/fixsfsi.c1
-rw-r--r--lib/fixunsdfdi.c1
-rw-r--r--lib/fixunsdfsi.c1
-rw-r--r--lib/fixunssfdi.c1
-rw-r--r--lib/fixunssfsi.c1
-rw-r--r--lib/floatdidf.c3
-rw-r--r--lib/floatdisf.c3
-rw-r--r--lib/floatsidf.c1
-rw-r--r--lib/floatsisf.c1
-rw-r--r--lib/floattidf.c1
-rw-r--r--lib/floattisf.c1
-rw-r--r--lib/floattixf.c1
-rw-r--r--lib/floatundidf.c4
-rw-r--r--lib/floatundisf.c3
-rw-r--r--lib/floatunsidf.c1
-rw-r--r--lib/floatunsisf.c1
-rw-r--r--lib/floatuntidf.c1
-rw-r--r--lib/floatuntisf.c1
-rw-r--r--lib/floatuntixf.c1
-rw-r--r--lib/fp_lib.h1
-rw-r--r--lib/gcc_personality_v0.c6
-rw-r--r--lib/i386/Makefile.mk1
-rw-r--r--lib/int_endianness.h (renamed from lib/endianness.h)13
-rw-r--r--lib/int_lib.h148
-rw-r--r--lib/int_math.h67
-rw-r--r--lib/int_types.h140
-rw-r--r--lib/int_util.c43
-rw-r--r--lib/int_util.h32
-rw-r--r--lib/lshrdi3.c1
-rw-r--r--lib/moddi3.c1
-rw-r--r--lib/modsi3.c1
-rw-r--r--lib/muldc3.c57
-rw-r--r--lib/muldf3.c1
-rw-r--r--lib/muldi3.c1
-rw-r--r--lib/mulodi4.c58
-rw-r--r--lib/mulosi4.c58
-rw-r--r--lib/muloti4.c62
-rw-r--r--lib/mulsc3.c57
-rw-r--r--lib/mulsf3.c1
-rw-r--r--lib/mulvdi3.c1
-rw-r--r--lib/mulvsi3.c1
-rw-r--r--lib/mulvti3.c1
-rw-r--r--lib/mulxc3.c57
-rw-r--r--lib/negdf2.c1
-rw-r--r--lib/negsf2.c1
-rw-r--r--lib/negvdi2.c2
-rw-r--r--lib/negvsi2.c2
-rw-r--r--lib/negvti2.c1
-rw-r--r--lib/paritydi2.c1
-rw-r--r--lib/paritysi2.c1
-rw-r--r--lib/popcountdi2.c1
-rw-r--r--lib/popcountsi2.c1
-rw-r--r--lib/powidf2.c1
-rw-r--r--lib/powisf2.c1
-rw-r--r--lib/ppc/DD.h2
-rw-r--r--lib/ppc/Makefile.mk1
-rw-r--r--lib/ppc/divtc3.c59
-rw-r--r--lib/ppc/fixtfdi.c4
-rw-r--r--lib/ppc/fixunstfdi.c1
-rw-r--r--lib/ppc/floatditf.c1
-rw-r--r--lib/ppc/floatunditf.c1
-rw-r--r--lib/ppc/multc3.c39
-rw-r--r--lib/profile/GCDAProfiling.c200
-rw-r--r--lib/profile/Makefile.mk18
-rw-r--r--lib/subdf3.c1
-rw-r--r--lib/subsf3.c1
-rw-r--r--lib/subvdi3.c2
-rw-r--r--lib/subvsi3.c2
-rw-r--r--lib/subvti3.c1
-rw-r--r--lib/trampoline_setup.c3
-rw-r--r--lib/truncdfsf2.c6
-rw-r--r--lib/ucmpdi2.c1
-rw-r--r--lib/udivdi3.c1
-rw-r--r--lib/udivmoddi4.c3
-rw-r--r--lib/udivmodsi4.c1
-rw-r--r--lib/udivmodti4.c2
-rw-r--r--lib/udivsi3.c1
-rw-r--r--lib/umoddi3.c1
-rw-r--r--lib/umodsi3.c1
-rw-r--r--lib/x86_64/Makefile.mk1
-rw-r--r--lib/x86_64/floatdidf.c2
-rw-r--r--lib/x86_64/floatdisf.c2
-rw-r--r--lib/x86_64/floatdixf.c2
230 files changed, 13134 insertions, 608 deletions
diff --git a/lib/Makefile.mk b/lib/Makefile.mk
index 1ed8c11..8394af3 100644
--- a/lib/Makefile.mk
+++ b/lib/Makefile.mk
@@ -7,8 +7,17 @@
#
#===------------------------------------------------------------------------===#
-SubDirs := i386 ppc x86_64 arm
+ModuleName := builtins
+SubDirs :=
+# Add arch specific optimized implementations.
+SubDirs += i386 ppc x86_64 arm
+
+# Add other submodules.
+SubDirs += asan
+SubDirs += profile
+
+# Define the variables for this specific directory.
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o)
Implementation := Generic
diff --git a/lib/abi.h b/lib/abi.h
deleted file mode 100644
index 2534317..0000000
--- a/lib/abi.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* ===------ abi.h - configuration header for compiler-rt -----------------===
- *
- * The LLVM Compiler Infrastructure
- *
- * This file is dual licensed under the MIT and the University of Illinois Open
- * Source Licenses. See LICENSE.TXT for details.
- *
- * ===----------------------------------------------------------------------===
- *
- * This file is a configuration header for compiler-rt.
- * This file is not part of the interface of this library.
- *
- * ===----------------------------------------------------------------------===
- */
-
-#if __ARM_EABI__
-# define ARM_EABI_FNALIAS(aeabi_name, name) \
- void __aeabi_##aeabi_name() __attribute__((alias("__" #name)));
-# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
-#else
-# define ARM_EABI_FNALIAS(aeabi_name, name)
-# define COMPILER_RT_ABI
-#endif
diff --git a/lib/absvdi2.c b/lib/absvdi2.c
index 9c5d4a2..682c235 100644
--- a/lib/absvdi2.c
+++ b/lib/absvdi2.c
@@ -11,10 +11,8 @@
*
*===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: absolute value */
diff --git a/lib/absvsi2.c b/lib/absvsi2.c
index 80a1a78..4812af8 100644
--- a/lib/absvsi2.c
+++ b/lib/absvsi2.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: absolute value */
diff --git a/lib/absvti2.c b/lib/absvti2.c
index 9e73a26..8f2bddc 100644
--- a/lib/absvti2.c
+++ b/lib/absvti2.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: absolute value */
diff --git a/lib/adddf3.c b/lib/adddf3.c
index 3cc997b..7eb40a1 100644
--- a/lib/adddf3.c
+++ b/lib/adddf3.c
@@ -12,8 +12,6 @@
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
-
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/addsf3.c b/lib/addsf3.c
index 20610ef..e57270a 100644
--- a/lib/addsf3.c
+++ b/lib/addsf3.c
@@ -12,8 +12,6 @@
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
-
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/addvdi3.c b/lib/addvdi3.c
index 51ad397..db45a27 100644
--- a/lib/addvdi3.c
+++ b/lib/addvdi3.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a + b */
diff --git a/lib/addvsi3.c b/lib/addvsi3.c
index c18f7bd..81f515c 100644
--- a/lib/addvsi3.c
+++ b/lib/addvsi3.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a + b */
diff --git a/lib/addvti3.c b/lib/addvti3.c
index ba220f5..9105c17 100644
--- a/lib/addvti3.c
+++ b/lib/addvti3.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a + b */
diff --git a/lib/arm/Makefile.mk b/lib/arm/Makefile.mk
index cde97c3..e7bbd7b 100644
--- a/lib/arm/Makefile.mk
+++ b/lib/arm/Makefile.mk
@@ -7,6 +7,7 @@
#
#===------------------------------------------------------------------------===#
+ModuleName := builtins
SubDirs :=
OnlyArchs := armv5 armv6 armv7
diff --git a/lib/arm/adddf3vfp.S b/lib/arm/adddf3vfp.S
index cced1e0..c90b0c2 100644
--- a/lib/arm/adddf3vfp.S
+++ b/lib/arm/adddf3vfp.S
@@ -15,10 +15,11 @@
// Adds two double precision floating point numbers using the Darwin
// calling convention where double arguments are passsed in GPR pairs
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__adddf3vfp)
- fmdrr d6, r0, r1 // move first param from r0/r1 pair into d6
- fmdrr d7, r2, r3 // move second param from r2/r3 pair into d7
- faddd d6, d6, d7
- fmrrd r0, r1, d6 // move result back to r0/r1 pair
+ vmov d6, r0, r1 // move first param from r0/r1 pair into d6
+ vmov d7, r2, r3 // move second param from r2/r3 pair into d7
+ vadd.f64 d6, d6, d7
+ vmov r0, r1, d6 // move result back to r0/r1 pair
bx lr
diff --git a/lib/arm/addsf3vfp.S b/lib/arm/addsf3vfp.S
index b747528..43653d5 100644
--- a/lib/arm/addsf3vfp.S
+++ b/lib/arm/addsf3vfp.S
@@ -15,10 +15,11 @@
// Adds two single precision floating point numbers using the Darwin
// calling convention where single arguments are passsed in GPRs
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__addsf3vfp)
- fmsr s14, r0 // move first param from r0 into float register
- fmsr s15, r1 // move second param from r1 into float register
- fadds s14, s14, s15
- fmrs r0, s14 // move result back to r0
+ vmov s14, r0 // move first param from r0 into float register
+ vmov s15, r1 // move second param from r1 into float register
+ vadd.f32 s14, s14, s15
+ vmov r0, s14 // move result back to r0
bx lr
diff --git a/lib/arm/divdf3vfp.S b/lib/arm/divdf3vfp.S
index 74ef0ea..52de67f 100644
--- a/lib/arm/divdf3vfp.S
+++ b/lib/arm/divdf3vfp.S
@@ -15,10 +15,11 @@
// Divides two double precision floating point numbers using the Darwin
// calling convention where double arguments are passsed in GPR pairs
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__divdf3vfp)
- fmdrr d6, r0, r1 // move first param from r0/r1 pair into d6
- fmdrr d7, r2, r3 // move second param from r2/r3 pair into d7
- fdivd d5, d6, d7
- fmrrd r0, r1, d5 // move result back to r0/r1 pair
+ vmov d6, r0, r1 // move first param from r0/r1 pair into d6
+ vmov d7, r2, r3 // move second param from r2/r3 pair into d7
+ vdiv.f64 d5, d6, d7
+ vmov r0, r1, d5 // move result back to r0/r1 pair
bx lr
diff --git a/lib/arm/divsf3vfp.S b/lib/arm/divsf3vfp.S
index 9eefcf31e..81ba903 100644
--- a/lib/arm/divsf3vfp.S
+++ b/lib/arm/divsf3vfp.S
@@ -15,10 +15,11 @@
// Divides two single precision floating point numbers using the Darwin
// calling convention where single arguments are passsed like 32-bit ints.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__divsf3vfp)
- fmsr s14, r0 // move first param from r0 into float register
- fmsr s15, r1 // move second param from r1 into float register
- fdivs s13, s14, s15
- fmrs r0, s13 // move result back to r0
+ vmov s14, r0 // move first param from r0 into float register
+ vmov s15, r1 // move second param from r1 into float register
+ vdiv.f32 s13, s14, s15
+ vmov r0, s13 // move result back to r0
bx lr
diff --git a/lib/arm/eqdf2vfp.S b/lib/arm/eqdf2vfp.S
index 2998a76..c41e55a 100644
--- a/lib/arm/eqdf2vfp.S
+++ b/lib/arm/eqdf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__eqdf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
moveq r0, #1 // set result register to 1 if equal
movne r0, #0
bx lr
diff --git a/lib/arm/eqsf2vfp.S b/lib/arm/eqsf2vfp.S
index 927566e..730ef88 100644
--- a/lib/arm/eqsf2vfp.S
+++ b/lib/arm/eqsf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__eqsf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
moveq r0, #1 // set result register to 1 if equal
movne r0, #0
bx lr
diff --git a/lib/arm/extendsfdf2vfp.S b/lib/arm/extendsfdf2vfp.S
index b1aa88e..17a146e 100644
--- a/lib/arm/extendsfdf2vfp.S
+++ b/lib/arm/extendsfdf2vfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a single precision parameter is
// passed in a GPR and a double precision result is returned in R0/R1 pair.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__extendsfdf2vfp)
- fmsr s15, r0 // load float register from R0
- fcvtds d7, s15 // convert single to double
- fmrrd r0, r1, d7 // return result in r0/r1 pair
+ vmov s15, r0 // load float register from R0
+ vcvt.f64.f32 d7, s15 // convert single to double
+ vmov r0, r1, d7 // return result in r0/r1 pair
bx lr
diff --git a/lib/arm/fixdfsivfp.S b/lib/arm/fixdfsivfp.S
index 0285a17..b7c3299 100644
--- a/lib/arm/fixdfsivfp.S
+++ b/lib/arm/fixdfsivfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a double precision parameter is
// passed in GPR register pair.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__fixdfsivfp)
- fmdrr d7, r0, r1 // load double register from R0/R1
- ftosizd s15, d7 // convert double to 32-bit int into s15
- fmrs r0, s15 // move s15 to result register
+ vmov d7, r0, r1 // load double register from R0/R1
+ vcvt.s32.f64 s15, d7 // convert double to 32-bit int into s15
+ vmov r0, s15 // move s15 to result register
bx lr
diff --git a/lib/arm/fixsfsivfp.S b/lib/arm/fixsfsivfp.S
index d05ba74..1cea6a4 100644
--- a/lib/arm/fixsfsivfp.S
+++ b/lib/arm/fixsfsivfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a single precision parameter is
// passed in a GPR..
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__fixsfsivfp)
- fmsr s15, r0 // load float register from R0
- ftosizs s15, s15 // convert single to 32-bit int into s15
- fmrs r0, s15 // move s15 to result register
+ vmov s15, r0 // load float register from R0
+ vcvt.s32.f32 s15, s15 // convert single to 32-bit int into s15
+ vmov r0, s15 // move s15 to result register
bx lr
diff --git a/lib/arm/fixunsdfsivfp.S b/lib/arm/fixunsdfsivfp.S
index ddb703c..54b0359 100644
--- a/lib/arm/fixunsdfsivfp.S
+++ b/lib/arm/fixunsdfsivfp.S
@@ -17,9 +17,10 @@
// Uses Darwin calling convention where a double precision parameter is
// passed in GPR register pair.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__fixunsdfsivfp)
- fmdrr d7, r0, r1 // load double register from R0/R1
- ftouizd s15, d7 // convert double to 32-bit int into s15
- fmrs r0, s15 // move s15 to result register
+ vmov d7, r0, r1 // load double register from R0/R1
+ vcvt.u32.f64 s15, d7 // convert double to 32-bit int into s15
+ vmov r0, s15 // move s15 to result register
bx lr
diff --git a/lib/arm/fixunssfsivfp.S b/lib/arm/fixunssfsivfp.S
index afbb64f..12adb52 100644
--- a/lib/arm/fixunssfsivfp.S
+++ b/lib/arm/fixunssfsivfp.S
@@ -17,9 +17,10 @@
// Uses Darwin calling convention where a single precision parameter is
// passed in a GPR..
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__fixunssfsivfp)
- fmsr s15, r0 // load float register from R0
- ftouizs s15, s15 // convert single to 32-bit unsigned into s15
- fmrs r0, s15 // move s15 to result register
+ vmov s15, r0 // load float register from R0
+ vcvt.u32.f32 s15, s15 // convert single to 32-bit unsigned into s15
+ vmov r0, s15 // move s15 to result register
bx lr
diff --git a/lib/arm/floatsidfvfp.S b/lib/arm/floatsidfvfp.S
index fe3366a..e6a1eb3 100644
--- a/lib/arm/floatsidfvfp.S
+++ b/lib/arm/floatsidfvfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a double precision result is
// return in GPR register pair.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__floatsidfvfp)
- fmsr s15, r0 // move int to float register s15
- fsitod d7, s15 // convert 32-bit int in s15 to double in d7
- fmrrd r0, r1, d7 // move d7 to result register pair r0/r1
+ vmov s15, r0 // move int to float register s15
+ vcvt.f64.s32 d7, s15 // convert 32-bit int in s15 to double in d7
+ vmov r0, r1, d7 // move d7 to result register pair r0/r1
bx lr
diff --git a/lib/arm/floatsisfvfp.S b/lib/arm/floatsisfvfp.S
index 5b41610..0d3a24f 100644
--- a/lib/arm/floatsisfvfp.S
+++ b/lib/arm/floatsisfvfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a single precision result is
// return in a GPR..
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__floatsisfvfp)
- fmsr s15, r0 // move int to float register s15
- fsitos s15, s15 // convert 32-bit int in s15 to float in s15
- fmrs r0, s15 // move s15 to result register
+ vmov s15, r0 // move int to float register s15
+ vcvt.f32.s32 s15, s15 // convert 32-bit int in s15 to float in s15
+ vmov r0, s15 // move s15 to result register
bx lr
diff --git a/lib/arm/floatunssidfvfp.S b/lib/arm/floatunssidfvfp.S
index 9b22a6f..770b202 100644
--- a/lib/arm/floatunssidfvfp.S
+++ b/lib/arm/floatunssidfvfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a double precision result is
// return in GPR register pair.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__floatunssidfvfp)
- fmsr s15, r0 // move int to float register s15
- fuitod d7, s15 // convert 32-bit int in s15 to double in d7
- fmrrd r0, r1, d7 // move d7 to result register pair r0/r1
+ vmov s15, r0 // move int to float register s15
+ vcvt.f64.u32 d7, s15 // convert 32-bit int in s15 to double in d7
+ vmov r0, r1, d7 // move d7 to result register pair r0/r1
bx lr
diff --git a/lib/arm/floatunssisfvfp.S b/lib/arm/floatunssisfvfp.S
index 44d5e93..16b3ffb 100644
--- a/lib/arm/floatunssisfvfp.S
+++ b/lib/arm/floatunssisfvfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a single precision result is
// return in a GPR..
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__floatunssisfvfp)
- fmsr s15, r0 // move int to float register s15
- fuitos s15, s15 // convert 32-bit int in s15 to float in s15
- fmrs r0, s15 // move s15 to result register
+ vmov s15, r0 // move int to float register s15
+ vcvt.f32.u32 s15, s15 // convert 32-bit int in s15 to float in s15
+ vmov r0, s15 // move s15 to result register
bx lr
diff --git a/lib/arm/gedf2vfp.S b/lib/arm/gedf2vfp.S
index 9993f52..55603b8 100644
--- a/lib/arm/gedf2vfp.S
+++ b/lib/arm/gedf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__gedf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
movge r0, #1 // set result register to 1 if greater than or equal
movlt r0, #0
bx lr
diff --git a/lib/arm/gesf2vfp.S b/lib/arm/gesf2vfp.S
index 9ce1682..02da35c 100644
--- a/lib/arm/gesf2vfp.S
+++ b/lib/arm/gesf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__gesf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
movge r0, #1 // set result register to 1 if greater than or equal
movlt r0, #0
bx lr
diff --git a/lib/arm/gtdf2vfp.S b/lib/arm/gtdf2vfp.S
index 8a049c8..b5b1e14 100644
--- a/lib/arm/gtdf2vfp.S
+++ b/lib/arm/gtdf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__gtdf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
movgt r0, #1 // set result register to 1 if equal
movle r0, #0
bx lr
diff --git a/lib/arm/gtsf2vfp.S b/lib/arm/gtsf2vfp.S
index 1ffe1ec..685a9ce 100644
--- a/lib/arm/gtsf2vfp.S
+++ b/lib/arm/gtsf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__gtsf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
movgt r0, #1 // set result register to 1 if equal
movle r0, #0
bx lr
diff --git a/lib/arm/ledf2vfp.S b/lib/arm/ledf2vfp.S
index a04d0f2..6e140dd 100644
--- a/lib/arm/ledf2vfp.S
+++ b/lib/arm/ledf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__ledf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
movls r0, #1 // set result register to 1 if equal
movhi r0, #0
bx lr
diff --git a/lib/arm/lesf2vfp.S b/lib/arm/lesf2vfp.S
index 3011200..7b28250 100644
--- a/lib/arm/lesf2vfp.S
+++ b/lib/arm/lesf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__lesf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
movls r0, #1 // set result register to 1 if equal
movhi r0, #0
bx lr
diff --git a/lib/arm/ltdf2vfp.S b/lib/arm/ltdf2vfp.S
index 87144a8..a09e67a 100644
--- a/lib/arm/ltdf2vfp.S
+++ b/lib/arm/ltdf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__ltdf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
movmi r0, #1 // set result register to 1 if equal
movpl r0, #0
bx lr
diff --git a/lib/arm/ltsf2vfp.S b/lib/arm/ltsf2vfp.S
index ca06ae2..8c7f9a8 100644
--- a/lib/arm/ltsf2vfp.S
+++ b/lib/arm/ltsf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__ltsf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
movmi r0, #1 // set result register to 1 if equal
movpl r0, #0
bx lr
diff --git a/lib/arm/muldf3vfp.S b/lib/arm/muldf3vfp.S
index 96bba06..838581e 100644
--- a/lib/arm/muldf3vfp.S
+++ b/lib/arm/muldf3vfp.S
@@ -15,10 +15,11 @@
// Multiplies two double precision floating point numbers using the Darwin
// calling convention where double arguments are passsed in GPR pairs
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__muldf3vfp)
- fmdrr d6, r0, r1 // move first param from r0/r1 pair into d6
- fmdrr d7, r2, r3 // move second param from r2/r3 pair into d7
- fmuld d6, d6, d7
- fmrrd r0, r1, d6 // move result back to r0/r1 pair
+ vmov d6, r0, r1 // move first param from r0/r1 pair into d6
+ vmov d7, r2, r3 // move second param from r2/r3 pair into d7
+ vmul.f64 d6, d6, d7
+ vmov r0, r1, d6 // move result back to r0/r1 pair
bx lr
diff --git a/lib/arm/mulsf3vfp.S b/lib/arm/mulsf3vfp.S
index c56991d..ea25913 100644
--- a/lib/arm/mulsf3vfp.S
+++ b/lib/arm/mulsf3vfp.S
@@ -15,10 +15,11 @@
// Multiplies two single precision floating point numbers using the Darwin
// calling convention where single arguments are passsed like 32-bit ints.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__mulsf3vfp)
- fmsr s14, r0 // move first param from r0 into float register
- fmsr s15, r1 // move second param from r1 into float register
- fmuls s13, s14, s15
- fmrs r0, s13 // move result back to r0
+ vmov s14, r0 // move first param from r0 into float register
+ vmov s15, r1 // move second param from r1 into float register
+ vmul.f32 s13, s14, s15
+ vmov r0, s13 // move result back to r0
bx lr
diff --git a/lib/arm/nedf2vfp.S b/lib/arm/nedf2vfp.S
index a02b09c..2167081 100644
--- a/lib/arm/nedf2vfp.S
+++ b/lib/arm/nedf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__nedf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
movne r0, #1 // set result register to 0 if unequal
moveq r0, #0
bx lr
diff --git a/lib/arm/negdf2vfp.S b/lib/arm/negdf2vfp.S
index 100f4fd..64c9b69 100644
--- a/lib/arm/negdf2vfp.S
+++ b/lib/arm/negdf2vfp.S
@@ -15,6 +15,7 @@
// Returns the negation a double precision floating point numbers using the
// Darwin calling convention where double arguments are passsed in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__negdf2vfp)
eor r1, r1, #-2147483648 // flip sign bit on double in r0/r1 pair
diff --git a/lib/arm/negsf2vfp.S b/lib/arm/negsf2vfp.S
index f96c8ad..b883b73 100644
--- a/lib/arm/negsf2vfp.S
+++ b/lib/arm/negsf2vfp.S
@@ -15,6 +15,7 @@
// Returns the negation of a single precision floating point numbers using the
// Darwin calling convention where single arguments are passsed like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__negsf2vfp)
eor r0, r0, #-2147483648 // flip sign bit on float in r0
diff --git a/lib/arm/nesf2vfp.S b/lib/arm/nesf2vfp.S
index d620549..fa7aa80 100644
--- a/lib/arm/nesf2vfp.S
+++ b/lib/arm/nesf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__nesf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
movne r0, #1 // set result register to 1 if unequal
moveq r0, #0
bx lr
diff --git a/lib/arm/subdf3vfp.S b/lib/arm/subdf3vfp.S
index ff53b30..3f88baa 100644
--- a/lib/arm/subdf3vfp.S
+++ b/lib/arm/subdf3vfp.S
@@ -15,10 +15,11 @@
// Returns difference between two double precision floating point numbers using
// the Darwin calling convention where double arguments are passsed in GPR pairs
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__subdf3vfp)
- fmdrr d6, r0, r1 // move first param from r0/r1 pair into d6
- fmdrr d7, r2, r3 // move second param from r2/r3 pair into d7
- fsubd d6, d6, d7
- fmrrd r0, r1, d6 // move result back to r0/r1 pair
+ vmov d6, r0, r1 // move first param from r0/r1 pair into d6
+ vmov d7, r2, r3 // move second param from r2/r3 pair into d7
+ vsub.f64 d6, d6, d7
+ vmov r0, r1, d6 // move result back to r0/r1 pair
bx lr
diff --git a/lib/arm/subsf3vfp.S b/lib/arm/subsf3vfp.S
index 238f3f0..ed02ba9 100644
--- a/lib/arm/subsf3vfp.S
+++ b/lib/arm/subsf3vfp.S
@@ -16,10 +16,11 @@
// using the Darwin calling convention where single arguments are passsed
// like 32-bit ints.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__subsf3vfp)
- fmsr s14, r0 // move first param from r0 into float register
- fmsr s15, r1 // move second param from r1 into float register
- fsubs s14, s14, s15
- fmrs r0, s14 // move result back to r0
+ vmov s14, r0 // move first param from r0 into float register
+ vmov s15, r1 // move second param from r1 into float register
+ vsub.f32 s14, s14, s15
+ vmov r0, s14 // move result back to r0
bx lr
diff --git a/lib/arm/truncdfsf2vfp.S b/lib/arm/truncdfsf2vfp.S
index 6e55c7f..371aee94 100644
--- a/lib/arm/truncdfsf2vfp.S
+++ b/lib/arm/truncdfsf2vfp.S
@@ -16,9 +16,10 @@
// Uses Darwin calling convention where a double precision parameter is
// passed in a R0/R1 pair and a signle precision result is returned in R0.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__truncdfsf2vfp)
- fmdrr d7, r0, r1 // load double from r0/r1 pair
- fcvtsd s15, d7 // convert double to single (trucate precision)
- fmrs r0, s15 // return result in r0
+ vmov d7, r0, r1 // load double from r0/r1 pair
+ vcvt.f32.f64 s15, d7 // convert double to single (trucate precision)
+ vmov r0, s15 // return result in r0
bx lr
diff --git a/lib/arm/unorddf2vfp.S b/lib/arm/unorddf2vfp.S
index 9b52131..c49e55f 100644
--- a/lib/arm/unorddf2vfp.S
+++ b/lib/arm/unorddf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where double precision arguments are passsed
// like in GPR pairs.
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__unorddf2vfp)
- fmdrr d6, r0, r1 // load r0/r1 pair in double register
- fmdrr d7, r2, r3 // load r2/r3 pair in double register
- fcmpd d6, d7
- fmstat
+ vmov d6, r0, r1 // load r0/r1 pair in double register
+ vmov d7, r2, r3 // load r2/r3 pair in double register
+ vcmp.f64 d6, d7
+ vmrs apsr_nzcv, fpscr
movvs r0, #1 // set result register to 1 if "overflow" (any NaNs)
movvc r0, #0
bx lr
diff --git a/lib/arm/unordsf2vfp.S b/lib/arm/unordsf2vfp.S
index e486533..0ab27ed 100644
--- a/lib/arm/unordsf2vfp.S
+++ b/lib/arm/unordsf2vfp.S
@@ -16,12 +16,13 @@
// Uses Darwin calling convention where single precision arguments are passsed
// like 32-bit ints
//
+ .syntax unified
.align 2
DEFINE_COMPILERRT_FUNCTION(__unordsf2vfp)
- fmsr s14, r0 // move from GPR 0 to float register
- fmsr s15, r1 // move from GPR 1 to float register
- fcmps s14, s15
- fmstat
+ vmov s14, r0 // move from GPR 0 to float register
+ vmov s15, r1 // move from GPR 1 to float register
+ vcmp.f32 s14, s15
+ vmrs apsr_nzcv, fpscr
movvs r0, #1 // set result register to 1 if "overflow" (any NaNs)
movvc r0, #0
bx lr
diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk
new file mode 100644
index 0000000..4d9e58d
--- /dev/null
+++ b/lib/asan/Makefile.mk
@@ -0,0 +1,22 @@
+#===- lib/asan/Makefile.mk ---------------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := asan
+SubDirs := mach_override sysinfo
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+ObjNames := $(Sources:%.cc=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+
+# Define a convenience variable for all the asan functions.
+AsanFunctions := $(Sources:%.cc=%)
diff --git a/lib/asan/Makefile.old b/lib/asan/Makefile.old
new file mode 100644
index 0000000..a96ff42
--- /dev/null
+++ b/lib/asan/Makefile.old
@@ -0,0 +1,352 @@
+#===- lib/asan/Makefile.old --------------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+OS=$(shell uname | tr '[A-Z]' '[a-z]')
+ROOT=$(shell pwd)
+MAKEFILE=Makefile.old # this file.
+
+ifeq ($(ARCH), android)
+ ANDROID_CFLAGS= \
+ -DANDROID \
+ -D__WORDSIZE=32 \
+ -I$(ANDROID_BUILD_TOP)/external/stlport/stlport \
+ -I$(ANDROID_BUILD_TOP)/bionic \
+ -I$(ANDROID_BUILD_TOP)/bionic/libstdc++/include \
+ -I$(ANDROID_BUILD_TOP)/bionic/libc/arch-arm/include \
+ -I$(ANDROID_BUILD_TOP)/bionic/libc/include \
+ -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/common \
+ -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/arch-arm \
+ -I$(ANDROID_BUILD_TOP)/bionic/libm/include \
+ -I$(ANDROID_BUILD_TOP)/bionic/libm/include/arm \
+ -I$(ANDROID_BUILD_TOP)/bionic/libthread_db/include \
+ -L$(ANDROID_PRODUCT_OUT)/obj/lib
+ CLANG_FLAGS= \
+ -ccc-host-triple arm-linux-androideabi \
+ -D__compiler_offsetof=__builtin_offsetof \
+ -D__ELF__=1 \
+ -ccc-gcc-name arm-linux-androideabi-g++ \
+ $(ANDROID_CFLAGS)
+ CC=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-gcc $(ANDROID_CFLAGS)
+ CXX=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-g++ $(ANDROID_CFLAGS)
+endif
+
+ifeq ($(ARCH), arm)
+ # Example make command line:
+ # CROSSTOOL=$HOME/x-tools/arm-unknown-linux-gnueabi/ PATH=$CROSSTOOL/bin:$PATH make ARCH=arm asan_test
+ CLANG_FLAGS= \
+ -ccc-host-triple arm-unknown-linux-gnueabi \
+ -march=armv7-a -mfloat-abi=softfp -mfp=neon \
+ -ccc-gcc-name arm-unknown-linux-gnueabi-g++ \
+ -B$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
+ -B$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib \
+ -I$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4/include \
+ -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4 \
+ -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4/arm-unknown-linux-gnueabi \
+ -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/include \
+ -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/include \
+ -L$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
+ -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/lib \
+ -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib
+ CC=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-gcc
+ CXX=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-g++
+endif
+
+CLANG_FLAGS=
+CLANG_BUILD=$(ROOT)/../../../../build/Release+Asserts
+CLANG_CC=$(CLANG_BUILD)/bin/clang $(CLANG_FLAGS)
+CLANG_CXX=$(CLANG_BUILD)/bin/clang++ $(CLANG_FLAGS)
+
+CC=$(CLANG_CC)
+CXX=$(CLANG_CXX)
+
+CFLAGS:=-Wall -fvisibility=hidden
+
+CLEANROOM_CXX=$(CXX) -Wall
+
+INSTALL_DIR=../asan_clang_$(OS)
+BIN=bin_$(OS)
+
+LIBS=#-lpthread -ldl
+ARCH=x86_64
+
+ASAN_STACK=1
+ASAN_GLOBALS=1
+ASAN_USE_CALL=1
+ASAN_SCALE=0 # default will be used
+ASAN_OFFSET=-1 #default will be used
+ASAN_UAR=0
+ASAN_HAS_EXCEPTIONS=1
+ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
+ASAN_HAS_BLACKLIST=1
+ASAN_NEEDS_SEGV=1
+ASAN_PIE=0
+
+ifeq ($(ARCH), i386)
+BITS=32
+SUFF=$(BITS)
+CFLAGS:=$(CFLAGS) -m$(BITS)
+endif
+
+ifeq ($(ARCH), x86_64)
+BITS=64
+SUFF=$(BITS)
+CFLAGS:=$(CFLAGS) -m$(BITS)
+endif
+
+ifeq ($(ARCH), arm)
+BITS=32
+SUFF=_arm
+CFLAGS:=$(CFLAGS) -march=armv7-a
+ASAN_HAS_EXCEPTIONS=0
+endif
+
+ifeq ($(ARCH), android)
+BITS=32
+SUFF=_android
+CFLAGS:=$(CFLAGS)
+ASAN_HAS_EXCEPTIONS=0
+endif
+
+PIE=
+ifeq ($(ASAN_PIE), 1)
+ PIE=-fPIE -pie
+endif
+
+# This will build libasan on linux for both x86_64 and i386 in the
+# desired location. The Mac library is already build by the clang's make.
+# $(CLANG_BUILD)/lib/clang/3.1/lib/$(OS)/libclang_rt.asan-$(ARCH).a
+LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/3.1/lib/$(OS)
+LIBASAN_A=$(LIBASAN_INST_DIR)/libclang_rt.asan-$(ARCH).a
+
+BLACKLIST=
+ifeq ($(ASAN_HAS_BLACKLIST), 1)
+ BLACKLIST=-mllvm -asan-blacklist=$(ROOT)/tests/asan_test.ignore
+endif
+
+COMMON_ASAN_DEFINES=\
+ -DASAN_UAR=$(ASAN_UAR) \
+ -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
+ -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
+ -DASAN_HAS_BLACKLIST=$(ASAN_HAS_BLACKLIST)
+
+CLANG_ASAN_CXX=$(CLANG_CXX) \
+ -faddress-sanitizer \
+ $(BLACKLIST) \
+ -mllvm -asan-stack=$(ASAN_STACK) \
+ -mllvm -asan-globals=$(ASAN_GLOBALS) \
+ -mllvm -asan-use-call=$(ASAN_USE_CALL) \
+ -mllvm -asan-mapping-scale=$(ASAN_SCALE) \
+ -mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \
+ -mllvm -asan-use-after-return=$(ASAN_UAR) \
+ $(COMMON_ASAN_DEFINES)
+
+CLANG_ASAN_LD=$(CLANG_CXX) -faddress-sanitizer
+
+GCC_ASAN_PATH=SET_FROM_COMMAND_LINE
+GCC_ASAN_CXX=$(GCC_ASAN_PATH)/g++ \
+ -faddress-sanitizer \
+ $(COMMON_ASAN_DEFINES)
+
+GCC_ASAN_LD=$(GCC_ASAN_PATH)/g++ -ldl -lpthread
+
+ASAN_COMPILER=clang
+
+ifeq ($(ASAN_COMPILER), clang)
+ ASAN_CXX=$(CLANG_ASAN_CXX)
+ ASAN_LD=$(CLANG_ASAN_LD)
+ ASAN_LD_TAIL=
+endif
+
+ifeq ($(ASAN_COMPILER), gcc)
+ ASAN_CXX=$(GCC_ASAN_CXX)
+ ASAN_LD=$(GCC_ASAN_LD)
+ ASAN_LD_TAIL=$(LIBASAN_A)
+endif
+
+RTL_HDR=asan_allocator.h \
+ asan_internal.h \
+ asan_interceptors.h \
+ asan_interface.h \
+ asan_lock.h \
+ asan_mac.h \
+ asan_mapping.h \
+ asan_stack.h \
+ asan_stats.h \
+ asan_thread.h \
+ asan_thread_registry.h \
+ mach_override/mach_override.h \
+ sysinfo/basictypes.h \
+ sysinfo/sysinfo.h
+
+LIBASAN_OBJ=$(BIN)/asan_rtl$(SUFF).o \
+ $(BIN)/asan_allocator$(SUFF).o \
+ $(BIN)/asan_globals$(SUFF).o \
+ $(BIN)/asan_interceptors$(SUFF).o \
+ $(BIN)/asan_linux$(SUFF).o \
+ $(BIN)/asan_mac$(SUFF).o \
+ $(BIN)/asan_malloc_linux$(SUFF).o \
+ $(BIN)/asan_malloc_mac$(SUFF).o \
+ $(BIN)/asan_poisoning$(SUFF).o \
+ $(BIN)/asan_printf$(SUFF).o \
+ $(BIN)/asan_stack$(SUFF).o \
+ $(BIN)/asan_stats$(SUFF).o \
+ $(BIN)/asan_thread$(SUFF).o \
+ $(BIN)/asan_thread_registry$(SUFF).o \
+ $(BIN)/mach_override/mach_override$(SUFF).o \
+ $(BIN)/sysinfo/sysinfo$(SUFF).o
+
+GTEST_ROOT=third_party/googletest
+GTEST_INCLUDE=-I$(GTEST_ROOT)/include
+GTEST_MAKE_DIR=$(GTEST_ROOT)/make-$(OS)$(SUFF)
+GTEST_LIB=$(GTEST_MAKE_DIR)/gtest-all.o
+
+all: b64 b32
+
+test: t64 t32 output_tests lint
+
+output_tests: b32 b64
+ cd tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC)
+
+t64: b64
+ $(BIN)/asan_test64
+t32: b32
+ $(BIN)/asan_test32
+
+b64: | $(BIN)
+ $(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks
+b32: | $(BIN)
+ $(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks
+
+lib64:
+ $(MAKE) $(MAKEFILE) ARCH=x86_64 lib
+lib32:
+ $(MAKE) $(MAKEFILE) ARCH=i386 lib
+
+$(BIN):
+ mkdir -p $(BIN)
+ mkdir -p $(BIN)/sysinfo
+ mkdir -p $(BIN)/mach_override
+
+clang:
+ cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null
+
+install: install_clang
+
+$(INSTALL_DIR):
+ mkdir -p $(INSTALL_DIR) $(INSTALL_DIR)/bin $(INSTALL_DIR)/lib
+
+install_clang: | $(INSTALL_DIR)
+ cp -v $(CLANG_BUILD)/bin/clang $(INSTALL_DIR)/bin
+ cp -rv $(CLANG_BUILD)/lib/clang $(INSTALL_DIR)/lib
+ (cd $(INSTALL_DIR)/bin; ln -sf clang clang++)
+
+#install_lib: | $(INSTALL_DIR)
+# cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib
+
+$(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE)
+ $(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@
+
+$(BIN)/asan_break_optimization$(SUFF).o: tests/asan_break_optimization.cc $(MAKEFILE)
+ $(CLEANROOM_CXX) $(PIE) $(CFLAGS) -c $< -O0 -o $@
+
+$(BIN)/%_test$(SUFF).o: tests/%_test.cc $(RTL_HDR) $(MAKEFILE)
+ $(ASAN_CXX) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS)
+
+$(BIN)/%_test$(SUFF).o: tests/%_test.mm $(RTL_HDR) $(MAKEFILE)
+ $(ASAN_CXX) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS)
+
+$(BIN)/%$(SUFF).o: %.cc $(RTL_HDR) $(MAKEFILE)
+ $(CXX) $(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \
+ -o $@ -g $< -Ithird_party \
+ -DASAN_USE_SYSINFO=1 \
+ -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
+ -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
+ -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET) \
+ $(ASAN_FLAGS)
+
+$(BIN)/%$(SUFF).o: %.c $(RTL_HDR) $(MAKEFILE)
+ $(CC) $(PIE) $(CFLAGS) -fPIC -c -O2 -o $@ -g $< -Ithird_party \
+ -DASAN_USE_SYSINFO=1 \
+ $(ASAN_FLAGS)
+
+ifeq ($(OS),darwin)
+LD_FLAGS=-framework Foundation
+else
+LD_FLAGS=
+endif
+
+lib: $(LIBASAN_A)
+
+$(LIBASAN_A): $(BIN) $(LIBASAN_OBJ) $(MAKEFILE)
+ mkdir -p $(LIBASAN_INST_DIR)
+ ar ru $@ $(LIBASAN_OBJ)
+ $(CXX) -shared $(CFLAGS) $(LIBASAN_OBJ) $(LD_FLAGS) -o $(BIN)/libasan$(SUFF).so
+
+TEST_OBJECTS_COMMON=\
+ $(BIN)/asan_test$(SUFF).o \
+ $(BIN)/asan_globals_test$(SUFF).o \
+ $(BIN)/asan_break_optimization$(SUFF).o \
+ $(BIN)/asan_noinst_test$(SUFF).o \
+ $(BIN)/asan_interface_test$(SUFF).o
+
+BENCHMARK_OBJECTS=\
+ $(BIN)/asan_benchmarks_test$(SUFF).o \
+ $(BIN)/asan_break_optimization$(SUFF).o
+
+ifeq ($(OS),darwin)
+TEST_OBJECTS=$(TEST_OBJECTS_COMMON) \
+ $(BIN)/asan_mac_test$(SUFF).o
+else
+TEST_OBJECTS=$(TEST_OBJECTS_COMMON)
+endif
+
+$(BIN)/asan_test$(SUFF): $(TEST_OBJECTS) $(LIBASAN_A) $(MAKEFILE) tests/asan_test.ignore $(GTEST_LIB)
+ $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(TEST_OBJECTS) \
+ $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
+
+$(BIN)/asan_benchmarks$(SUFF): $(BENCHMARK_OBJECTS) $(LIBASAN_A) $(MAKEFILE) $(GTEST_LIB)
+ $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(BENCHMARK_OBJECTS) \
+ $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
+
+asan_test: $(BIN)/asan_test$(SUFF)
+
+asan_benchmarks: $(BIN)/asan_benchmarks$(SUFF)
+
+# for now, build gtest with clang/asan even if we use a different compiler.
+$(GTEST_LIB):
+ mkdir -p $(GTEST_MAKE_DIR) && \
+ cd $(GTEST_MAKE_DIR) && \
+ $(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \
+ CXX="$(CLANG_ASAN_CXX)"
+
+RTL_LINT_FITLER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright
+# TODO(kcc): remove these filters one by one
+TEST_LINT_FITLER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf
+
+LLVM_LINT_FILTER=-,+whitespace
+
+ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitizer.cpp
+
+lint:
+ third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP)
+ third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) asan_*.cc asan_*.h
+ third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FITLER) tests/*.cc
+
+get_third_party:
+ rm -rf third_party
+ mkdir third_party
+ (cd third_party && \
+ svn co -r375 http://googletest.googlecode.com/svn/trunk googletest && \
+ svn co -r69 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \
+ )
+
+clean:
+ rm -f *.o *.ll *.S *.a *.log asan_test64* asan_test32* a.out perf.data log
+ rm -rf $(BIN)
+ rm -rf $(GTEST_ROOT)/make-*
diff --git a/lib/asan/README.txt b/lib/asan/README.txt
new file mode 100644
index 0000000..00ae3c4
--- /dev/null
+++ b/lib/asan/README.txt
@@ -0,0 +1,26 @@
+AddressSanitizer RT
+================================
+This directory contains sources of the AddressSanitizer (asan) run-time library.
+We are in the process of integrating AddressSanitizer with LLVM, stay tuned.
+
+Directory structre:
+
+README.txt : This file.
+Makefile.mk : Currently a stub for a proper makefile. not usable.
+Makefile.old : Old out-of-tree makefile, the only usable one so far.
+asan_*.{cc,h} : Sources of the asan run-time lirbary.
+mach_override/* : Utility to override functions on Darwin (MIT License).
+sysinfo/* : Portable utility to iterate over /proc/maps (BSD License).
+scripts/* : Helper scripts.
+
+Temporary build instructions (verified on linux):
+
+cd lib/asan
+make -f Makefile.old get_third_party # gets googletest and cpplint
+make -f Makefile.old test -j 8 CLANG_BUILD=/path/to/Release+Asserts
+# Optional:
+# make -f Makefile.old install # installs clang and rt to lib/asan_clang_linux
+
+For more info see http://code.google.com/p/address-sanitizer/
+
+
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
new file mode 100644
index 0000000..f86dc0b
--- /dev/null
+++ b/lib/asan/asan_allocator.cc
@@ -0,0 +1,1020 @@
+//===-- asan_allocator.cc ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Implementation of ASan's memory allocator.
+// Evey piece of memory (AsanChunk) allocated by the allocator
+// has a left redzone of REDZONE bytes and
+// a right redzone such that the end of the chunk is aligned by REDZONE
+// (i.e. the right redzone is between 0 and REDZONE-1).
+// The left redzone is always poisoned.
+// The right redzone is poisoned on malloc, the body is poisoned on free.
+// Once freed, a chunk is moved to a quarantine (fifo list).
+// After quarantine, a chunk is returned to freelists.
+//
+// The left redzone contains ASan's internal data and the stack trace of
+// the malloc call.
+// Once freed, the body of the chunk contains the stack trace of the free call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_lock.h"
+#include "asan_mapping.h"
+#include "asan_stats.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+namespace __asan {
+
+#define REDZONE FLAG_redzone
+static const size_t kMinAllocSize = REDZONE * 2;
+static const size_t kMinMmapSize = 4UL << 20; // 4M
+static const uint64_t kMaxAvailableRam = 128ULL << 30; // 128G
+static const size_t kMaxThreadLocalQuarantine = 1 << 20; // 1M
+static const size_t kMaxSizeForThreadLocalFreeList = 1 << 17;
+
+// Size classes less than kMallocSizeClassStep are powers of two.
+// All other size classes are multiples of kMallocSizeClassStep.
+static const size_t kMallocSizeClassStepLog = 26;
+static const size_t kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog;
+
+#if __WORDSIZE == 32
+static const size_t kMaxAllowedMallocSize = 3UL << 30; // 3G
+#else
+static const size_t kMaxAllowedMallocSize = 8UL << 30; // 8G
+#endif
+
+static inline bool IsAligned(uintptr_t a, uintptr_t alignment) {
+ return (a & (alignment - 1)) == 0;
+}
+
+static inline size_t Log2(size_t x) {
+ CHECK(IsPowerOfTwo(x));
+ return __builtin_ctzl(x);
+}
+
+static inline size_t RoundUpToPowerOfTwo(size_t size) {
+ CHECK(size);
+ if (IsPowerOfTwo(size)) return size;
+ size_t up = __WORDSIZE - __builtin_clzl(size);
+ CHECK(size < (1ULL << up));
+ CHECK(size > (1ULL << (up - 1)));
+ return 1UL << up;
+}
+
+static inline size_t SizeClassToSize(uint8_t size_class) {
+ CHECK(size_class < kNumberOfSizeClasses);
+ if (size_class <= kMallocSizeClassStepLog) {
+ return 1UL << size_class;
+ } else {
+ return (size_class - kMallocSizeClassStepLog) * kMallocSizeClassStep;
+ }
+}
+
+static inline uint8_t SizeToSizeClass(size_t size) {
+ uint8_t res = 0;
+ if (size <= kMallocSizeClassStep) {
+ size_t rounded = RoundUpToPowerOfTwo(size);
+ res = Log2(rounded);
+ } else {
+ res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep)
+ + kMallocSizeClassStepLog;
+ }
+ CHECK(res < kNumberOfSizeClasses);
+ CHECK(size <= SizeClassToSize(res));
+ return res;
+}
+
+// Given REDZONE bytes, we need to mark first size bytes
+// as addressable and the rest REDZONE-size bytes as unaddressable.
+static void PoisonHeapPartialRightRedzone(uintptr_t mem, size_t size) {
+ CHECK(size <= REDZONE);
+ CHECK(IsAligned(mem, REDZONE));
+ CHECK(IsPowerOfTwo(SHADOW_GRANULARITY));
+ CHECK(IsPowerOfTwo(REDZONE));
+ CHECK(REDZONE >= SHADOW_GRANULARITY);
+ PoisonShadowPartialRightRedzone(mem, size, REDZONE,
+ kAsanHeapRightRedzoneMagic);
+}
+
+static uint8_t *MmapNewPagesAndPoisonShadow(size_t size) {
+ CHECK(IsAligned(size, kPageSize));
+ uint8_t *res = (uint8_t*)AsanMmapSomewhereOrDie(size, __FUNCTION__);
+ PoisonShadow((uintptr_t)res, size, kAsanHeapLeftRedzoneMagic);
+ if (FLAG_debug) {
+ Printf("ASAN_MMAP: [%p, %p)\n", res, res + size);
+ }
+ return res;
+}
+
+// Every chunk of memory allocated by this allocator can be in one of 3 states:
+// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
+// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
+// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
+//
+// The pseudo state CHUNK_MEMALIGN is used to mark that the address is not
+// the beginning of a AsanChunk (in which case 'next' contains the address
+// of the AsanChunk).
+//
+// The magic numbers for the enum values are taken randomly.
+enum {
+ CHUNK_AVAILABLE = 0x573B,
+ CHUNK_ALLOCATED = 0x3204,
+ CHUNK_QUARANTINE = 0x1978,
+ CHUNK_MEMALIGN = 0xDC68,
+};
+
+struct ChunkBase {
+ uint16_t chunk_state;
+ uint8_t size_class;
+ uint32_t offset; // User-visible memory starts at this+offset (beg()).
+ int32_t alloc_tid;
+ int32_t free_tid;
+ size_t used_size; // Size requested by the user.
+ AsanChunk *next;
+
+ uintptr_t beg() { return (uintptr_t)this + offset; }
+ size_t Size() { return SizeClassToSize(size_class); }
+ uint8_t SizeClass() { return size_class; }
+};
+
+struct AsanChunk: public ChunkBase {
+ uint32_t *compressed_alloc_stack() {
+ CHECK(REDZONE >= sizeof(ChunkBase));
+ return (uint32_t*)((uintptr_t)this + sizeof(ChunkBase));
+ }
+ uint32_t *compressed_free_stack() {
+ CHECK(REDZONE >= sizeof(ChunkBase));
+ return (uint32_t*)((uintptr_t)this + REDZONE);
+ }
+
+ // The left redzone after the ChunkBase is given to the alloc stack trace.
+ size_t compressed_alloc_stack_size() {
+ return (REDZONE - sizeof(ChunkBase)) / sizeof(uint32_t);
+ }
+ size_t compressed_free_stack_size() {
+ return (REDZONE) / sizeof(uint32_t);
+ }
+
+ bool AddrIsInside(uintptr_t addr, size_t access_size, size_t *offset) {
+ if (addr >= beg() && (addr + access_size) <= (beg() + used_size)) {
+ *offset = addr - beg();
+ return true;
+ }
+ return false;
+ }
+
+ bool AddrIsAtLeft(uintptr_t addr, size_t access_size, size_t *offset) {
+ if (addr < beg()) {
+ *offset = beg() - addr;
+ return true;
+ }
+ return false;
+ }
+
+ bool AddrIsAtRight(uintptr_t addr, size_t access_size, size_t *offset) {
+ if (addr + access_size >= beg() + used_size) {
+ if (addr <= beg() + used_size)
+ *offset = 0;
+ else
+ *offset = addr - (beg() + used_size);
+ return true;
+ }
+ return false;
+ }
+
+ void DescribeAddress(uintptr_t addr, size_t access_size) {
+ size_t offset;
+ Printf("%p is located ", addr);
+ if (AddrIsInside(addr, access_size, &offset)) {
+ Printf("%ld bytes inside of", offset);
+ } else if (AddrIsAtLeft(addr, access_size, &offset)) {
+ Printf("%ld bytes to the left of", offset);
+ } else if (AddrIsAtRight(addr, access_size, &offset)) {
+ Printf("%ld bytes to the right of", offset);
+ } else {
+ Printf(" somewhere around (this is AddressSanitizer bug!)");
+ }
+ Printf(" %lu-byte region [%p,%p)\n",
+ used_size, beg(), beg() + used_size);
+ }
+};
+
+static AsanChunk *PtrToChunk(uintptr_t ptr) {
+ AsanChunk *m = (AsanChunk*)(ptr - REDZONE);
+ if (m->chunk_state == CHUNK_MEMALIGN) {
+ m = m->next;
+ }
+ return m;
+}
+
+
+void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
+ if (last_) {
+ CHECK(first_);
+ CHECK(!last_->next);
+ last_->next = q->first_;
+ last_ = q->last_;
+ } else {
+ CHECK(!first_);
+ last_ = q->last_;
+ first_ = q->first_;
+ }
+ size_ += q->size();
+ q->clear();
+}
+
+void AsanChunkFifoList::Push(AsanChunk *n) {
+ CHECK(n->next == NULL);
+ if (last_) {
+ CHECK(first_);
+ CHECK(!last_->next);
+ last_->next = n;
+ last_ = n;
+ } else {
+ CHECK(!first_);
+ last_ = first_ = n;
+ }
+ size_ += n->Size();
+}
+
+// Interesting performance observation: this function takes up to 15% of overal
+// allocator time. That's because *first_ has been evicted from cache long time
+// ago. Not sure if we can or want to do anything with this.
+AsanChunk *AsanChunkFifoList::Pop() {
+ CHECK(first_);
+ AsanChunk *res = first_;
+ first_ = first_->next;
+ if (first_ == NULL)
+ last_ = NULL;
+ CHECK(size_ >= res->Size());
+ size_ -= res->Size();
+ if (last_) {
+ CHECK(!last_->next);
+ }
+ return res;
+}
+
+// All pages we ever allocated.
+struct PageGroup {
+ uintptr_t beg;
+ uintptr_t end;
+ size_t size_of_chunk;
+ uintptr_t last_chunk;
+ bool InRange(uintptr_t addr) {
+ return addr >= beg && addr < end;
+ }
+};
+
+class MallocInfo {
+ public:
+
+ explicit MallocInfo(LinkerInitialized x) : mu_(x) { }
+
+ AsanChunk *AllocateChunks(uint8_t size_class, size_t n_chunks) {
+ AsanChunk *m = NULL;
+ AsanChunk **fl = &free_lists_[size_class];
+ {
+ ScopedLock lock(&mu_);
+ for (size_t i = 0; i < n_chunks; i++) {
+ if (!(*fl)) {
+ *fl = GetNewChunks(size_class);
+ }
+ AsanChunk *t = *fl;
+ *fl = t->next;
+ t->next = m;
+ CHECK(t->chunk_state == CHUNK_AVAILABLE);
+ m = t;
+ }
+ }
+ return m;
+ }
+
+ void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x,
+ bool eat_free_lists) {
+ CHECK(FLAG_quarantine_size > 0);
+ ScopedLock lock(&mu_);
+ AsanChunkFifoList *q = &x->quarantine_;
+ if (q->size() > 0) {
+ quarantine_.PushList(q);
+ while (quarantine_.size() > FLAG_quarantine_size) {
+ QuarantinePop();
+ }
+ }
+ if (eat_free_lists) {
+ for (size_t size_class = 0; size_class < kNumberOfSizeClasses;
+ size_class++) {
+ AsanChunk *m = x->free_lists_[size_class];
+ while (m) {
+ AsanChunk *t = m->next;
+ m->next = free_lists_[size_class];
+ free_lists_[size_class] = m;
+ m = t;
+ }
+ x->free_lists_[size_class] = 0;
+ }
+ }
+ }
+
+ void BypassThreadLocalQuarantine(AsanChunk *chunk) {
+ ScopedLock lock(&mu_);
+ quarantine_.Push(chunk);
+ }
+
+ AsanChunk *FindMallocedOrFreed(uintptr_t addr, size_t access_size) {
+ ScopedLock lock(&mu_);
+ return FindChunkByAddr(addr);
+ }
+
+ // TODO(glider): AllocationSize() may become very slow if the size of
+ // page_groups_ grows. This can be fixed by increasing kMinMmapSize,
+ // but a better solution is to speed up the search somehow.
+ size_t AllocationSize(uintptr_t ptr) {
+ ScopedLock lock(&mu_);
+
+ // first, check if this is our memory
+ PageGroup *g = FindPageGroupUnlocked(ptr);
+ if (!g) return 0;
+ AsanChunk *m = PtrToChunk(ptr);
+ if (m->chunk_state == CHUNK_ALLOCATED) {
+ return m->used_size;
+ } else {
+ return 0;
+ }
+ }
+
+ void ForceLock() {
+ mu_.Lock();
+ }
+
+ void ForceUnlock() {
+ mu_.Unlock();
+ }
+
+ void PrintStatus() {
+ ScopedLock lock(&mu_);
+ size_t malloced = 0;
+
+ Printf(" MallocInfo: in quarantine: %ld malloced: %ld; ",
+ quarantine_.size() >> 20, malloced >> 20);
+ for (size_t j = 1; j < kNumberOfSizeClasses; j++) {
+ AsanChunk *i = free_lists_[j];
+ if (!i) continue;
+ size_t t = 0;
+ for (; i; i = i->next) {
+ t += i->Size();
+ }
+ Printf("%ld:%ld ", j, t >> 20);
+ }
+ Printf("\n");
+ }
+
+ PageGroup *FindPageGroup(uintptr_t addr) {
+ ScopedLock lock(&mu_);
+ return FindPageGroupUnlocked(addr);
+ }
+
+ private:
+ PageGroup *FindPageGroupUnlocked(uintptr_t addr) {
+ for (int i = 0; i < n_page_groups_; i++) {
+ PageGroup *g = page_groups_[i];
+ if (g->InRange(addr)) {
+ return g;
+ }
+ }
+ return NULL;
+ }
+
+ // We have an address between two chunks, and we want to report just one.
+ AsanChunk *ChooseChunk(uintptr_t addr,
+ AsanChunk *left_chunk, AsanChunk *right_chunk) {
+ // Prefer an allocated chunk or a chunk from quarantine.
+ if (left_chunk->chunk_state == CHUNK_AVAILABLE &&
+ right_chunk->chunk_state != CHUNK_AVAILABLE)
+ return right_chunk;
+ if (right_chunk->chunk_state == CHUNK_AVAILABLE &&
+ left_chunk->chunk_state != CHUNK_AVAILABLE)
+ return left_chunk;
+ // Choose based on offset.
+ size_t l_offset = 0, r_offset = 0;
+ CHECK(left_chunk->AddrIsAtRight(addr, 1, &l_offset));
+ CHECK(right_chunk->AddrIsAtLeft(addr, 1, &r_offset));
+ if (l_offset < r_offset)
+ return left_chunk;
+ return right_chunk;
+ }
+
+ AsanChunk *FindChunkByAddr(uintptr_t addr) {
+ PageGroup *g = FindPageGroupUnlocked(addr);
+ if (!g) return 0;
+ CHECK(g->size_of_chunk);
+ uintptr_t offset_from_beg = addr - g->beg;
+ uintptr_t this_chunk_addr = g->beg +
+ (offset_from_beg / g->size_of_chunk) * g->size_of_chunk;
+ CHECK(g->InRange(this_chunk_addr));
+ AsanChunk *m = (AsanChunk*)this_chunk_addr;
+ CHECK(m->chunk_state == CHUNK_ALLOCATED ||
+ m->chunk_state == CHUNK_AVAILABLE ||
+ m->chunk_state == CHUNK_QUARANTINE);
+ size_t offset = 0;
+ if (m->AddrIsInside(addr, 1, &offset))
+ return m;
+
+ if (m->AddrIsAtRight(addr, 1, &offset)) {
+ if (this_chunk_addr == g->last_chunk) // rightmost chunk
+ return m;
+ uintptr_t right_chunk_addr = this_chunk_addr + g->size_of_chunk;
+ CHECK(g->InRange(right_chunk_addr));
+ return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr);
+ } else {
+ CHECK(m->AddrIsAtLeft(addr, 1, &offset));
+ if (this_chunk_addr == g->beg) // leftmost chunk
+ return m;
+ uintptr_t left_chunk_addr = this_chunk_addr - g->size_of_chunk;
+ CHECK(g->InRange(left_chunk_addr));
+ return ChooseChunk(addr, (AsanChunk*)left_chunk_addr, m);
+ }
+ }
+
+ void QuarantinePop() {
+ CHECK(quarantine_.size() > 0);
+ AsanChunk *m = quarantine_.Pop();
+ CHECK(m);
+ // if (F_v >= 2) Printf("MallocInfo::pop %p\n", m);
+
+ CHECK(m->chunk_state == CHUNK_QUARANTINE);
+ m->chunk_state = CHUNK_AVAILABLE;
+ CHECK(m->alloc_tid >= 0);
+ CHECK(m->free_tid >= 0);
+
+ size_t size_class = m->SizeClass();
+ m->next = free_lists_[size_class];
+ free_lists_[size_class] = m;
+
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.real_frees++;
+ thread_stats.really_freed += m->used_size;
+ thread_stats.really_freed_redzones += m->Size() - m->used_size;
+ thread_stats.really_freed_by_size[m->SizeClass()]++;
+ }
+
+ // Get a list of newly allocated chunks.
+ AsanChunk *GetNewChunks(uint8_t size_class) {
+ size_t size = SizeClassToSize(size_class);
+ CHECK(IsPowerOfTwo(kMinMmapSize));
+ CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0);
+ size_t mmap_size = Max(size, kMinMmapSize);
+ size_t n_chunks = mmap_size / size;
+ CHECK(n_chunks * size == mmap_size);
+ if (size < kPageSize) {
+ // Size is small, just poison the last chunk.
+ n_chunks--;
+ } else {
+ // Size is large, allocate an extra page at right and poison it.
+ mmap_size += kPageSize;
+ }
+ CHECK(n_chunks > 0);
+ uint8_t *mem = MmapNewPagesAndPoisonShadow(mmap_size);
+
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.mmaps++;
+ thread_stats.mmaped += mmap_size;
+ thread_stats.mmaped_by_size[size_class] += n_chunks;
+
+ AsanChunk *res = NULL;
+ for (size_t i = 0; i < n_chunks; i++) {
+ AsanChunk *m = (AsanChunk*)(mem + i * size);
+ m->chunk_state = CHUNK_AVAILABLE;
+ m->size_class = size_class;
+ m->next = res;
+ res = m;
+ }
+ PageGroup *pg = (PageGroup*)(mem + n_chunks * size);
+ // This memory is already poisoned, no need to poison it again.
+ pg->beg = (uintptr_t)mem;
+ pg->end = pg->beg + mmap_size;
+ pg->size_of_chunk = size;
+ pg->last_chunk = (uintptr_t)(mem + size * (n_chunks - 1));
+ int page_group_idx = AtomicInc(&n_page_groups_) - 1;
+ CHECK(page_group_idx < (int)ASAN_ARRAY_SIZE(page_groups_));
+ page_groups_[page_group_idx] = pg;
+ return res;
+ }
+
+ AsanChunk *free_lists_[kNumberOfSizeClasses];
+ AsanChunkFifoList quarantine_;
+ AsanLock mu_;
+
+ PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize];
+ int n_page_groups_; // atomic
+};
+
+static MallocInfo malloc_info(LINKER_INITIALIZED);
+
+void AsanThreadLocalMallocStorage::CommitBack() {
+ malloc_info.SwallowThreadLocalMallocStorage(this, true);
+}
+
+static void Describe(uintptr_t addr, size_t access_size) {
+ AsanChunk *m = malloc_info.FindMallocedOrFreed(addr, access_size);
+ if (!m) return;
+ m->DescribeAddress(addr, access_size);
+ CHECK(m->alloc_tid >= 0);
+ AsanThreadSummary *alloc_thread =
+ asanThreadRegistry().FindByTid(m->alloc_tid);
+ AsanStackTrace alloc_stack;
+ AsanStackTrace::UncompressStack(&alloc_stack, m->compressed_alloc_stack(),
+ m->compressed_alloc_stack_size());
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ CHECK(t);
+ if (m->free_tid >= 0) {
+ AsanThreadSummary *free_thread =
+ asanThreadRegistry().FindByTid(m->free_tid);
+ Printf("freed by thread T%d here:\n", free_thread->tid());
+ AsanStackTrace free_stack;
+ AsanStackTrace::UncompressStack(&free_stack, m->compressed_free_stack(),
+ m->compressed_free_stack_size());
+ free_stack.PrintStack();
+ Printf("previously allocated by thread T%d here:\n",
+ alloc_thread->tid());
+
+ alloc_stack.PrintStack();
+ t->summary()->Announce();
+ free_thread->Announce();
+ alloc_thread->Announce();
+ } else {
+ Printf("allocated by thread T%d here:\n", alloc_thread->tid());
+ alloc_stack.PrintStack();
+ t->summary()->Announce();
+ alloc_thread->Announce();
+ }
+}
+
+static uint8_t *Allocate(size_t alignment, size_t size, AsanStackTrace *stack) {
+ __asan_init();
+ CHECK(stack);
+ if (size == 0) {
+ size = 1; // TODO(kcc): do something smarter
+ }
+ CHECK(IsPowerOfTwo(alignment));
+ size_t rounded_size = RoundUpTo(size, REDZONE);
+ size_t needed_size = rounded_size + REDZONE;
+ if (alignment > REDZONE) {
+ needed_size += alignment;
+ }
+ CHECK(IsAligned(needed_size, REDZONE));
+ if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+ Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", size);
+ return 0;
+ }
+
+ uint8_t size_class = SizeToSizeClass(needed_size);
+ size_t size_to_allocate = SizeClassToSize(size_class);
+ CHECK(size_to_allocate >= kMinAllocSize);
+ CHECK(size_to_allocate >= needed_size);
+ CHECK(IsAligned(size_to_allocate, REDZONE));
+
+ if (FLAG_v >= 2) {
+ Printf("Allocate align: %ld size: %ld class: %d real: %ld\n",
+ alignment, size, size_class, size_to_allocate);
+ }
+
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ // Statistics
+ thread_stats.mallocs++;
+ thread_stats.malloced += size;
+ thread_stats.malloced_redzones += size_to_allocate - size;
+ thread_stats.malloced_by_size[size_class]++;
+
+ AsanChunk *m = NULL;
+ if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) {
+ // get directly from global storage.
+ m = malloc_info.AllocateChunks(size_class, 1);
+ thread_stats.malloc_large++;
+ } else {
+ // get from the thread-local storage.
+ AsanChunk **fl = &t->malloc_storage().free_lists_[size_class];
+ if (!*fl) {
+ size_t n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate;
+ *fl = malloc_info.AllocateChunks(size_class, n_new_chunks);
+ thread_stats.malloc_small_slow++;
+ }
+ m = *fl;
+ *fl = (*fl)->next;
+ }
+ CHECK(m);
+ CHECK(m->chunk_state == CHUNK_AVAILABLE);
+ m->chunk_state = CHUNK_ALLOCATED;
+ m->next = NULL;
+ CHECK(m->Size() == size_to_allocate);
+ uintptr_t addr = (uintptr_t)m + REDZONE;
+ CHECK(addr == (uintptr_t)m->compressed_free_stack());
+
+ if (alignment > REDZONE && (addr & (alignment - 1))) {
+ addr = RoundUpTo(addr, alignment);
+ CHECK((addr & (alignment - 1)) == 0);
+ AsanChunk *p = (AsanChunk*)(addr - REDZONE);
+ p->chunk_state = CHUNK_MEMALIGN;
+ p->next = m;
+ }
+ CHECK(m == PtrToChunk(addr));
+ m->used_size = size;
+ m->offset = addr - (uintptr_t)m;
+ CHECK(m->beg() == addr);
+ m->alloc_tid = t ? t->tid() : 0;
+ m->free_tid = AsanThread::kInvalidTid;
+ AsanStackTrace::CompressStack(stack, m->compressed_alloc_stack(),
+ m->compressed_alloc_stack_size());
+ PoisonShadow(addr, rounded_size, 0);
+ if (size < rounded_size) {
+ PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE,
+ size & (REDZONE - 1));
+ }
+ if (size <= FLAG_max_malloc_fill_size) {
+ real_memset((void*)addr, 0, rounded_size);
+ }
+ return (uint8_t*)addr;
+}
+
+static void Deallocate(uint8_t *ptr, AsanStackTrace *stack) {
+ if (!ptr) return;
+ CHECK(stack);
+
+ if (FLAG_debug) {
+ CHECK(malloc_info.FindPageGroup((uintptr_t)ptr));
+ }
+
+ // Printf("Deallocate %p\n", ptr);
+ AsanChunk *m = PtrToChunk((uintptr_t)ptr);
+ if (m->chunk_state == CHUNK_QUARANTINE) {
+ Report("ERROR: AddressSanitizer attempting double-free on %p:\n", ptr);
+ stack->PrintStack();
+ m->DescribeAddress((uintptr_t)ptr, 1);
+ ShowStatsAndAbort();
+ } else if (m->chunk_state != CHUNK_ALLOCATED) {
+ Report("ERROR: AddressSanitizer attempting free on address which was not"
+ " malloc()-ed: %p\n", ptr);
+ stack->PrintStack();
+ ShowStatsAndAbort();
+ }
+ CHECK(m->chunk_state == CHUNK_ALLOCATED);
+ CHECK(m->free_tid == AsanThread::kInvalidTid);
+ CHECK(m->alloc_tid >= 0);
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ m->free_tid = t ? t->tid() : 0;
+ AsanStackTrace::CompressStack(stack, m->compressed_free_stack(),
+ m->compressed_free_stack_size());
+ size_t rounded_size = RoundUpTo(m->used_size, REDZONE);
+ PoisonShadow((uintptr_t)ptr, rounded_size, kAsanHeapFreeMagic);
+
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.frees++;
+ thread_stats.freed += m->used_size;
+ thread_stats.freed_by_size[m->SizeClass()]++;
+
+ m->chunk_state = CHUNK_QUARANTINE;
+ if (t) {
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ CHECK(!m->next);
+ ms->quarantine_.Push(m);
+
+ if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) {
+ malloc_info.SwallowThreadLocalMallocStorage(ms, false);
+ }
+ } else {
+ CHECK(!m->next);
+ malloc_info.BypassThreadLocalQuarantine(m);
+ }
+}
+
+static uint8_t *Reallocate(uint8_t *old_ptr, size_t new_size,
+ AsanStackTrace *stack) {
+ CHECK(old_ptr && new_size);
+
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.reallocs++;
+ thread_stats.realloced += new_size;
+
+ AsanChunk *m = PtrToChunk((uintptr_t)old_ptr);
+ CHECK(m->chunk_state == CHUNK_ALLOCATED);
+ size_t old_size = m->used_size;
+ size_t memcpy_size = Min(new_size, old_size);
+ uint8_t *new_ptr = Allocate(0, new_size, stack);
+ if (new_ptr) {
+ real_memcpy(new_ptr, old_ptr, memcpy_size);
+ Deallocate(old_ptr, stack);
+ }
+ return new_ptr;
+}
+
+} // namespace __asan
+
+// Malloc hooks declaration.
+// ASAN_NEW_HOOK(ptr, size) is called immediately after
+// allocation of "size" bytes, which returned "ptr".
+// ASAN_DELETE_HOOK(ptr) is called immediately before
+// deallocation of "ptr".
+// If ASAN_NEW_HOOK or ASAN_DELETE_HOOK is defined, user
+// program must provide implementation of this hook.
+// If macro is undefined, the hook is no-op.
+#ifdef ASAN_NEW_HOOK
+extern "C" void ASAN_NEW_HOOK(void *ptr, size_t size);
+#else
+static inline void ASAN_NEW_HOOK(void *ptr, size_t size) { }
+#endif
+
+#ifdef ASAN_DELETE_HOOK
+extern "C" void ASAN_DELETE_HOOK(void *ptr);
+#else
+static inline void ASAN_DELETE_HOOK(void *ptr) { }
+#endif
+
+namespace __asan {
+
+void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack) {
+ void *ptr = (void*)Allocate(alignment, size, stack);
+ ASAN_NEW_HOOK(ptr, size);
+ return ptr;
+}
+
+void asan_free(void *ptr, AsanStackTrace *stack) {
+ ASAN_DELETE_HOOK(ptr);
+ Deallocate((uint8_t*)ptr, stack);
+}
+
+void *asan_malloc(size_t size, AsanStackTrace *stack) {
+ void *ptr = (void*)Allocate(0, size, stack);
+ ASAN_NEW_HOOK(ptr, size);
+ return ptr;
+}
+
+void *asan_calloc(size_t nmemb, size_t size, AsanStackTrace *stack) {
+ void *ptr = (void*)Allocate(0, nmemb * size, stack);
+ if (ptr)
+ real_memset(ptr, 0, nmemb * size);
+ ASAN_NEW_HOOK(ptr, nmemb * size);
+ return ptr;
+}
+
+void *asan_realloc(void *p, size_t size, AsanStackTrace *stack) {
+ if (p == NULL) {
+ void *ptr = (void*)Allocate(0, size, stack);
+ ASAN_NEW_HOOK(ptr, size);
+ return ptr;
+ } else if (size == 0) {
+ ASAN_DELETE_HOOK(p);
+ Deallocate((uint8_t*)p, stack);
+ return NULL;
+ }
+ return Reallocate((uint8_t*)p, size, stack);
+}
+
+void *asan_valloc(size_t size, AsanStackTrace *stack) {
+ void *ptr = (void*)Allocate(kPageSize, size, stack);
+ ASAN_NEW_HOOK(ptr, size);
+ return ptr;
+}
+
+void *asan_pvalloc(size_t size, AsanStackTrace *stack) {
+ size = RoundUpTo(size, kPageSize);
+ if (size == 0) {
+ // pvalloc(0) should allocate one page.
+ size = kPageSize;
+ }
+ void *ptr = (void*)Allocate(kPageSize, size, stack);
+ ASAN_NEW_HOOK(ptr, size);
+ return ptr;
+}
+
+int asan_posix_memalign(void **memptr, size_t alignment, size_t size,
+ AsanStackTrace *stack) {
+ void *ptr = Allocate(alignment, size, stack);
+ CHECK(IsAligned((uintptr_t)ptr, alignment));
+ ASAN_NEW_HOOK(ptr, size);
+ *memptr = ptr;
+ return 0;
+}
+
+size_t __asan_mz_size(const void *ptr) {
+ return malloc_info.AllocationSize((uintptr_t)ptr);
+}
+
+void DescribeHeapAddress(uintptr_t addr, uintptr_t access_size) {
+ Describe(addr, access_size);
+}
+
+void __asan_mz_force_lock() {
+ malloc_info.ForceLock();
+}
+
+void __asan_mz_force_unlock() {
+ malloc_info.ForceUnlock();
+}
+
+// ---------------------- Fake stack-------------------- {{{1
+FakeStack::FakeStack() {
+ CHECK(real_memset);
+ real_memset(this, 0, sizeof(*this));
+}
+
+bool FakeStack::AddrIsInSizeClass(uintptr_t addr, size_t size_class) {
+ uintptr_t mem = allocated_size_classes_[size_class];
+ uintptr_t size = ClassMmapSize(size_class);
+ bool res = mem && addr >= mem && addr < mem + size;
+ return res;
+}
+
+uintptr_t FakeStack::AddrIsInFakeStack(uintptr_t addr) {
+ for (size_t i = 0; i < kNumberOfSizeClasses; i++) {
+ if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
+ }
+ return 0;
+}
+
+// We may want to compute this during compilation.
+inline size_t FakeStack::ComputeSizeClass(size_t alloc_size) {
+ size_t rounded_size = RoundUpToPowerOfTwo(alloc_size);
+ size_t log = Log2(rounded_size);
+ CHECK(alloc_size <= (1UL << log));
+ if (!(alloc_size > (1UL << (log-1)))) {
+ Printf("alloc_size %ld log %ld\n", alloc_size, log);
+ }
+ CHECK(alloc_size > (1UL << (log-1)));
+ size_t res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
+ CHECK(res < kNumberOfSizeClasses);
+ CHECK(ClassSize(res) >= rounded_size);
+ return res;
+}
+
+void FakeFrameFifo::FifoPush(FakeFrame *node) {
+ CHECK(node);
+ node->next = 0;
+ if (first_ == 0 && last_ == 0) {
+ first_ = last_ = node;
+ } else {
+ CHECK(first_);
+ CHECK(last_);
+ last_->next = node;
+ last_ = node;
+ }
+}
+
+FakeFrame *FakeFrameFifo::FifoPop() {
+ CHECK(first_ && last_ && "Exhausted fake stack");
+ FakeFrame *res = 0;
+ if (first_ == last_) {
+ res = first_;
+ first_ = last_ = 0;
+ } else {
+ res = first_;
+ first_ = first_->next;
+ }
+ return res;
+}
+
+void FakeStack::Init(size_t stack_size) {
+ stack_size_ = stack_size;
+ alive_ = true;
+}
+
+void FakeStack::Cleanup() {
+ alive_ = false;
+ for (size_t i = 0; i < kNumberOfSizeClasses; i++) {
+ uintptr_t mem = allocated_size_classes_[i];
+ if (mem) {
+ PoisonShadow(mem, ClassMmapSize(i), 0);
+ allocated_size_classes_[i] = 0;
+ AsanUnmapOrDie((void*)mem, ClassMmapSize(i));
+ }
+ }
+}
+
+size_t FakeStack::ClassMmapSize(size_t size_class) {
+ return RoundUpToPowerOfTwo(stack_size_);
+}
+
+void FakeStack::AllocateOneSizeClass(size_t size_class) {
+ CHECK(ClassMmapSize(size_class) >= kPageSize);
+ uintptr_t new_mem = (uintptr_t)AsanMmapSomewhereOrDie(
+ ClassMmapSize(size_class), __FUNCTION__);
+ // Printf("T%d new_mem[%ld]: %p-%p mmap %ld\n",
+ // asanThreadRegistry().GetCurrent()->tid(),
+ // size_class, new_mem, new_mem + ClassMmapSize(size_class),
+ // ClassMmapSize(size_class));
+ size_t i;
+ for (i = 0; i < ClassMmapSize(size_class);
+ i += ClassSize(size_class)) {
+ size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
+ }
+ CHECK(i == ClassMmapSize(size_class));
+ allocated_size_classes_[size_class] = new_mem;
+}
+
+uintptr_t FakeStack::AllocateStack(size_t size, size_t real_stack) {
+ if (!alive_) return real_stack;
+ CHECK(size <= kMaxStackMallocSize && size > 1);
+ size_t size_class = ComputeSizeClass(size);
+ if (!allocated_size_classes_[size_class]) {
+ AllocateOneSizeClass(size_class);
+ }
+ FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
+ CHECK(fake_frame);
+ fake_frame->size_minus_one = size - 1;
+ fake_frame->real_stack = real_stack;
+ while (FakeFrame *top = call_stack_.top()) {
+ if (top->real_stack > real_stack) break;
+ call_stack_.LifoPop();
+ DeallocateFrame(top);
+ }
+ call_stack_.LifoPush(fake_frame);
+ uintptr_t ptr = (uintptr_t)fake_frame;
+ PoisonShadow(ptr, size, 0);
+ return ptr;
+}
+
+void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
+ CHECK(alive_);
+ size_t size = fake_frame->size_minus_one + 1;
+ size_t size_class = ComputeSizeClass(size);
+ CHECK(allocated_size_classes_[size_class]);
+ uintptr_t ptr = (uintptr_t)fake_frame;
+ CHECK(AddrIsInSizeClass(ptr, size_class));
+ CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
+ size_classes_[size_class].FifoPush(fake_frame);
+}
+
+void FakeStack::OnFree(size_t ptr, size_t size, size_t real_stack) {
+ FakeFrame *fake_frame = (FakeFrame*)ptr;
+ CHECK(fake_frame->magic = kRetiredStackFrameMagic);
+ CHECK(fake_frame->descr != 0);
+ CHECK(fake_frame->size_minus_one == size - 1);
+ PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+size_t __asan_stack_malloc(size_t size, size_t real_stack) {
+ if (!FLAG_use_fake_stack) return real_stack;
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (!t) {
+ // TSD is gone, use the real stack.
+ return real_stack;
+ }
+ size_t ptr = t->fake_stack().AllocateStack(size, real_stack);
+ // Printf("__asan_stack_malloc %p %ld %p\n", ptr, size, real_stack);
+ return ptr;
+}
+
+void __asan_stack_free(size_t ptr, size_t size, size_t real_stack) {
+ if (!FLAG_use_fake_stack) return;
+ if (ptr != real_stack) {
+ FakeStack::OnFree(ptr, size, real_stack);
+ }
+}
+
+// ASan allocator doesn't reserve extra bytes, so normally we would
+// just return "size".
+size_t __asan_get_estimated_allocated_size(size_t size) {
+ if (size == 0) return 1;
+ return Min(size, kMaxAllowedMallocSize);
+}
+
+bool __asan_get_ownership(const void *p) {
+ return (p == NULL) ||
+ (malloc_info.AllocationSize((uintptr_t)p) > 0);
+}
+
+size_t __asan_get_allocated_size(const void *p) {
+ if (p == NULL) return 0;
+ size_t allocated_size = malloc_info.AllocationSize((uintptr_t)p);
+ // Die if p is not malloced or if it is already freed.
+ if (allocated_size == 0) {
+ Printf("__asan_get_allocated_size failed, ptr=%p is not owned\n", p);
+ PRINT_CURRENT_STACK();
+ ShowStatsAndAbort();
+ }
+ return allocated_size;
+}
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
new file mode 100644
index 0000000..9b691e0
--- /dev/null
+++ b/lib/asan/asan_allocator.h
@@ -0,0 +1,157 @@
+//===-- asan_allocator.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_allocator.cc.
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_ALLOCATOR_H
+#define ASAN_ALLOCATOR_H
+
+#include "asan_internal.h"
+#include "asan_interceptors.h"
+
+namespace __asan {
+
+static const size_t kNumberOfSizeClasses = 255;
+struct AsanChunk;
+
+class AsanChunkFifoList {
+ public:
+ explicit AsanChunkFifoList(LinkerInitialized) { }
+ AsanChunkFifoList() { clear(); }
+ void Push(AsanChunk *n);
+ void PushList(AsanChunkFifoList *q);
+ AsanChunk *Pop();
+ size_t size() { return size_; }
+ void clear() {
+ first_ = last_ = NULL;
+ size_ = 0;
+ }
+ private:
+ AsanChunk *first_;
+ AsanChunk *last_;
+ size_t size_;
+};
+
+struct AsanThreadLocalMallocStorage {
+ explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
+ : quarantine_(x) { }
+ AsanThreadLocalMallocStorage() {
+ CHECK(real_memset);
+ real_memset(this, 0, sizeof(AsanThreadLocalMallocStorage));
+ }
+
+ AsanChunkFifoList quarantine_;
+ AsanChunk *free_lists_[kNumberOfSizeClasses];
+ void CommitBack();
+};
+
+// Fake stack frame contains local variables of one function.
+// This struct should fit into a stack redzone (32 bytes).
+struct FakeFrame {
+ uintptr_t magic; // Modified by the instrumented code.
+ uintptr_t descr; // Modified by the instrumented code.
+ FakeFrame *next;
+ uint64_t real_stack : 48;
+ uint64_t size_minus_one : 16;
+};
+
+struct FakeFrameFifo {
+ public:
+ void FifoPush(FakeFrame *node);
+ FakeFrame *FifoPop();
+ private:
+ FakeFrame *first_, *last_;
+};
+
+class FakeFrameLifo {
+ public:
+ void LifoPush(FakeFrame *node) {
+ node->next = top_;
+ top_ = node;
+ }
+ void LifoPop() {
+ CHECK(top_);
+ top_ = top_->next;
+ }
+ FakeFrame *top() { return top_; }
+ private:
+ FakeFrame *top_;
+};
+
+// For each thread we create a fake stack and place stack objects on this fake
+// stack instead of the real stack. The fake stack is not really a stack but
+// a fast malloc-like allocator so that when a function exits the fake stack
+// is not poped but remains there for quite some time until gets used again.
+// So, we poison the objects on the fake stack when function returns.
+// It helps us find use-after-return bugs.
+// We can not rely on __asan_stack_free being called on every function exit,
+// so we maintain a lifo list of all current fake frames and update it on every
+// call to __asan_stack_malloc.
+class FakeStack {
+ public:
+ FakeStack();
+ explicit FakeStack(LinkerInitialized) {}
+ void Init(size_t stack_size);
+ void StopUsingFakeStack() { alive_ = false; }
+ void Cleanup();
+ uintptr_t AllocateStack(size_t size, size_t real_stack);
+ static void OnFree(size_t ptr, size_t size, size_t real_stack);
+ // Return the bottom of the maped region.
+ uintptr_t AddrIsInFakeStack(uintptr_t addr);
+ private:
+ static const size_t kMinStackFrameSizeLog = 9; // Min frame is 512B.
+ static const size_t kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
+ static const size_t kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
+ static const size_t kNumberOfSizeClasses =
+ kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
+
+ bool AddrIsInSizeClass(uintptr_t addr, size_t size_class);
+
+ // Each size class should be large enough to hold all frames.
+ size_t ClassMmapSize(size_t size_class);
+
+ size_t ClassSize(size_t size_class) {
+ return 1UL << (size_class + kMinStackFrameSizeLog);
+ }
+
+ void DeallocateFrame(FakeFrame *fake_frame);
+
+ size_t ComputeSizeClass(size_t alloc_size);
+ void AllocateOneSizeClass(size_t size_class);
+
+ size_t stack_size_;
+ bool alive_;
+
+ uintptr_t allocated_size_classes_[kNumberOfSizeClasses];
+ FakeFrameFifo size_classes_[kNumberOfSizeClasses];
+ FakeFrameLifo call_stack_;
+};
+
+void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack);
+void asan_free(void *ptr, AsanStackTrace *stack);
+
+void *asan_malloc(size_t size, AsanStackTrace *stack);
+void *asan_calloc(size_t nmemb, size_t size, AsanStackTrace *stack);
+void *asan_realloc(void *p, size_t size, AsanStackTrace *stack);
+void *asan_valloc(size_t size, AsanStackTrace *stack);
+void *asan_pvalloc(size_t size, AsanStackTrace *stack);
+
+int asan_posix_memalign(void **memptr, size_t alignment, size_t size,
+ AsanStackTrace *stack);
+
+size_t __asan_mz_size(const void *ptr);
+void __asan_mz_force_lock();
+void __asan_mz_force_unlock();
+void DescribeHeapAddress(uintptr_t addr, size_t access_size);
+
+} // namespace __asan
+#endif // ASAN_ALLOCATOR_H
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
new file mode 100644
index 0000000..f53bf38
--- /dev/null
+++ b/lib/asan/asan_globals.cc
@@ -0,0 +1,171 @@
+//===-- asan_globals.cc -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Handle globals.
+//===----------------------------------------------------------------------===//
+#include "asan_interceptors.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_lock.h"
+#include "asan_mapping.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_thread.h"
+
+#include <ctype.h>
+
+namespace __asan {
+
+typedef __asan_global Global;
+
+struct ListOfGlobals {
+ const Global *g;
+ ListOfGlobals *next;
+};
+
+static AsanLock mu_for_globals(LINKER_INITIALIZED);
+static ListOfGlobals *list_of_globals;
+static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);
+
+void PoisonRedZones(const Global &g) {
+ size_t shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
+ CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4);
+ // full right redzone
+ size_t g_aligned_size = kGlobalAndStackRedzone *
+ ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
+ PoisonShadow(g.beg + g_aligned_size,
+ kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic);
+ if ((g.size % kGlobalAndStackRedzone) != 0) {
+ // partial right redzone
+ uint64_t g_aligned_down_size = kGlobalAndStackRedzone *
+ (g.size / kGlobalAndStackRedzone);
+ CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone);
+ PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size,
+ g.size % kGlobalAndStackRedzone,
+ kGlobalAndStackRedzone,
+ kAsanGlobalRedzoneMagic);
+ }
+}
+
+static size_t GetAlignedSize(size_t size) {
+ return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
+ * kGlobalAndStackRedzone;
+}
+
+ // Check if the global is a zero-terminated ASCII string. If so, print it.
+void PrintIfASCII(const Global &g) {
+ for (size_t p = g.beg; p < g.beg + g.size - 1; p++) {
+ if (!isascii(*(char*)p)) return;
+ }
+ if (*(char*)(g.beg + g.size - 1) != 0) return;
+ Printf(" '%s' is ascii string '%s'\n", g.name, g.beg);
+}
+
+bool DescribeAddrIfMyRedZone(const Global &g, uintptr_t addr) {
+ if (addr < g.beg - kGlobalAndStackRedzone) return false;
+ if (addr >= g.beg + g.size_with_redzone) return false;
+ Printf("%p is located ", addr);
+ if (addr < g.beg) {
+ Printf("%d bytes to the left", g.beg - addr);
+ } else if (addr >= g.beg + g.size) {
+ Printf("%d bytes to the right", addr - (g.beg + g.size));
+ } else {
+ Printf("%d bytes inside", addr - g.beg); // Can it happen?
+ }
+ Printf(" of global variable '%s' (0x%lx) of size %ld\n",
+ g.name, g.beg, g.size);
+ PrintIfASCII(g);
+ return true;
+}
+
+
+bool DescribeAddrIfGlobal(uintptr_t addr) {
+ if (!FLAG_report_globals) return false;
+ ScopedLock lock(&mu_for_globals);
+ bool res = false;
+ for (ListOfGlobals *l = list_of_globals; l; l = l->next) {
+ const Global &g = *l->g;
+ if (FLAG_report_globals >= 2)
+ Printf("Search Global: beg=%p size=%ld name=%s\n",
+ g.beg, g.size, g.name);
+ res |= DescribeAddrIfMyRedZone(g, addr);
+ }
+ return res;
+}
+
+// Register a global variable.
+// This function may be called more than once for every global
+// so we store the globals in a map.
+static void RegisterGlobal(const Global *g) {
+ CHECK(asan_inited);
+ CHECK(FLAG_report_globals);
+ CHECK(AddrIsInMem(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+ PoisonRedZones(*g);
+ ListOfGlobals *l =
+ (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
+ l->g = g;
+ l->next = list_of_globals;
+ list_of_globals = l;
+ if (FLAG_report_globals >= 2)
+ Report("Added Global: beg=%p size=%ld name=%s\n",
+ g->beg, g->size, g->name);
+}
+
+static void UnregisterGlobal(const Global *g) {
+ CHECK(asan_inited);
+ CHECK(FLAG_report_globals);
+ CHECK(AddrIsInMem(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+ PoisonShadow(g->beg, g->size_with_redzone, 0);
+ // We unpoison the shadow memory for the global but we do not remove it from
+ // the list because that would require O(n^2) time with the current list
+ // implementation. It might not be worth doing anyway.
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// Register one global with a default redzone.
+void __asan_register_global(uintptr_t addr, size_t size,
+ const char *name) {
+ if (!FLAG_report_globals) return;
+ ScopedLock lock(&mu_for_globals);
+ Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
+ g->beg = addr;
+ g->size = size;
+ g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
+ g->name = name;
+ RegisterGlobal(g);
+}
+
+// Register an array of globals.
+void __asan_register_globals(__asan_global *globals, size_t n) {
+ if (!FLAG_report_globals) return;
+ ScopedLock lock(&mu_for_globals);
+ for (size_t i = 0; i < n; i++) {
+ RegisterGlobal(&globals[i]);
+ }
+}
+
+// Unregister an array of globals.
+// We must do it when a shared objects gets dlclosed.
+void __asan_unregister_globals(__asan_global *globals, size_t n) {
+ if (!FLAG_report_globals) return;
+ ScopedLock lock(&mu_for_globals);
+ for (size_t i = 0; i < n; i++) {
+ UnregisterGlobal(&globals[i]);
+ }
+}
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
new file mode 100644
index 0000000..53ef91a
--- /dev/null
+++ b/lib/asan/asan_interceptors.cc
@@ -0,0 +1,391 @@
+//===-- asan_interceptors.cc ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Intercept various libc functions to catch buggy memory accesses there.
+//===----------------------------------------------------------------------===//
+#include "asan_interceptors.h"
+
+#include "asan_allocator.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+
+#include <ctype.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <strings.h>
+
+namespace __asan {
+
+index_f real_index;
+memcmp_f real_memcmp;
+memcpy_f real_memcpy;
+memmove_f real_memmove;
+memset_f real_memset;
+strcasecmp_f real_strcasecmp;
+strcat_f real_strcat;
+strchr_f real_strchr;
+strcmp_f real_strcmp;
+strcpy_f real_strcpy;
+strdup_f real_strdup;
+strlen_f real_strlen;
+strncasecmp_f real_strncasecmp;
+strncmp_f real_strncmp;
+strncpy_f real_strncpy;
+strnlen_f real_strnlen;
+
+// Instruments read/write access to a single byte in memory.
+// On error calls __asan_report_error, which aborts the program.
+__attribute__((noinline))
+static void AccessAddress(uintptr_t address, bool isWrite) {
+ if (__asan_address_is_poisoned((void*)address)) {
+ GET_BP_PC_SP;
+ __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1);
+ }
+}
+
+// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
+// and ASAN_WRITE_RANGE as macro instead of function so
+// that no extra frames are created, and stack trace contains
+// relevant information only.
+
+// Instruments read/write access to a memory range.
+// More complex implementation is possible, for now just
+// checking the first and the last byte of a range.
+#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
+ if (size > 0) { \
+ uintptr_t ptr = (uintptr_t)(offset); \
+ AccessAddress(ptr, isWrite); \
+ AccessAddress(ptr + (size) - 1, isWrite); \
+ } \
+} while (0)
+
+#define ASAN_READ_RANGE(offset, size) do { \
+ ACCESS_MEMORY_RANGE(offset, size, false); \
+} while (0)
+
+#define ASAN_WRITE_RANGE(offset, size) do { \
+ ACCESS_MEMORY_RANGE(offset, size, true); \
+} while (0)
+
+// Behavior of functions like "memcpy" or "strcpy" is undefined
+// if memory intervals overlap. We report error in this case.
+// Macro is used to avoid creation of new frames.
+static inline bool RangesOverlap(const char *offset1, size_t length1,
+ const char *offset2, size_t length2) {
+ return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1));
+}
+#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \
+ const char *offset1 = (const char*)_offset1; \
+ const char *offset2 = (const char*)_offset2; \
+ if (RangesOverlap(offset1, length1, offset2, length2)) { \
+ Report("ERROR: AddressSanitizer %s-param-overlap: " \
+ "memory ranges [%p,%p) and [%p, %p) overlap\n", \
+ name, offset1, offset1 + length1, offset2, offset2 + length2); \
+ PRINT_CURRENT_STACK(); \
+ ShowStatsAndAbort(); \
+ } \
+} while (0)
+
+#define ENSURE_ASAN_INITED() do { \
+ CHECK(!asan_init_is_running); \
+ if (!asan_inited) { \
+ __asan_init(); \
+ } \
+} while (0)
+
+size_t internal_strlen(const char *s) {
+ size_t i = 0;
+ while (s[i]) i++;
+ return i;
+}
+
+size_t internal_strnlen(const char *s, size_t maxlen) {
+ if (real_strnlen != NULL) {
+ return real_strnlen(s, maxlen);
+ }
+ size_t i = 0;
+ while (i < maxlen && s[i]) i++;
+ return i;
+}
+
+void* internal_memchr(const void* s, int c, size_t n) {
+ const char* t = (char*)s;
+ for (size_t i = 0; i < n; ++i, ++t)
+ if (*t == c)
+ return (void*)t;
+ return NULL;
+}
+
+int internal_memcmp(const void* s1, const void* s2, size_t n) {
+ const char* t1 = (char*)s1;
+ const char* t2 = (char*)s2;
+ for (size_t i = 0; i < n; ++i, ++t1, ++t2)
+ if (*t1 != *t2)
+ return *t1 < *t2 ? -1 : 1;
+ return 0;
+}
+
+void InitializeAsanInterceptors() {
+#ifndef __APPLE__
+ INTERCEPT_FUNCTION(index);
+#else
+ OVERRIDE_FUNCTION(index, WRAP(strchr));
+#endif
+ INTERCEPT_FUNCTION(memcmp);
+ INTERCEPT_FUNCTION(memcpy);
+ INTERCEPT_FUNCTION(memmove);
+ INTERCEPT_FUNCTION(memset);
+ INTERCEPT_FUNCTION(strcasecmp);
+ INTERCEPT_FUNCTION(strcat); // NOLINT
+ INTERCEPT_FUNCTION(strchr);
+ INTERCEPT_FUNCTION(strcmp);
+ INTERCEPT_FUNCTION(strcpy); // NOLINT
+ INTERCEPT_FUNCTION(strdup);
+ INTERCEPT_FUNCTION(strlen);
+ INTERCEPT_FUNCTION(strncasecmp);
+ INTERCEPT_FUNCTION(strncmp);
+ INTERCEPT_FUNCTION(strncpy);
+#ifndef __APPLE__
+ INTERCEPT_FUNCTION(strnlen);
+#endif
+ if (FLAG_v > 0) {
+ Printf("AddressSanitizer: libc interceptors initialized\n");
+ }
+}
+
+} // namespace __asan
+
+// ---------------------- Wrappers ---------------- {{{1
+using namespace __asan; // NOLINT
+
+static inline int CharCmp(unsigned char c1, unsigned char c2) {
+ return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
+}
+
+static inline int CharCaseCmp(unsigned char c1, unsigned char c2) {
+ int c1_low = tolower(c1);
+ int c2_low = tolower(c2);
+ return c1_low - c2_low;
+}
+
+int WRAP(memcmp)(const void *a1, const void *a2, size_t size) {
+ ENSURE_ASAN_INITED();
+ unsigned char c1 = 0, c2 = 0;
+ const unsigned char *s1 = (const unsigned char*)a1;
+ const unsigned char *s2 = (const unsigned char*)a2;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ c1 = s1[i];
+ c2 = s2[i];
+ if (c1 != c2) break;
+ }
+ ASAN_READ_RANGE(s1, Min(i + 1, size));
+ ASAN_READ_RANGE(s2, Min(i + 1, size));
+ return CharCmp(c1, c2);
+}
+
+void *WRAP(memcpy)(void *to, const void *from, size_t size) {
+ // memcpy is called during __asan_init() from the internals
+ // of printf(...).
+ if (asan_init_is_running) {
+ return real_memcpy(to, from, size);
+ }
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_intrin) {
+ CHECK_RANGES_OVERLAP("memcpy", to, size, from, size);
+ ASAN_WRITE_RANGE(from, size);
+ ASAN_READ_RANGE(to, size);
+ }
+ return real_memcpy(to, from, size);
+}
+
+void *WRAP(memmove)(void *to, const void *from, size_t size) {
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_intrin) {
+ ASAN_WRITE_RANGE(from, size);
+ ASAN_READ_RANGE(to, size);
+ }
+ return real_memmove(to, from, size);
+}
+
+void *WRAP(memset)(void *block, int c, size_t size) {
+ // memset is called inside INTERCEPT_FUNCTION on Mac.
+ if (asan_init_is_running) {
+ return real_memset(block, c, size);
+ }
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_intrin) {
+ ASAN_WRITE_RANGE(block, size);
+ }
+ return real_memset(block, c, size);
+}
+
+// Note that on Linux index and strchr are definined differently depending on
+// the compiler (gcc vs clang).
+// see __CORRECT_ISO_CPP_STRING_H_PROTO in /usr/include/string.h
+
+#ifndef __APPLE__
+char *WRAP(index)(const char *str, int c)
+ __attribute__((alias(WRAPPER_NAME(strchr))));
+#endif
+
+char *WRAP(strchr)(const char *str, int c) {
+ ENSURE_ASAN_INITED();
+ char *result = real_strchr(str, c);
+ if (FLAG_replace_str) {
+ size_t bytes_read = (result ? result - str : real_strlen(str)) + 1;
+ ASAN_READ_RANGE(str, bytes_read);
+ }
+ return result;
+}
+
+int WRAP(strcasecmp)(const char *s1, const char *s2) {
+ ENSURE_ASAN_INITED();
+ unsigned char c1, c2;
+ size_t i;
+ for (i = 0; ; i++) {
+ c1 = (unsigned char)s1[i];
+ c2 = (unsigned char)s2[i];
+ if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
+ }
+ ASAN_READ_RANGE(s1, i + 1);
+ ASAN_READ_RANGE(s2, i + 1);
+ return CharCaseCmp(c1, c2);
+}
+
+char *WRAP(strcat)(char *to, const char *from) { // NOLINT
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_str) {
+ size_t from_length = real_strlen(from);
+ ASAN_READ_RANGE(from, from_length + 1);
+ if (from_length > 0) {
+ size_t to_length = real_strlen(to);
+ ASAN_READ_RANGE(to, to_length);
+ ASAN_WRITE_RANGE(to + to_length, from_length + 1);
+ CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1);
+ }
+ }
+ return real_strcat(to, from);
+}
+
+int WRAP(strcmp)(const char *s1, const char *s2) {
+ // strcmp is called from malloc_default_purgeable_zone()
+ // in __asan::ReplaceSystemAlloc() on Mac.
+ if (asan_init_is_running) {
+ return real_strcmp(s1, s2);
+ }
+ unsigned char c1, c2;
+ size_t i;
+ for (i = 0; ; i++) {
+ c1 = (unsigned char)s1[i];
+ c2 = (unsigned char)s2[i];
+ if (c1 != c2 || c1 == '\0') break;
+ }
+ ASAN_READ_RANGE(s1, i + 1);
+ ASAN_READ_RANGE(s2, i + 1);
+ return CharCmp(c1, c2);
+}
+
+char *WRAP(strcpy)(char *to, const char *from) { // NOLINT
+ // strcpy is called from malloc_default_purgeable_zone()
+ // in __asan::ReplaceSystemAlloc() on Mac.
+ if (asan_init_is_running) {
+ return real_strcpy(to, from);
+ }
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_str) {
+ size_t from_size = real_strlen(from) + 1;
+ CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size);
+ ASAN_READ_RANGE(from, from_size);
+ ASAN_WRITE_RANGE(to, from_size);
+ }
+ return real_strcpy(to, from);
+}
+
+char *WRAP(strdup)(const char *s) {
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_str) {
+ size_t length = real_strlen(s);
+ ASAN_READ_RANGE(s, length + 1);
+ }
+ return real_strdup(s);
+}
+
+size_t WRAP(strlen)(const char *s) {
+ // strlen is called from malloc_default_purgeable_zone()
+ // in __asan::ReplaceSystemAlloc() on Mac.
+ if (asan_init_is_running) {
+ return real_strlen(s);
+ }
+ ENSURE_ASAN_INITED();
+ size_t length = real_strlen(s);
+ if (FLAG_replace_str) {
+ ASAN_READ_RANGE(s, length + 1);
+ }
+ return length;
+}
+
+int WRAP(strncasecmp)(const char *s1, const char *s2, size_t size) {
+ ENSURE_ASAN_INITED();
+ unsigned char c1 = 0, c2 = 0;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ c1 = (unsigned char)s1[i];
+ c2 = (unsigned char)s2[i];
+ if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
+ }
+ ASAN_READ_RANGE(s1, Min(i + 1, size));
+ ASAN_READ_RANGE(s2, Min(i + 1, size));
+ return CharCaseCmp(c1, c2);
+}
+
+int WRAP(strncmp)(const char *s1, const char *s2, size_t size) {
+ // strncmp is called from malloc_default_purgeable_zone()
+ // in __asan::ReplaceSystemAlloc() on Mac.
+ if (asan_init_is_running) {
+ return real_strncmp(s1, s2, size);
+ }
+ unsigned char c1 = 0, c2 = 0;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ c1 = (unsigned char)s1[i];
+ c2 = (unsigned char)s2[i];
+ if (c1 != c2 || c1 == '\0') break;
+ }
+ ASAN_READ_RANGE(s1, Min(i + 1, size));
+ ASAN_READ_RANGE(s2, Min(i + 1, size));
+ return CharCmp(c1, c2);
+}
+
+char *WRAP(strncpy)(char *to, const char *from, size_t size) {
+ ENSURE_ASAN_INITED();
+ if (FLAG_replace_str) {
+ size_t from_size = Min(size, internal_strnlen(from, size) + 1);
+ CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size);
+ ASAN_READ_RANGE(from, from_size);
+ ASAN_WRITE_RANGE(to, size);
+ }
+ return real_strncpy(to, from, size);
+}
+
+#ifndef __APPLE__
+size_t WRAP(strnlen)(const char *s, size_t maxlen) {
+ ENSURE_ASAN_INITED();
+ size_t length = real_strnlen(s, maxlen);
+ if (FLAG_replace_str) {
+ ASAN_READ_RANGE(s, Min(length + 1, maxlen));
+ }
+ return length;
+}
+#endif
diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h
new file mode 100644
index 0000000..07b9420
--- /dev/null
+++ b/lib/asan/asan_interceptors.h
@@ -0,0 +1,134 @@
+//===-- asan_interceptors.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_interceptors.cc
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INTERCEPTORS_H
+#define ASAN_INTERCEPTORS_H
+
+#include "asan_internal.h"
+
+// To replace weak system functions on Linux we just need to declare functions
+// with same names in our library and then obtain the real function pointers
+// using dlsym(). This is not so on Mac OS, where the two-level namespace makes
+// our replacement functions invisible to other libraries. This may be overcomed
+// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
+// libraries in Chromium were noticed when doing so.
+// Instead we use mach_override, a handy framework for patching functions at
+// runtime. To avoid possible name clashes, our replacement functions have
+// the "wrap_" prefix on Mac.
+//
+// After interception, the calls to system functions will be substituted by
+// calls to our interceptors. We store pointers to system function f()
+// in __asan::real_f().
+//
+// TODO(glider): mach_override_ptr() tends to spend too much time
+// in allocateBranchIsland(). This should be ok for real-word
+// application, but slows down our tests which fork too many children.
+#ifdef __APPLE__
+#include "mach_override/mach_override.h"
+#define WRAP(x) wrap_##x
+#define WRAPPER_NAME(x) "wrap_"#x
+
+#define OVERRIDE_FUNCTION(oldfunc, newfunc) \
+ CHECK(0 == __asan_mach_override_ptr((void*)(oldfunc), \
+ (void*)(newfunc), \
+ (void**)&real_##oldfunc)); \
+ CHECK(real_##oldfunc != NULL);
+
+#define OVERRIDE_FUNCTION_IF_EXISTS(oldfunc, newfunc) \
+ do { __asan_mach_override_ptr((void*)(oldfunc), \
+ (void*)(newfunc), \
+ (void**)&real_##oldfunc); } while (0)
+
+#define INTERCEPT_FUNCTION(func) \
+ OVERRIDE_FUNCTION(func, WRAP(func))
+
+#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
+ OVERRIDE_FUNCTION_IF_EXISTS(func, WRAP(func))
+
+#else // __linux__
+#define WRAP(x) x
+#define WRAPPER_NAME(x) #x
+
+#define INTERCEPT_FUNCTION(func) \
+ CHECK((real_##func = (func##_f)dlsym(RTLD_NEXT, #func)));
+
+#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
+ do { real_##func = (func##_f)dlsym(RTLD_NEXT, #func); } while (0)
+#endif
+
+#ifdef __APPLE__
+int WRAP(memcmp)(const void *a1, const void *a2, size_t size);
+void *WRAP(memcpy)(void *to, const void *from, size_t size);
+void *WRAP(memmove)(void *to, const void *from, size_t size);
+void *WRAP(memset)(void *block, int c, size_t size);
+int WRAP(strcasecmp)(const char *s1, const char *s2);
+char *WRAP(strcat)(char *to, const char *from); // NOLINT
+char *WRAP(strchr)(const char *string, int c);
+int WRAP(strcmp)(const char *s1, const char *s2);
+char *WRAP(strcpy)(char *to, const char *from); // NOLINT
+char *WRAP(strdup)(const char *s);
+size_t WRAP(strlen)(const char *s);
+int WRAP(strncasecmp)(const char *s1, const char *s2, size_t n);
+int WRAP(strncmp)(const char *s1, const char *s2, size_t size);
+char *WRAP(strncpy)(char *to, const char *from, size_t size);
+#endif
+
+namespace __asan {
+
+typedef void* (*index_f)(const char *string, int c);
+typedef int (*memcmp_f)(const void *a1, const void *a2, size_t size);
+typedef void* (*memcpy_f)(void *to, const void *from, size_t size);
+typedef void* (*memmove_f)(void *to, const void *from, size_t size);
+typedef void* (*memset_f)(void *block, int c, size_t size);
+typedef int (*strcasecmp_f)(const char *s1, const char *s2);
+typedef char* (*strcat_f)(char *to, const char *from);
+typedef char* (*strchr_f)(const char *str, int c);
+typedef int (*strcmp_f)(const char *s1, const char *s2);
+typedef char* (*strcpy_f)(char *to, const char *from);
+typedef char* (*strdup_f)(const char *s);
+typedef size_t (*strlen_f)(const char *s);
+typedef int (*strncasecmp_f)(const char *s1, const char *s2, size_t n);
+typedef int (*strncmp_f)(const char *s1, const char *s2, size_t size);
+typedef char* (*strncpy_f)(char *to, const char *from, size_t size);
+typedef size_t (*strnlen_f)(const char *s, size_t maxlen);
+
+// __asan::real_X() holds pointer to library implementation of X().
+extern index_f real_index;
+extern memcmp_f real_memcmp;
+extern memcpy_f real_memcpy;
+extern memmove_f real_memmove;
+extern memset_f real_memset;
+extern strcasecmp_f real_strcasecmp;
+extern strcat_f real_strcat;
+extern strchr_f real_strchr;
+extern strcmp_f real_strcmp;
+extern strcpy_f real_strcpy;
+extern strdup_f real_strdup;
+extern strlen_f real_strlen;
+extern strncasecmp_f real_strncasecmp;
+extern strncmp_f real_strncmp;
+extern strncpy_f real_strncpy;
+extern strnlen_f real_strnlen;
+
+// __asan::internal_X() is the implementation of X() for use in RTL.
+size_t internal_strlen(const char *s);
+size_t internal_strnlen(const char *s, size_t maxlen);
+void* internal_memchr(const void* s, int c, size_t n);
+int internal_memcmp(const void* s1, const void* s2, size_t n);
+
+// Initializes pointers to str*/mem* functions.
+void InitializeAsanInterceptors();
+
+} // namespace __asan
+
+#endif // ASAN_INTERCEPTORS_H
diff --git a/lib/asan/asan_interface.h b/lib/asan/asan_interface.h
new file mode 100644
index 0000000..7506586
--- /dev/null
+++ b/lib/asan/asan_interface.h
@@ -0,0 +1,135 @@
+//===-- asan_interface.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This header can be included by the instrumented program to fetch
+// data (mostly allocator statistics) from ASan runtime library.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INTERFACE_H
+#define ASAN_INTERFACE_H
+
+#include <stdint.h> // for __WORDSIZE
+#include <stdlib.h> // for size_t
+
+// This header should NOT include any other headers from ASan runtime.
+// All functions in this header are extern "C" and start with __asan_.
+
+extern "C" {
+ // This function should be called at the very beginning of the process,
+ // before any instrumented code is executed and before any call to malloc.
+ void __asan_init()
+ __attribute__((visibility("default")));
+
+ // This function should be called by the instrumented code.
+ // 'addr' is the address of a global variable called 'name' of 'size' bytes.
+ void __asan_register_global(uintptr_t addr, size_t size, const char *name)
+ __attribute__((visibility("default")));
+
+ // This structure describes an instrumented global variable.
+ struct __asan_global {
+ size_t beg; // The address of the global.
+ size_t size; // The original size of the global.
+ size_t size_with_redzone; // The size with the redzone.
+ const char *name; // Name as a C string.
+ };
+
+ // These two functions should be called by the instrumented code.
+ // 'globals' is an array of structures describing 'n' globals.
+ void __asan_register_globals(__asan_global *globals, size_t n)
+ __attribute__((visibility("default")));
+ void __asan_unregister_globals(__asan_global *globals, size_t n)
+ __attribute__((visibility("default")));
+
+ // These two functions are used by the instrumented code in the
+ // use-after-return mode. __asan_stack_malloc allocates size bytes of
+ // fake stack and __asan_stack_free poisons it. real_stack is a pointer to
+ // the real stack region.
+ size_t __asan_stack_malloc(size_t size, size_t real_stack)
+ __attribute__((visibility("default")));
+ void __asan_stack_free(size_t ptr, size_t size, size_t real_stack)
+ __attribute__((visibility("default")));
+
+ // Marks memory region [addr, addr+size) as unaddressable.
+ // This memory must be previously allocated by the user program. Accessing
+ // addresses in this region from instrumented code is forbidden until
+ // this region is unpoisoned. This function is not guaranteed to poison
+ // the whole region - it may poison only subregion of [addr, addr+size) due
+ // to ASan alignment restrictions.
+ // Method is NOT thread-safe in the sense that no two threads can
+ // (un)poison memory in the same memory region simultaneously.
+ void __asan_poison_memory_region(void const volatile *addr, size_t size);
+ // Marks memory region [addr, addr+size) as addressable.
+ // This memory must be previously allocated by the user program. Accessing
+ // addresses in this region is allowed until this region is poisoned again.
+ // This function may unpoison a superregion of [addr, addr+size) due to
+ // ASan alignment restrictions.
+ // Method is NOT thread-safe in the sense that no two threads can
+ // (un)poison memory in the same memory region simultaneously.
+ void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+
+// User code should use macro instead of functions.
+#if defined(__has_feature) && __has_feature(address_sanitizer)
+#define ASAN_POISON_MEMORY_REGION(addr, size) \
+ __asan_poison_memory_region((addr), (size))
+#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
+ __asan_unpoison_memory_region((addr), (size))
+#else
+#define ASAN_POISON_MEMORY_REGION(addr, size) \
+ ((void)(addr), (void)(size))
+#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
+ ((void)(addr), (void)(size))
+#endif
+
+ // Returns true iff addr is poisoned (i.e. 1-byte read/write access to this
+ // address will result in error report from AddressSanitizer).
+ bool __asan_address_is_poisoned(void const volatile *addr);
+
+ // This is an internal function that is called to report an error.
+ // However it is still a part of the interface because users may want to
+ // set a breakpoint on this function in a debugger.
+ void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp,
+ uintptr_t addr, bool is_write, size_t access_size)
+ __attribute__((visibility("default")));
+
+ // Sets the exit code to use when reporting an error.
+ // Returns the old value.
+ int __asan_set_error_exit_code(int exit_code);
+
+ // Returns the estimated number of bytes that will be reserved by allocator
+ // for request of "size" bytes. If ASan allocator can't allocate that much
+ // memory, returns the maximal possible allocation size, otherwise returns
+ // "size".
+ size_t __asan_get_estimated_allocated_size(size_t size);
+ // Returns true if p is NULL or if p was returned by the ASan allocator and
+ // is not yet freed.
+ bool __asan_get_ownership(const void *p);
+ // Returns the number of bytes reserved for the pointer p.
+ // Requires (get_ownership(p) == true).
+ size_t __asan_get_allocated_size(const void *p);
+ // Number of bytes, allocated and not yet freed by the application.
+ size_t __asan_get_current_allocated_bytes();
+ // Number of bytes, mmaped by asan allocator to fulfill allocation requests.
+ // Generally, for request of X bytes, allocator can reserve and add to free
+ // lists a large number of chunks of size X to use them for future requests.
+ // All these chunks count toward the heap size. Currently, allocator never
+ // releases memory to OS (instead, it just puts freed chunks to free lists).
+ size_t __asan_get_heap_size();
+ // Number of bytes, mmaped by asan allocator, which can be used to fulfill
+ // allocation requests. When a user program frees memory chunk, it can first
+ // fall into quarantine and will count toward __asan_get_free_bytes() later.
+ size_t __asan_get_free_bytes();
+ // Number of bytes in unmapped pages, that are released to OS. Currently,
+ // always returns 0.
+ size_t __asan_get_unmapped_bytes();
+ // Prints accumulated stats to stderr. Used for debugging.
+ void __asan_print_accumulated_stats();
+} // namespace
+
+#endif // ASAN_INTERFACE_H
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
new file mode 100644
index 0000000..d0790a7
--- /dev/null
+++ b/lib/asan/asan_internal.h
@@ -0,0 +1,239 @@
+//===-- asan_internal.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header which defines various general utilities.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INTERNAL_H
+#define ASAN_INTERNAL_H
+
+#if !defined(__linux__) && !defined(__APPLE__)
+# error "This operating system is not supported by AddressSanitizer"
+#endif
+
+#include <stdint.h> // for __WORDSIZE
+#include <stdlib.h> // for size_t
+#include <unistd.h> // for _exit
+
+// If __WORDSIZE was undefined by the platform, define it in terms of the
+// compiler built-in __LP64__.
+#ifndef __WORDSIZE
+#if __LP64__
+#define __WORDSIZE 64
+#else
+#define __WORDSIZE 32
+#endif
+#endif
+
+#ifdef ANDROID
+#include <sys/atomics.h>
+#endif
+
+#if defined(__has_feature) && __has_feature(address_sanitizer)
+# error "The AddressSanitizer run-time should not be"
+ " instrumented by AddressSanitizer"
+#endif
+
+// Build-time configuration options.
+
+// If set, sysinfo/sysinfo.h will be used to iterate over /proc/maps.
+#ifndef ASAN_USE_SYSINFO
+# define ASAN_USE_SYSINFO 1
+#endif
+
+// If set, asan will install its own SEGV signal handler.
+#ifndef ASAN_NEEDS_SEGV
+# define ASAN_NEEDS_SEGV 1
+#endif
+
+// If set, asan will intercept C++ exception api call(s).
+#ifndef ASAN_HAS_EXCEPTIONS
+# define ASAN_HAS_EXCEPTIONS 1
+#endif
+
+// If set, asan uses the values of SHADOW_SCALE and SHADOW_OFFSET
+// provided by the instrumented objects. Otherwise constants are used.
+#ifndef ASAN_FLEXIBLE_MAPPING_AND_OFFSET
+# define ASAN_FLEXIBLE_MAPPING_AND_OFFSET 0
+#endif
+
+// All internal functions in asan reside inside the __asan namespace
+// to avoid namespace collisions with the user programs.
+// Seperate namespace also makes it simpler to distinguish the asan run-time
+// functions from the instrumented user code in a profile.
+namespace __asan {
+
+class AsanThread;
+struct AsanStackTrace;
+
+// asan_rtl.cc
+void CheckFailed(const char *cond, const char *file, int line);
+void ShowStatsAndAbort();
+
+// asan_globals.cc
+bool DescribeAddrIfGlobal(uintptr_t addr);
+
+// asan_malloc_linux.cc / asan_malloc_mac.cc
+void ReplaceSystemMalloc();
+
+void OutOfMemoryMessageAndDie(const char *mem_type, size_t size);
+
+// asan_linux.cc / asan_mac.cc
+void *AsanDoesNotSupportStaticLinkage();
+int AsanOpenReadonly(const char* filename);
+
+void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size);
+void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size);
+void *AsanMprotect(uintptr_t fixed_addr, size_t size);
+void *AsanMmapSomewhereOrDie(size_t size, const char *where);
+void AsanUnmapOrDie(void *ptr, size_t size);
+
+ssize_t AsanRead(int fd, void *buf, size_t count);
+ssize_t AsanWrite(int fd, const void *buf, size_t count);
+int AsanClose(int fd);
+
+// asan_printf.cc
+void RawWrite(const char *buffer);
+int SNPrint(char *buffer, size_t length, const char *format, ...);
+void Printf(const char *format, ...);
+void Report(const char *format, ...);
+
+// Don't use std::min and std::max, to minimize dependency on libstdc++.
+template<class T> T Min(T a, T b) { return a < b ? a : b; }
+template<class T> T Max(T a, T b) { return a > b ? a : b; }
+
+// asan_poisoning.cc
+// Poisons the shadow memory for "size" bytes starting from "addr".
+void PoisonShadow(uintptr_t addr, size_t size, uint8_t value);
+// Poisons the shadow memory for "redzone_size" bytes starting from
+// "addr + size".
+void PoisonShadowPartialRightRedzone(uintptr_t addr,
+ uintptr_t size,
+ uintptr_t redzone_size,
+ uint8_t value);
+
+extern size_t FLAG_quarantine_size;
+extern int FLAG_demangle;
+extern bool FLAG_symbolize;
+extern int FLAG_v;
+extern size_t FLAG_redzone;
+extern int FLAG_debug;
+extern bool FLAG_poison_shadow;
+extern int FLAG_report_globals;
+extern size_t FLAG_malloc_context_size;
+extern bool FLAG_replace_str;
+extern bool FLAG_replace_intrin;
+extern bool FLAG_replace_cfallocator;
+extern bool FLAG_fast_unwind;
+extern bool FLAG_use_fake_stack;
+extern size_t FLAG_max_malloc_fill_size;
+extern int FLAG_exitcode;
+extern bool FLAG_allow_user_poisoning;
+
+extern int asan_inited;
+// Used to avoid infinite recursion in __asan_init().
+extern bool asan_init_is_running;
+
+enum LinkerInitialized { LINKER_INITIALIZED = 0 };
+
+#ifndef ASAN_DIE
+#define ASAN_DIE _exit(FLAG_exitcode)
+#endif // ASAN_DIE
+
+#define CHECK(cond) do { if (!(cond)) { \
+ CheckFailed(#cond, __FILE__, __LINE__); \
+}}while(0)
+
+#define RAW_CHECK_MSG(expr, msg) do { \
+ if (!(expr)) { \
+ RawWrite(msg); \
+ ASAN_DIE; \
+ } \
+} while (0)
+
+#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr)
+
+#define UNIMPLEMENTED() CHECK("unimplemented" && 0)
+
+#define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+const size_t kWordSize = __WORDSIZE / 8;
+const size_t kWordSizeInBits = 8 * kWordSize;
+const size_t kPageSizeBits = 12;
+const size_t kPageSize = 1UL << kPageSizeBits;
+
+#define GET_CALLER_PC() (uintptr_t)__builtin_return_address(0)
+#define GET_CURRENT_FRAME() (uintptr_t)__builtin_frame_address(0)
+
+#define GET_BP_PC_SP \
+ uintptr_t bp = GET_CURRENT_FRAME(); \
+ uintptr_t pc = GET_CALLER_PC(); \
+ uintptr_t local_stack; \
+ uintptr_t sp = (uintptr_t)&local_stack;
+
+// These magic values are written to shadow for better error reporting.
+const int kAsanHeapLeftRedzoneMagic = 0xfa;
+const int kAsanHeapRightRedzoneMagic = 0xfb;
+const int kAsanHeapFreeMagic = 0xfd;
+const int kAsanStackLeftRedzoneMagic = 0xf1;
+const int kAsanStackMidRedzoneMagic = 0xf2;
+const int kAsanStackRightRedzoneMagic = 0xf3;
+const int kAsanStackPartialRedzoneMagic = 0xf4;
+const int kAsanStackAfterReturnMagic = 0xf5;
+const int kAsanUserPoisonedMemoryMagic = 0xf7;
+const int kAsanGlobalRedzoneMagic = 0xf9;
+const int kAsanInternalHeapMagic = 0xfe;
+
+static const uintptr_t kCurrentStackFrameMagic = 0x41B58AB3;
+static const uintptr_t kRetiredStackFrameMagic = 0x45E0360E;
+
+// --------------------------- Bit twiddling ------- {{{1
+inline bool IsPowerOfTwo(size_t x) {
+ return (x & (x - 1)) == 0;
+}
+
+inline size_t RoundUpTo(size_t size, size_t boundary) {
+ CHECK(IsPowerOfTwo(boundary));
+ return (size + boundary - 1) & ~(boundary - 1);
+}
+
+// -------------------------- LowLevelAllocator ----- {{{1
+// A simple low-level memory allocator for internal use.
+class LowLevelAllocator {
+ public:
+ explicit LowLevelAllocator(LinkerInitialized) {}
+ // 'size' must be a power of two.
+ // Requires an external lock.
+ void *Allocate(size_t size);
+ private:
+ char *allocated_end_;
+ char *allocated_current_;
+};
+
+// -------------------------- Atomic ---------------- {{{1
+static inline int AtomicInc(int *a) {
+#ifdef ANDROID
+ return __atomic_inc(a) + 1;
+#else
+ return __sync_add_and_fetch(a, 1);
+#endif
+}
+
+static inline int AtomicDec(int *a) {
+#ifdef ANDROID
+ return __atomic_dec(a) - 1;
+#else
+ return __sync_add_and_fetch(a, -1);
+#endif
+}
+
+} // namespace __asan
+
+#endif // ASAN_INTERNAL_H
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
new file mode 100644
index 0000000..26a08ae
--- /dev/null
+++ b/lib/asan/asan_linux.cc
@@ -0,0 +1,101 @@
+//===-- asan_linux.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Linux-specific details.
+//===----------------------------------------------------------------------===//
+#ifdef __linux__
+
+#include "asan_internal.h"
+
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+extern char _DYNAMIC[];
+
+namespace __asan {
+
+void *AsanDoesNotSupportStaticLinkage() {
+ // This will fail to link with -static.
+ return &_DYNAMIC;
+}
+
+static void *asan_mmap(void *addr, size_t length, int prot, int flags,
+ int fd, uint64_t offset) {
+# if __WORDSIZE == 64
+ return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
+# else
+ return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
+# endif
+}
+
+void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) {
+ size = RoundUpTo(size, kPageSize);
+ void *res = asan_mmap(0, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (res == (void*)-1) {
+ OutOfMemoryMessageAndDie(mem_type, size);
+ }
+ return res;
+}
+
+void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) {
+ return asan_mmap((void*)fixed_addr, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
+ 0, 0);
+}
+
+void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) {
+ return asan_mmap((void*)fixed_addr, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ 0, 0);
+}
+
+void *AsanMprotect(uintptr_t fixed_addr, size_t size) {
+ return asan_mmap((void*)fixed_addr, size,
+ PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
+ 0, 0);
+}
+
+void AsanUnmapOrDie(void *addr, size_t size) {
+ if (!addr || !size) return;
+ int res = syscall(__NR_munmap, addr, size);
+ if (res != 0) {
+ Report("Failed to unmap\n");
+ ASAN_DIE;
+ }
+}
+
+ssize_t AsanWrite(int fd, const void *buf, size_t count) {
+ return (ssize_t)syscall(__NR_write, fd, buf, count);
+}
+
+int AsanOpenReadonly(const char* filename) {
+ return open(filename, O_RDONLY);
+}
+
+ssize_t AsanRead(int fd, void *buf, size_t count) {
+ return (ssize_t)syscall(__NR_read, fd, buf, count);
+}
+
+int AsanClose(int fd) {
+ return close(fd);
+}
+
+} // namespace __asan
+
+#endif // __linux__
diff --git a/lib/asan/asan_lock.h b/lib/asan/asan_lock.h
new file mode 100644
index 0000000..030fae6
--- /dev/null
+++ b/lib/asan/asan_lock.h
@@ -0,0 +1,100 @@
+//===-- asan_lock.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// A wrapper for a simple lock.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_LOCK_H
+#define ASAN_LOCK_H
+
+#include "asan_internal.h"
+
+// The locks in ASan are global objects and they are never destroyed to avoid
+// at-exit races (that is, a lock is being used by other threads while the main
+// thread is doing atexit destructors).
+
+#ifdef __APPLE__
+#include <pthread.h>
+
+#include <libkern/OSAtomic.h>
+namespace __asan {
+class AsanLock {
+ public:
+ explicit AsanLock(LinkerInitialized) :
+ mu_(OS_SPINLOCK_INIT),
+ owner_(0),
+ is_locked_(false) {}
+
+ void Lock() {
+ CHECK(owner_ != pthread_self());
+ OSSpinLockLock(&mu_);
+ is_locked_ = true;
+ owner_ = pthread_self();
+ }
+ void Unlock() {
+ owner_ = 0;
+ is_locked_ = false;
+ OSSpinLockUnlock(&mu_);
+ }
+
+ bool IsLocked() {
+ // This is not atomic, e.g. one thread may get different values if another
+ // one is about to release the lock.
+ return is_locked_;
+ }
+ private:
+ OSSpinLock mu_;
+ volatile pthread_t owner_; // for debugging purposes
+ bool is_locked_; // for silly malloc_introspection_t interface
+};
+} // namespace __asan
+
+#else // assume linux
+#include <pthread.h>
+namespace __asan {
+class AsanLock {
+ public:
+ explicit AsanLock(LinkerInitialized) {
+ // We assume that pthread_mutex_t initialized to all zeroes is a valid
+ // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
+ // a gcc warning:
+ // extended initializer lists only available with -std=c++0x or -std=gnu++0x
+ }
+ void Lock() {
+ pthread_mutex_lock(&mu_);
+ // pthread_spin_lock(&mu_);
+ }
+ void Unlock() {
+ pthread_mutex_unlock(&mu_);
+ // pthread_spin_unlock(&mu_);
+ }
+ private:
+ pthread_mutex_t mu_;
+ // pthread_spinlock_t mu_;
+};
+} // namespace __asan
+#endif
+
+namespace __asan {
+class ScopedLock {
+ public:
+ explicit ScopedLock(AsanLock *mu) : mu_(mu) {
+ mu_->Lock();
+ }
+ ~ScopedLock() {
+ mu_->Unlock();
+ }
+ private:
+ AsanLock *mu_;
+};
+
+} // namespace __asan
+
+#endif // ASAN_LOCK_H
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
new file mode 100644
index 0000000..b202e63
--- /dev/null
+++ b/lib/asan/asan_mac.cc
@@ -0,0 +1,311 @@
+//===-- asan_mac.cc -------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Mac-specific details.
+//===----------------------------------------------------------------------===//
+
+#ifdef __APPLE__
+
+#include "asan_mac.h"
+
+#include "asan_internal.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <new>
+
+namespace __asan {
+
+extern dispatch_async_f_f real_dispatch_async_f;
+extern dispatch_sync_f_f real_dispatch_sync_f;
+extern dispatch_after_f_f real_dispatch_after_f;
+extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
+extern dispatch_group_async_f_f real_dispatch_group_async_f;
+extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
+
+// No-op. Mac does not support static linkage anyway.
+void *AsanDoesNotSupportStaticLinkage() {
+ return NULL;
+}
+
+static void *asan_mmap(void *addr, size_t length, int prot, int flags,
+ int fd, uint64_t offset) {
+ return mmap(addr, length, prot, flags, fd, offset);
+}
+
+ssize_t AsanWrite(int fd, const void *buf, size_t count) {
+ return write(fd, buf, count);
+}
+
+void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) {
+ size = RoundUpTo(size, kPageSize);
+ void *res = asan_mmap(0, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (res == (void*)-1) {
+ OutOfMemoryMessageAndDie(mem_type, size);
+ }
+ return res;
+}
+
+void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) {
+ return asan_mmap((void*)fixed_addr, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
+ 0, 0);
+}
+
+void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) {
+ return asan_mmap((void*)fixed_addr, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ 0, 0);
+}
+
+void *AsanMprotect(uintptr_t fixed_addr, size_t size) {
+ return asan_mmap((void*)fixed_addr, size,
+ PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
+ 0, 0);
+}
+
+void AsanUnmapOrDie(void *addr, size_t size) {
+ if (!addr || !size) return;
+ int res = munmap(addr, size);
+ if (res != 0) {
+ Report("Failed to unmap\n");
+ ASAN_DIE;
+ }
+}
+
+int AsanOpenReadonly(const char* filename) {
+ return open(filename, O_RDONLY);
+}
+
+ssize_t AsanRead(int fd, void *buf, size_t count) {
+ return read(fd, buf, count);
+}
+
+int AsanClose(int fd) {
+ return close(fd);
+}
+
+// Support for the following functions from libdispatch on Mac OS:
+// dispatch_async_f()
+// dispatch_async()
+// dispatch_sync_f()
+// dispatch_sync()
+// dispatch_after_f()
+// dispatch_after()
+// dispatch_group_async_f()
+// dispatch_group_async()
+// TODO(glider): libdispatch API contains other functions that we don't support
+// yet.
+//
+// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
+// they can cause jobs to run on a thread different from the current one.
+// TODO(glider): if so, we need a test for this (otherwise we should remove
+// them).
+//
+// The following functions use dispatch_barrier_async_f() (which isn't a library
+// function but is exported) and are thus supported:
+// dispatch_source_set_cancel_handler_f()
+// dispatch_source_set_cancel_handler()
+// dispatch_source_set_event_handler_f()
+// dispatch_source_set_event_handler()
+//
+// The reference manual for Grand Central Dispatch is available at
+// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
+// The implementation details are at
+// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
+
+extern "C"
+void asan_dispatch_call_block_and_release(void *block) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *context = (asan_block_context_t*)block;
+ if (FLAG_v >= 2) {
+ Report("asan_dispatch_call_block_and_release(): "
+ "context: %p, pthread_self: %p\n",
+ block, pthread_self());
+ }
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (t) {
+ // We've already executed a job on this worker thread. Let's reuse the
+ // AsanThread object.
+ if (t != asanThreadRegistry().GetMain()) {
+ // Flush the statistics and update the current thread's tid.
+ asanThreadRegistry().UnregisterThread(t);
+ asanThreadRegistry().RegisterThread(t, context->parent_tid, &stack);
+ }
+ // Otherwise the worker is being executed on the main thread -- we are
+ // draining the dispatch queue.
+ // TODO(glider): any checks for that?
+ } else {
+ // It's incorrect to assert that the current thread is not dying: at least
+ // the callbacks from dispatch_sync() are sometimes called after the TSD is
+ // destroyed.
+ t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack);
+ new(t) AsanThread(context->parent_tid,
+ /*start_routine*/NULL, /*arg*/NULL, &stack);
+ t->Init();
+ asanThreadRegistry().SetCurrent(t);
+ }
+ // Call the original dispatcher for the block.
+ context->func(context->block);
+ asan_free(context, &stack);
+}
+
+} // namespace __asan
+
+using namespace __asan; // NOLINT
+
+// Wrap |ctxt| and |func| into an asan_block_context_t.
+// The caller retains control of the allocated context.
+extern "C"
+asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
+ AsanStackTrace *stack) {
+ asan_block_context_t *asan_ctxt =
+ (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
+ asan_ctxt->block = ctxt;
+ asan_ctxt->func = func;
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ if (FLAG_debug) {
+ // Sometimes at Chromium teardown this assertion is violated:
+ // -- a task is created via dispatch_async() on the "CFMachPort"
+ // thread while doing _dispatch_queue_drain();
+ // -- a task is created via dispatch_async_f() on the
+ // "com.apple.root.default-overcommit-priority" thread while doing
+ // _dispatch_dispose().
+ // TODO(glider): find out what's going on.
+ CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
+ }
+ asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
+ return asan_ctxt;
+}
+
+// TODO(glider): can we reduce code duplication by introducing a macro?
+extern "C"
+int WRAP(dispatch_async_f)(dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (FLAG_v >= 2) {
+ Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
+ asan_ctxt, pthread_self());
+ PRINT_CURRENT_STACK();
+ }
+ return real_dispatch_async_f(dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+extern "C"
+int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (FLAG_v >= 2) {
+ Report("dispatch_sync_f(): context: %p, pthread_self: %p\n",
+ asan_ctxt, pthread_self());
+ PRINT_CURRENT_STACK();
+ }
+ return real_dispatch_sync_f(dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+extern "C"
+int WRAP(dispatch_after_f)(dispatch_time_t when,
+ dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (FLAG_v >= 2) {
+ Report("dispatch_after_f: %p\n", asan_ctxt);
+ PRINT_CURRENT_STACK();
+ }
+ return real_dispatch_after_f(when, dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+extern "C"
+void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (FLAG_v >= 2) {
+ Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n",
+ asan_ctxt, pthread_self());
+ PRINT_CURRENT_STACK();
+ }
+ real_dispatch_barrier_async_f(dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+extern "C"
+void WRAP(dispatch_group_async_f)(dispatch_group_t group,
+ dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (FLAG_v >= 2) {
+ Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
+ asan_ctxt, pthread_self());
+ PRINT_CURRENT_STACK();
+ }
+ real_dispatch_group_async_f(group, dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+// The following stuff has been extremely helpful while looking for the
+// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
+// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
+// find the points of worker thread creation (each of such threads may be used
+// to run several tasks, that's why this is not enough to support the whole
+// libdispatch API.
+extern "C"
+void *wrap_workitem_func(void *arg) {
+ if (FLAG_v >= 2) {
+ Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
+ }
+ asan_block_context_t *ctxt = (asan_block_context_t*)arg;
+ worker_t fn = (worker_t)(ctxt->func);
+ void *result = fn(ctxt->block);
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_free(arg, &stack);
+ return result;
+}
+
+extern "C"
+int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
+ void *(*workitem_func)(void *), void * workitem_arg,
+ pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ asan_block_context_t *asan_ctxt =
+ (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
+ asan_ctxt->block = workitem_arg;
+ asan_ctxt->func = (dispatch_function_t)workitem_func;
+ asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
+ if (FLAG_v >= 2) {
+ Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
+ PRINT_CURRENT_STACK();
+ }
+ return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt,
+ itemhandlep, gencountp);
+}
+
+#endif // __APPLE__
diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h
new file mode 100644
index 0000000..32739e7
--- /dev/null
+++ b/lib/asan/asan_mac.h
@@ -0,0 +1,87 @@
+//===-- asan_mac.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_mac.cc
+//===----------------------------------------------------------------------===//
+#ifdef __APPLE__
+
+#ifndef ASAN_MAC_H
+#define ASAN_MAC_H
+
+#include "asan_interceptors.h"
+
+// TODO(glider): need to check if the OS X version is 10.6 or greater.
+#include <dispatch/dispatch.h>
+#include <setjmp.h>
+
+typedef void* pthread_workqueue_t;
+typedef void* pthread_workitem_handle_t;
+
+typedef void (*dispatch_function_t)(void *block);
+typedef void* (*worker_t)(void *block);
+typedef int (*dispatch_async_f_f)(dispatch_queue_t dq, void *ctxt,
+ dispatch_function_t func);
+typedef int (*dispatch_sync_f_f)(dispatch_queue_t dq, void *ctxt,
+ dispatch_function_t func);
+typedef int (*dispatch_after_f_f)(dispatch_time_t when,
+ dispatch_queue_t dq, void *ctxt,
+ dispatch_function_t func);
+typedef void (*dispatch_barrier_async_f_f)(dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func);
+typedef void (*dispatch_group_async_f_f)(dispatch_group_t group,
+ dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+typedef int (*pthread_workqueue_additem_np_f)(pthread_workqueue_t workq,
+ void *(*workitem_func)(void *), void * workitem_arg,
+ pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
+
+
+// A wrapper for the ObjC blocks used to support libdispatch.
+typedef struct {
+ void *block;
+ dispatch_function_t func;
+ int parent_tid;
+} asan_block_context_t;
+
+
+extern "C" {
+// dispatch_barrier_async_f() is not declared in <dispatch/dispatch.h>.
+void dispatch_barrier_async_f(dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+// Neither is pthread_workqueue_additem_np().
+int pthread_workqueue_additem_np(pthread_workqueue_t workq,
+ void *(*workitem_func)(void *), void * workitem_arg,
+ pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
+
+int WRAP(dispatch_async_f)(dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func);
+int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func);
+int WRAP(dispatch_after_f)(dispatch_time_t when,
+ dispatch_queue_t dq,
+ void *ctxt,
+ dispatch_function_t func);
+void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+void WRAP(dispatch_group_async_f)(dispatch_group_t group,
+ dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
+ void *(*workitem_func)(void *), void * workitem_arg,
+ pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
+}
+
+#endif // ASAN_MAC_H
+
+#endif // __APPLE__
diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc
new file mode 100644
index 0000000..9dbc7a1
--- /dev/null
+++ b/lib/asan/asan_malloc_linux.cc
@@ -0,0 +1,142 @@
+//===-- asan_malloc_linux.cc ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Linux-specific malloc interception.
+// We simply define functions like malloc, free, realloc, etc.
+// They will replace the corresponding libc functions automagically.
+//===----------------------------------------------------------------------===//
+#ifdef __linux__
+
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_stack.h"
+
+#include <malloc.h>
+
+#define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
+
+#ifdef ANDROID
+struct MallocDebug {
+ void* (*malloc)(size_t bytes);
+ void (*free)(void* mem);
+ void* (*calloc)(size_t n_elements, size_t elem_size);
+ void* (*realloc)(void* oldMem, size_t bytes);
+ void* (*memalign)(size_t alignment, size_t bytes);
+};
+
+const MallocDebug asan_malloc_dispatch __attribute__((aligned(32))) = {
+ malloc, free, calloc, realloc, memalign
+};
+
+extern "C" const MallocDebug* __libc_malloc_dispatch;
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+ __libc_malloc_dispatch = &asan_malloc_dispatch;
+}
+} // namespace __asan
+
+#else // ANDROID
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+}
+} // namespace __asan
+#endif // ANDROID
+
+// ---------------------- Replacement functions ---------------- {{{1
+using namespace __asan; // NOLINT
+
+extern "C" {
+INTERCEPTOR_ATTRIBUTE
+void free(void *ptr) {
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ asan_free(ptr, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void cfree(void *ptr) {
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ asan_free(ptr, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void *malloc(size_t size) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_malloc(size, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void *calloc(size_t nmemb, size_t size) {
+ if (!asan_inited) {
+ // Hack: dlsym calls calloc before real_calloc is retrieved from dlsym.
+ const size_t kCallocPoolSize = 1024;
+ static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize];
+ static size_t allocated;
+ size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
+ void *mem = (void*)&calloc_memory_for_dlsym[allocated];
+ allocated += size_in_words;
+ CHECK(allocated < kCallocPoolSize);
+ return mem;
+ }
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_calloc(nmemb, size, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void *realloc(void *ptr, size_t size) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void *memalign(size_t boundary, size_t size) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_memalign(boundary, size, &stack);
+}
+
+void* __libc_memalign(size_t align, size_t s)
+ __attribute__((alias("memalign")));
+
+INTERCEPTOR_ATTRIBUTE
+struct mallinfo mallinfo() {
+ struct mallinfo res;
+ real_memset(&res, 0, sizeof(res));
+ return res;
+}
+
+INTERCEPTOR_ATTRIBUTE
+int mallopt(int cmd, int value) {
+ return -1;
+}
+
+INTERCEPTOR_ATTRIBUTE
+int posix_memalign(void **memptr, size_t alignment, size_t size) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ // Printf("posix_memalign: %lx %ld\n", alignment, size);
+ return asan_posix_memalign(memptr, alignment, size, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void *valloc(size_t size) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_valloc(size, &stack);
+}
+
+INTERCEPTOR_ATTRIBUTE
+void *pvalloc(size_t size) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_pvalloc(size, &stack);
+}
+} // extern "C"
+
+#endif // __linux__
diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc
new file mode 100644
index 0000000..8a6f1bc
--- /dev/null
+++ b/lib/asan/asan_malloc_mac.cc
@@ -0,0 +1,390 @@
+//===-- asan_rtl.cc ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#ifdef __APPLE__
+
+#include <AvailabilityMacros.h>
+#include <CoreFoundation/CFBase.h>
+#include <malloc/malloc.h>
+#include <setjmp.h>
+
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_stack.h"
+
+// Similar code is used in Google Perftools,
+// http://code.google.com/p/google-perftools.
+
+// ---------------------- Replacement functions ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// The free() implementation provided by OS X calls malloc_zone_from_ptr()
+// to find the owner of |ptr|. If the result is NULL, an invalid free() is
+// reported. Our implementation falls back to asan_free() in this case
+// in order to print an ASan-style report.
+extern "C"
+void free(void *ptr) {
+ malloc_zone_t *zone = malloc_zone_from_ptr(ptr);
+ if (zone) {
+#if defined(MAC_OS_X_VERSION_10_6) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ if ((zone->version >= 6) && (zone->free_definite_size)) {
+ zone->free_definite_size(zone, ptr, malloc_size(ptr));
+ } else {
+ malloc_zone_free(zone, ptr);
+ }
+#else
+ malloc_zone_free(zone, ptr);
+#endif
+ } else {
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ asan_free(ptr, &stack);
+ }
+}
+
+// TODO(glider): do we need both zones?
+static malloc_zone_t *system_malloc_zone = NULL;
+static malloc_zone_t *system_purgeable_zone = NULL;
+
+// We need to provide wrappers around all the libc functions.
+namespace {
+// TODO(glider): the mz_* functions should be united with the Linux wrappers,
+// as they are basically copied from there.
+size_t mz_size(malloc_zone_t* zone, const void* ptr) {
+ // Fast path: check whether this pointer belongs to the original malloc zone.
+ // We cannot just call malloc_zone_from_ptr(), because it in turn
+ // calls our mz_size().
+ if (system_malloc_zone) {
+ if ((system_malloc_zone->size)(system_malloc_zone, ptr)) return 0;
+ }
+ return __asan_mz_size(ptr);
+}
+
+void *mz_malloc(malloc_zone_t *zone, size_t size) {
+ if (!asan_inited) {
+ CHECK(system_malloc_zone);
+ return malloc_zone_malloc(system_malloc_zone, size);
+ }
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_malloc(size, &stack);
+}
+
+void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
+ if (!asan_inited) {
+ CHECK(system_malloc_zone);
+ return malloc_zone_malloc(system_malloc_zone, size);
+ }
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_malloc(size, &stack);
+}
+
+void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
+ if (!asan_inited) {
+ // Hack: dlsym calls calloc before real_calloc is retrieved from dlsym.
+ const size_t kCallocPoolSize = 1024;
+ static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize];
+ static size_t allocated;
+ size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
+ void *mem = (void*)&calloc_memory_for_dlsym[allocated];
+ allocated += size_in_words;
+ CHECK(allocated < kCallocPoolSize);
+ return mem;
+ }
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_calloc(nmemb, size, &stack);
+}
+
+void *mz_valloc(malloc_zone_t *zone, size_t size) {
+ if (!asan_inited) {
+ CHECK(system_malloc_zone);
+ return malloc_zone_valloc(system_malloc_zone, size);
+ }
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_memalign(kPageSize, size, &stack);
+}
+
+void print_zone_for_ptr(void *ptr) {
+ malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
+ if (orig_zone) {
+ if (orig_zone->zone_name) {
+ Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
+ ptr, orig_zone, orig_zone->zone_name);
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
+ ptr, orig_zone);
+ }
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = NULL\n", ptr);
+ }
+}
+
+// TODO(glider): the allocation callbacks need to be refactored.
+void mz_free(malloc_zone_t *zone, void *ptr) {
+ if (!ptr) return;
+ malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
+ // For some reason Chromium calls mz_free() for pointers that belong to
+ // DefaultPurgeableMallocZone instead of asan_zone. We might want to
+ // fix this someday.
+ if (orig_zone == system_purgeable_zone) {
+ system_purgeable_zone->free(system_purgeable_zone, ptr);
+ return;
+ }
+ if (__asan_mz_size(ptr)) {
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ asan_free(ptr, &stack);
+ } else {
+ // Let us just leak this memory for now.
+ Printf("mz_free(%p) -- attempting to free unallocated memory.\n"
+ "AddressSanitizer is ignoring this error on Mac OS now.\n", ptr);
+ print_zone_for_ptr(ptr);
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ stack.PrintStack();
+ return;
+ }
+}
+
+void cf_free(void *ptr, void *info) {
+ if (!ptr) return;
+ malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
+ // For some reason Chromium calls mz_free() for pointers that belong to
+ // DefaultPurgeableMallocZone instead of asan_zone. We might want to
+ // fix this someday.
+ if (orig_zone == system_purgeable_zone) {
+ system_purgeable_zone->free(system_purgeable_zone, ptr);
+ return;
+ }
+ if (__asan_mz_size(ptr)) {
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ asan_free(ptr, &stack);
+ } else {
+ // Let us just leak this memory for now.
+ Printf("cf_free(%p) -- attempting to free unallocated memory.\n"
+ "AddressSanitizer is ignoring this error on Mac OS now.\n", ptr);
+ print_zone_for_ptr(ptr);
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ stack.PrintStack();
+ return;
+ }
+}
+
+void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
+ if (!ptr) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_malloc(size, &stack);
+ } else {
+ if (__asan_mz_size(ptr)) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+ } else {
+ // We can't recover from reallocating an unknown address, because
+ // this would require reading at most |size| bytes from
+ // potentially unaccessible memory.
+ Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n", ptr);
+ print_zone_for_ptr(ptr);
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ stack.PrintStack();
+ ShowStatsAndAbort();
+ return NULL; // unreachable
+ }
+ }
+}
+
+void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
+ if (!ptr) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_malloc(size, &stack);
+ } else {
+ if (__asan_mz_size(ptr)) {
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+ } else {
+ // We can't recover from reallocating an unknown address, because
+ // this would require reading at most |size| bytes from
+ // potentially unaccessible memory.
+ Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n", ptr);
+ print_zone_for_ptr(ptr);
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);
+ stack.PrintStack();
+ ShowStatsAndAbort();
+ return NULL; // unreachable
+ }
+ }
+}
+
+void mz_destroy(malloc_zone_t* zone) {
+ // A no-op -- we will not be destroyed!
+ Printf("mz_destroy() called -- ignoring\n");
+}
+ // from AvailabilityMacros.h
+#if defined(MAC_OS_X_VERSION_10_6) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
+ if (!asan_inited) {
+ CHECK(system_malloc_zone);
+ return malloc_zone_memalign(system_malloc_zone, align, size);
+ }
+ GET_STACK_TRACE_HERE_FOR_MALLOC;
+ return asan_memalign(align, size, &stack);
+}
+
+// This function is currently unused, and we build with -Werror.
+#if 0
+void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) {
+ // TODO(glider): check that |size| is valid.
+ UNIMPLEMENTED();
+}
+#endif
+#endif
+
+// malloc_introspection callbacks. I'm not clear on what all of these do.
+kern_return_t mi_enumerator(task_t task, void *,
+ unsigned type_mask, vm_address_t zone_address,
+ memory_reader_t reader,
+ vm_range_recorder_t recorder) {
+ // Should enumerate all the pointers we have. Seems like a lot of work.
+ return KERN_FAILURE;
+}
+
+size_t mi_good_size(malloc_zone_t *zone, size_t size) {
+ // I think it's always safe to return size, but we maybe could do better.
+ return size;
+}
+
+boolean_t mi_check(malloc_zone_t *zone) {
+ UNIMPLEMENTED();
+ return true;
+}
+
+void mi_print(malloc_zone_t *zone, boolean_t verbose) {
+ UNIMPLEMENTED();
+ return;
+}
+
+void mi_log(malloc_zone_t *zone, void *address) {
+ // I don't think we support anything like this
+}
+
+void mi_force_lock(malloc_zone_t *zone) {
+ __asan_mz_force_lock();
+}
+
+void mi_force_unlock(malloc_zone_t *zone) {
+ __asan_mz_force_unlock();
+}
+
+// This function is currently unused, and we build with -Werror.
+#if 0
+void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
+ // TODO(csilvers): figure out how to fill these out
+ // TODO(glider): port this from tcmalloc when ready.
+ stats->blocks_in_use = 0;
+ stats->size_in_use = 0;
+ stats->max_size_in_use = 0;
+ stats->size_allocated = 0;
+}
+#endif
+
+boolean_t mi_zone_locked(malloc_zone_t *zone) {
+ // UNIMPLEMENTED();
+ return false;
+}
+
+} // unnamed namespace
+
+extern bool kCFUseCollectableAllocator; // is GC on?
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+ static malloc_introspection_t asan_introspection;
+ __asan::real_memset(&asan_introspection, 0, sizeof(asan_introspection));
+
+ asan_introspection.enumerator = &mi_enumerator;
+ asan_introspection.good_size = &mi_good_size;
+ asan_introspection.check = &mi_check;
+ asan_introspection.print = &mi_print;
+ asan_introspection.log = &mi_log;
+ asan_introspection.force_lock = &mi_force_lock;
+ asan_introspection.force_unlock = &mi_force_unlock;
+
+ static malloc_zone_t asan_zone;
+ __asan::real_memset(&asan_zone, 0, sizeof(malloc_zone_t));
+
+ // Start with a version 4 zone which is used for OS X 10.4 and 10.5.
+ asan_zone.version = 4;
+ asan_zone.zone_name = "asan";
+ asan_zone.size = &mz_size;
+ asan_zone.malloc = &mz_malloc;
+ asan_zone.calloc = &mz_calloc;
+ asan_zone.valloc = &mz_valloc;
+ asan_zone.free = &mz_free;
+ asan_zone.realloc = &mz_realloc;
+ asan_zone.destroy = &mz_destroy;
+ asan_zone.batch_malloc = NULL;
+ asan_zone.batch_free = NULL;
+ asan_zone.introspect = &asan_introspection;
+
+ // from AvailabilityMacros.h
+#if defined(MAC_OS_X_VERSION_10_6) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ // Switch to version 6 on OSX 10.6 to support memalign.
+ asan_zone.version = 6;
+ asan_zone.free_definite_size = 0;
+ asan_zone.memalign = &mz_memalign;
+ asan_introspection.zone_locked = &mi_zone_locked;
+
+ // Request the default purgable zone to force its creation. The
+ // current default zone is registered with the purgable zone for
+ // doing tiny and small allocs. Sadly, it assumes that the default
+ // zone is the szone implementation from OS X and will crash if it
+ // isn't. By creating the zone now, this will be true and changing
+ // the default zone won't cause a problem. (OS X 10.6 and higher.)
+ system_purgeable_zone = malloc_default_purgeable_zone();
+#endif
+
+ // Register the ASan zone. At this point, it will not be the
+ // default zone.
+ malloc_zone_register(&asan_zone);
+
+ // Unregister and reregister the default zone. Unregistering swaps
+ // the specified zone with the last one registered which for the
+ // default zone makes the more recently registered zone the default
+ // zone. The default zone is then re-registered to ensure that
+ // allocations made from it earlier will be handled correctly.
+ // Things are not guaranteed to work that way, but it's how they work now.
+ system_malloc_zone = malloc_default_zone();
+ malloc_zone_unregister(system_malloc_zone);
+ malloc_zone_register(system_malloc_zone);
+ // Make sure the default allocator was replaced.
+ CHECK(malloc_default_zone() == &asan_zone);
+
+ if (FLAG_replace_cfallocator) {
+ static CFAllocatorContext asan_context =
+ { /*version*/ 0, /*info*/ &asan_zone,
+ /*retain*/ NULL, /*release*/ NULL,
+ /*copyDescription*/NULL,
+ /*allocate*/ &cf_malloc,
+ /*reallocate*/ &cf_realloc,
+ /*deallocate*/ &cf_free,
+ /*preferredSize*/ NULL };
+ CFAllocatorRef cf_asan =
+ CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
+ CFAllocatorSetDefault(cf_asan);
+ }
+}
+} // namespace __asan
+
+#endif // __APPLE__
diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h
new file mode 100644
index 0000000..63aba10
--- /dev/null
+++ b/lib/asan/asan_mapping.h
@@ -0,0 +1,100 @@
+//===-- asan_mapping.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Defines ASan memory mapping.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_MAPPING_H
+#define ASAN_MAPPING_H
+
+#include "asan_internal.h"
+
+// The full explanation of the memory mapping could be found here:
+// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
+
+#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
+extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_scale;
+extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_offset;
+#define SHADOW_SCALE (__asan_mapping_scale)
+#define SHADOW_OFFSET (__asan_mapping_offset)
+#else
+#define SHADOW_SCALE (3)
+#if __WORDSIZE == 32
+#define SHADOW_OFFSET (1 << 29)
+#else
+#define SHADOW_OFFSET (1ULL << 44)
+#endif
+#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
+
+#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE)
+#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET))
+
+#if __WORDSIZE == 64
+ static const size_t kHighMemEnd = 0x00007fffffffffffUL;
+#else // __WORDSIZE == 32
+ static const size_t kHighMemEnd = 0xffffffff;
+#endif // __WORDSIZE
+
+
+#define kLowMemBeg 0
+#define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0)
+
+#define kLowShadowBeg SHADOW_OFFSET
+#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd)
+
+#define kHighMemBeg (MEM_TO_SHADOW(kHighMemEnd) + 1)
+
+#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg)
+#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd)
+
+#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 : 16 * kPageSize)
+#define kShadowGapEnd (kHighShadowBeg - 1)
+
+#define kGlobalAndStackRedzone \
+ (SHADOW_GRANULARITY < 32 ? 32 : SHADOW_GRANULARITY)
+
+namespace __asan {
+
+static inline bool AddrIsInLowMem(uintptr_t a) {
+ return a < kLowMemEnd;
+}
+
+static inline bool AddrIsInLowShadow(uintptr_t a) {
+ return a >= kLowShadowBeg && a <= kLowShadowEnd;
+}
+
+static inline bool AddrIsInHighMem(uintptr_t a) {
+ return a >= kHighMemBeg && a <= kHighMemEnd;
+}
+
+static inline bool AddrIsInMem(uintptr_t a) {
+ return AddrIsInLowMem(a) || AddrIsInHighMem(a);
+}
+
+static inline uintptr_t MemToShadow(uintptr_t p) {
+ CHECK(AddrIsInMem(p));
+ return MEM_TO_SHADOW(p);
+}
+
+static inline bool AddrIsInHighShadow(uintptr_t a) {
+ return a >= kHighShadowBeg && a <= kHighMemEnd;
+}
+
+static inline bool AddrIsInShadow(uintptr_t a) {
+ return AddrIsInLowShadow(a) || AddrIsInHighShadow(a);
+}
+
+static inline bool AddrIsAlignedByGranularity(uintptr_t a) {
+ return (a & (SHADOW_GRANULARITY - 1)) == 0;
+}
+
+} // namespace __asan
+
+#endif // ASAN_MAPPING_H
diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc
new file mode 100644
index 0000000..daa1ad6
--- /dev/null
+++ b/lib/asan/asan_poisoning.cc
@@ -0,0 +1,159 @@
+//===-- asan_poisoning.cc ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Shadow memory poisoning by ASan RTL and by user application.
+//===----------------------------------------------------------------------===//
+
+#include "asan_interceptors.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+
+namespace __asan {
+
+void PoisonShadow(uintptr_t addr, size_t size, uint8_t value) {
+ CHECK(AddrIsAlignedByGranularity(addr));
+ CHECK(AddrIsAlignedByGranularity(addr + size));
+ uintptr_t shadow_beg = MemToShadow(addr);
+ uintptr_t shadow_end = MemToShadow(addr + size);
+ real_memset((void*)shadow_beg, value, shadow_end - shadow_beg);
+}
+
+void PoisonShadowPartialRightRedzone(uintptr_t addr,
+ uintptr_t size,
+ uintptr_t redzone_size,
+ uint8_t value) {
+ CHECK(AddrIsAlignedByGranularity(addr));
+ uint8_t *shadow = (uint8_t*)MemToShadow(addr);
+ for (uintptr_t i = 0; i < redzone_size;
+ i += SHADOW_GRANULARITY, shadow++) {
+ if (i + SHADOW_GRANULARITY <= size) {
+ *shadow = 0; // fully addressable
+ } else if (i >= size) {
+ *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable
+ } else {
+ *shadow = size - i; // first size-i bytes are addressable
+ }
+ }
+}
+
+
+struct ShadowSegmentEndpoint {
+ uint8_t *chunk;
+ int8_t offset; // in [0, SHADOW_GRANULARITY)
+ int8_t value; // = *chunk;
+
+ explicit ShadowSegmentEndpoint(uintptr_t address) {
+ chunk = (uint8_t*)MemToShadow(address);
+ offset = address & (SHADOW_GRANULARITY - 1);
+ value = *chunk;
+ }
+};
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// Current implementation of __asan_(un)poison_memory_region doesn't check
+// that user program (un)poisons the memory it owns. It poisons memory
+// conservatively, and unpoisons progressively to make sure asan shadow
+// mapping invariant is preserved (see detailed mapping description here:
+// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm).
+//
+// * if user asks to poison region [left, right), the program poisons
+// at least [left, AlignDown(right)).
+// * if user asks to unpoison region [left, right), the program unpoisons
+// at most [AlignDown(left), right).
+void __asan_poison_memory_region(void const volatile *addr, size_t size) {
+ if (!FLAG_allow_user_poisoning || size == 0) return;
+ uintptr_t beg_addr = (uintptr_t)addr;
+ uintptr_t end_addr = beg_addr + size;
+ if (FLAG_v >= 1) {
+ Printf("Trying to poison memory region [%p, %p)\n", beg_addr, end_addr);
+ }
+ ShadowSegmentEndpoint beg(beg_addr);
+ ShadowSegmentEndpoint end(end_addr);
+ if (beg.chunk == end.chunk) {
+ CHECK(beg.offset < end.offset);
+ int8_t value = beg.value;
+ CHECK(value == end.value);
+ // We can only poison memory if the byte in end.offset is unaddressable.
+ // No need to re-poison memory if it is poisoned already.
+ if (value > 0 && value <= end.offset) {
+ if (beg.offset > 0) {
+ *beg.chunk = Min(value, beg.offset);
+ } else {
+ *beg.chunk = kAsanUserPoisonedMemoryMagic;
+ }
+ }
+ return;
+ }
+ CHECK(beg.chunk < end.chunk);
+ if (beg.offset > 0) {
+ // Mark bytes from beg.offset as unaddressable.
+ if (beg.value == 0) {
+ *beg.chunk = beg.offset;
+ } else {
+ *beg.chunk = Min(beg.value, beg.offset);
+ }
+ beg.chunk++;
+ }
+ real_memset(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
+ // Poison if byte in end.offset is unaddressable.
+ if (end.value > 0 && end.value <= end.offset) {
+ *end.chunk = kAsanUserPoisonedMemoryMagic;
+ }
+}
+
+void __asan_unpoison_memory_region(void const volatile *addr, size_t size) {
+ if (!FLAG_allow_user_poisoning || size == 0) return;
+ uintptr_t beg_addr = (uintptr_t)addr;
+ uintptr_t end_addr = beg_addr + size;
+ if (FLAG_v >= 1) {
+ Printf("Trying to unpoison memory region [%p, %p)\n", beg_addr, end_addr);
+ }
+ ShadowSegmentEndpoint beg(beg_addr);
+ ShadowSegmentEndpoint end(end_addr);
+ if (beg.chunk == end.chunk) {
+ CHECK(beg.offset < end.offset);
+ int8_t value = beg.value;
+ CHECK(value == end.value);
+ // We unpoison memory bytes up to enbytes up to end.offset if it is not
+ // unpoisoned already.
+ if (value != 0) {
+ *beg.chunk = Max(value, end.offset);
+ }
+ return;
+ }
+ CHECK(beg.chunk < end.chunk);
+ if (beg.offset > 0) {
+ *beg.chunk = 0;
+ beg.chunk++;
+ }
+ real_memset(beg.chunk, 0, end.chunk - beg.chunk);
+ if (end.offset > 0 && end.value != 0) {
+ *end.chunk = Max(end.value, end.offset);
+ }
+}
+
+bool __asan_address_is_poisoned(void const volatile *addr) {
+ const size_t kAccessSize = 1;
+ uintptr_t address = (uintptr_t)addr;
+ uint8_t *shadow_address = (uint8_t*)MemToShadow(address);
+ int8_t shadow_value = *shadow_address;
+ if (shadow_value) {
+ uint8_t last_accessed_byte = (address & (SHADOW_GRANULARITY - 1))
+ + kAccessSize - 1;
+ return (last_accessed_byte >= shadow_value);
+ }
+ return false;
+}
diff --git a/lib/asan/asan_printf.cc b/lib/asan/asan_printf.cc
new file mode 100644
index 0000000..a3d06ff
--- /dev/null
+++ b/lib/asan/asan_printf.cc
@@ -0,0 +1,181 @@
+//===-- asan_printf.cc ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Internal printf function, used inside ASan run-time library.
+// We can't use libc printf because we intercept some of the functions used
+// inside it.
+//===----------------------------------------------------------------------===//
+
+#include "asan_internal.h"
+#include "asan_interceptors.h"
+
+#include <stdarg.h>
+
+namespace __asan {
+
+void RawWrite(const char *buffer) {
+ static const char *kRawWriteError = "RawWrite can't output requested buffer!";
+ ssize_t length = (ssize_t)internal_strlen(buffer);
+ if (length != AsanWrite(2, buffer, length)) {
+ AsanWrite(2, kRawWriteError, internal_strlen(kRawWriteError));
+ ASAN_DIE;
+ }
+}
+
+static inline int AppendChar(char **buff, const char *buff_end, char c) {
+ if (*buff < buff_end) {
+ **buff = c;
+ (*buff)++;
+ }
+ return 1;
+}
+
+// Appends number in a given base to buffer. If its length is less than
+// "minimal_num_length", it is padded with leading zeroes.
+static int AppendUnsigned(char **buff, const char *buff_end, uint64_t num,
+ uint8_t base, uint8_t minimal_num_length) {
+ size_t const kMaxLen = 30;
+ RAW_CHECK(base == 10 || base == 16);
+ RAW_CHECK(minimal_num_length < kMaxLen);
+ size_t num_buffer[kMaxLen];
+ size_t pos = 0;
+ do {
+ RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow");
+ num_buffer[pos++] = num % base;
+ num /= base;
+ } while (num > 0);
+ while (pos < minimal_num_length) num_buffer[pos++] = 0;
+ int result = 0;
+ while (pos-- > 0) {
+ size_t digit = num_buffer[pos];
+ result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
+ : 'a' + digit - 10);
+ }
+ return result;
+}
+
+static inline int AppendSignedDecimal(char **buff, const char *buff_end,
+ int64_t num) {
+ int result = 0;
+ if (num < 0) {
+ result += AppendChar(buff, buff_end, '-');
+ num = -num;
+ }
+ result += AppendUnsigned(buff, buff_end, (uint64_t)num, 10, 0);
+ return result;
+}
+
+static inline int AppendString(char **buff, const char *buff_end,
+ const char *s) {
+ // Avoid library functions like stpcpy here.
+ RAW_CHECK(s);
+ int result = 0;
+ for (; *s; s++) {
+ result += AppendChar(buff, buff_end, *s);
+ }
+ return result;
+}
+
+static inline int AppendPointer(char **buff, const char *buff_end,
+ uint64_t ptr_value) {
+ int result = 0;
+ result += AppendString(buff, buff_end, "0x");
+ result += AppendUnsigned(buff, buff_end, ptr_value, 16,
+ (__WORDSIZE == 64) ? 12 : 8);
+ return result;
+}
+
+static int VSNPrintf(char *buff, int buff_length,
+ const char *format, va_list args) {
+ static const char *kPrintfFormatsHelp = "Supported Printf formats: "
+ "%%[l]{d,u,x}; %%p; %%s";
+ RAW_CHECK(format);
+ RAW_CHECK(buff_length > 0);
+ const char *buff_end = &buff[buff_length - 1];
+ const char *cur = format;
+ int result = 0;
+ for (; *cur; cur++) {
+ if (*cur == '%') {
+ cur++;
+ bool have_l = (*cur == 'l');
+ cur += have_l;
+ int64_t dval;
+ uint64_t uval, xval;
+ switch (*cur) {
+ case 'd': dval = have_l ? va_arg(args, intptr_t)
+ : va_arg(args, int);
+ result += AppendSignedDecimal(&buff, buff_end, dval);
+ break;
+ case 'u': uval = have_l ? va_arg(args, uintptr_t)
+ : va_arg(args, unsigned int);
+ result += AppendUnsigned(&buff, buff_end, uval, 10, 0);
+ break;
+ case 'x': xval = have_l ? va_arg(args, uintptr_t)
+ : va_arg(args, unsigned int);
+ result += AppendUnsigned(&buff, buff_end, xval, 16, 0);
+ break;
+ case 'p': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp);
+ result += AppendPointer(&buff, buff_end,
+ va_arg(args, uintptr_t));
+ break;
+ case 's': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp);
+ result += AppendString(&buff, buff_end, va_arg(args, char*));
+ break;
+ default: RAW_CHECK_MSG(false, kPrintfFormatsHelp);
+ }
+ } else {
+ result += AppendChar(&buff, buff_end, *cur);
+ }
+ }
+ RAW_CHECK(buff <= buff_end);
+ AppendChar(&buff, buff_end + 1, '\0');
+ return result;
+}
+
+void Printf(const char *format, ...) {
+ const int kLen = 1024 * 4;
+ char buffer[kLen];
+ va_list args;
+ va_start(args, format);
+ int needed_length = VSNPrintf(buffer, kLen, format, args);
+ va_end(args);
+ RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n");
+ RawWrite(buffer);
+}
+
+// Writes at most "length" symbols to "buffer" (including trailing '\0').
+// Returns the number of symbols that should have been written to buffer
+// (not including trailing '\0'). Thus, the string is truncated
+// iff return value is not less than "length".
+int SNPrintf(char *buffer, size_t length, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ int needed_length = VSNPrintf(buffer, length, format, args);
+ va_end(args);
+ return needed_length;
+}
+
+// Like Printf, but prints the current PID before the output string.
+void Report(const char *format, ...) {
+ const int kLen = 1024 * 4;
+ char buffer[kLen];
+ int needed_length = SNPrintf(buffer, kLen, "==%d== ", getpid());
+ RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
+ va_list args;
+ va_start(args, format);
+ needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length,
+ format, args);
+ va_end(args);
+ RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
+ RawWrite(buffer);
+}
+
+} // namespace __asan
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
new file mode 100644
index 0000000..c876f6d
--- /dev/null
+++ b/lib/asan/asan_rtl.cc
@@ -0,0 +1,810 @@
+//===-- asan_rtl.cc ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Main file of the ASan run-time library.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_lock.h"
+#include "asan_mac.h"
+#include "asan_mapping.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+
+#include <new>
+#include <dlfcn.h>
+#include <execinfo.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifndef ANDROID
+#include <sys/ucontext.h>
+#endif
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+// must not include <setjmp.h> on Linux
+
+namespace __asan {
+
+// -------------------------- Flags ------------------------- {{{1
+static const size_t kMallocContextSize = 30;
+static int FLAG_atexit;
+bool FLAG_fast_unwind = true;
+
+size_t FLAG_redzone; // power of two, >= 32
+size_t FLAG_quarantine_size;
+int FLAG_demangle;
+bool FLAG_symbolize;
+int FLAG_v;
+int FLAG_debug;
+bool FLAG_poison_shadow;
+int FLAG_report_globals;
+size_t FLAG_malloc_context_size = kMallocContextSize;
+uintptr_t FLAG_large_malloc;
+bool FLAG_lazy_shadow;
+bool FLAG_handle_segv;
+bool FLAG_handle_sigill;
+bool FLAG_replace_str;
+bool FLAG_replace_intrin;
+bool FLAG_replace_cfallocator; // Used on Mac only.
+size_t FLAG_max_malloc_fill_size = 0;
+bool FLAG_use_fake_stack;
+int FLAG_exitcode = EXIT_FAILURE;
+bool FLAG_allow_user_poisoning;
+
+// -------------------------- Globals --------------------- {{{1
+int asan_inited;
+bool asan_init_is_running;
+
+// -------------------------- Interceptors ---------------- {{{1
+typedef int (*sigaction_f)(int signum, const struct sigaction *act,
+ struct sigaction *oldact);
+typedef sig_t (*signal_f)(int signum, sig_t handler);
+typedef void (*longjmp_f)(void *env, int val);
+typedef longjmp_f _longjmp_f;
+typedef longjmp_f siglongjmp_f;
+typedef void (*__cxa_throw_f)(void *, void *, void *);
+typedef int (*pthread_create_f)(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg);
+#ifdef __APPLE__
+dispatch_async_f_f real_dispatch_async_f;
+dispatch_sync_f_f real_dispatch_sync_f;
+dispatch_after_f_f real_dispatch_after_f;
+dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
+dispatch_group_async_f_f real_dispatch_group_async_f;
+pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
+#endif
+
+sigaction_f real_sigaction;
+signal_f real_signal;
+longjmp_f real_longjmp;
+_longjmp_f real__longjmp;
+siglongjmp_f real_siglongjmp;
+__cxa_throw_f real___cxa_throw;
+pthread_create_f real_pthread_create;
+
+// -------------------------- Misc ---------------- {{{1
+void ShowStatsAndAbort() {
+ __asan_print_accumulated_stats();
+ ASAN_DIE;
+}
+
+static void PrintBytes(const char *before, uintptr_t *a) {
+ uint8_t *bytes = (uint8_t*)a;
+ size_t byte_num = (__WORDSIZE) / 8;
+ Printf("%s%p:", before, (uintptr_t)a);
+ for (size_t i = 0; i < byte_num; i++) {
+ Printf(" %lx%lx", bytes[i] >> 4, bytes[i] & 15);
+ }
+ Printf("\n");
+}
+
+// Opens the file 'file_name" and reads up to 'max_len' bytes.
+// The resulting buffer is mmaped and stored in '*buff'.
+// Returns the number of read bytes or -1 if file can not be opened.
+static ssize_t ReadFileToBuffer(const char *file_name, char **buff,
+ size_t max_len) {
+ const size_t kMinFileLen = kPageSize;
+ ssize_t read_len = -1;
+ *buff = 0;
+ size_t maped_size = 0;
+ // The files we usually open are not seekable, so try different buffer sizes.
+ for (size_t size = kMinFileLen; size <= max_len; size *= 2) {
+ int fd = AsanOpenReadonly(file_name);
+ if (fd < 0) return -1;
+ AsanUnmapOrDie(*buff, maped_size);
+ maped_size = size;
+ *buff = (char*)AsanMmapSomewhereOrDie(size, __FUNCTION__);
+ read_len = AsanRead(fd, *buff, size);
+ AsanClose(fd);
+ if (read_len < size) // We've read the whole file.
+ break;
+ }
+ return read_len;
+}
+
+// Like getenv, but reads env directly from /proc and does not use libc.
+// This function should be called first inside __asan_init.
+static const char* GetEnvFromProcSelfEnviron(const char* name) {
+ static char *environ;
+ static ssize_t len;
+ static bool inited;
+ if (!inited) {
+ inited = true;
+ len = ReadFileToBuffer("/proc/self/environ", &environ, 1 << 20);
+ }
+ if (!environ || len <= 0) return NULL;
+ size_t namelen = internal_strlen(name);
+ const char *p = environ;
+ while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
+ // proc file has the format NAME=value\0NAME=value\0NAME=value\0...
+ const char* endp =
+ (char*)internal_memchr(p, '\0', len - (p - environ));
+ if (endp == NULL) // this entry isn't NUL terminated
+ return NULL;
+ else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
+ return p + namelen + 1; // point after =
+ p = endp + 1;
+ }
+ return NULL; // Not found.
+}
+
+// ---------------------- Thread ------------------------- {{{1
+static void *asan_thread_start(void *arg) {
+ AsanThread *t= (AsanThread*)arg;
+ asanThreadRegistry().SetCurrent(t);
+ return t->ThreadStart();
+}
+
+// ---------------------- mmap -------------------- {{{1
+void OutOfMemoryMessageAndDie(const char *mem_type, size_t size) {
+ Report("ERROR: AddressSanitizer failed to allocate "
+ "0x%lx (%ld) bytes of %s\n",
+ size, size, mem_type);
+ PRINT_CURRENT_STACK();
+ ShowStatsAndAbort();
+}
+
+// Reserve memory range [beg, end].
+static void ReserveShadowMemoryRange(uintptr_t beg, uintptr_t end) {
+ CHECK((beg % kPageSize) == 0);
+ CHECK(((end + 1) % kPageSize) == 0);
+ size_t size = end - beg + 1;
+ void *res = AsanMmapFixedNoReserve(beg, size);
+ CHECK(res == (void*)beg && "ReserveShadowMemoryRange failed");
+}
+
+// ---------------------- LowLevelAllocator ------------- {{{1
+void *LowLevelAllocator::Allocate(size_t size) {
+ CHECK((size & (size - 1)) == 0 && "size must be a power of two");
+ if (allocated_end_ - allocated_current_ < size) {
+ size_t size_to_allocate = Max(size, kPageSize);
+ allocated_current_ =
+ (char*)AsanMmapSomewhereOrDie(size_to_allocate, __FUNCTION__);
+ allocated_end_ = allocated_current_ + size_to_allocate;
+ PoisonShadow((uintptr_t)allocated_current_, size_to_allocate,
+ kAsanInternalHeapMagic);
+ }
+ CHECK(allocated_end_ - allocated_current_ >= size);
+ void *res = allocated_current_;
+ allocated_current_ += size;
+ return res;
+}
+
+// ---------------------- DescribeAddress -------------------- {{{1
+static bool DescribeStackAddress(uintptr_t addr, uintptr_t access_size) {
+ AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
+ if (!t) return false;
+ const intptr_t kBufSize = 4095;
+ char buf[kBufSize];
+ uintptr_t offset = 0;
+ const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
+ // This string is created by the compiler and has the following form:
+ // "FunctioName n alloc_1 alloc_2 ... alloc_n"
+ // where alloc_i looks like "offset size len ObjectName ".
+ CHECK(frame_descr);
+ // Report the function name and the offset.
+ const char *name_end = real_strchr(frame_descr, ' ');
+ CHECK(name_end);
+ buf[0] = 0;
+ strncat(buf, frame_descr,
+ Min(kBufSize, static_cast<intptr_t>(name_end - frame_descr)));
+ Printf("Address %p is located at offset %ld "
+ "in frame <%s> of T%d's stack:\n",
+ addr, offset, buf, t->tid());
+ // Report the number of stack objects.
+ char *p;
+ size_t n_objects = strtol(name_end, &p, 10);
+ CHECK(n_objects > 0);
+ Printf(" This frame has %ld object(s):\n", n_objects);
+ // Report all objects in this frame.
+ for (size_t i = 0; i < n_objects; i++) {
+ size_t beg, size;
+ intptr_t len;
+ beg = strtol(p, &p, 10);
+ size = strtol(p, &p, 10);
+ len = strtol(p, &p, 10);
+ if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
+ Printf("AddressSanitizer can't parse the stack frame descriptor: |%s|\n",
+ frame_descr);
+ break;
+ }
+ p++;
+ buf[0] = 0;
+ strncat(buf, p, Min(kBufSize, len));
+ p += len;
+ Printf(" [%ld, %ld) '%s'\n", beg, beg + size, buf);
+ }
+ Printf("HINT: this may be a false positive if your program uses "
+ "some custom stack unwind mechanism\n"
+ " (longjmp and C++ exceptions *are* supported)\n");
+ t->summary()->Announce();
+ return true;
+}
+
+__attribute__((noinline))
+static void DescribeAddress(uintptr_t addr, uintptr_t access_size) {
+ // Check if this is a global.
+ if (DescribeAddrIfGlobal(addr))
+ return;
+
+ if (DescribeStackAddress(addr, access_size))
+ return;
+
+ // finally, check if this is a heap.
+ DescribeHeapAddress(addr, access_size);
+}
+
+// -------------------------- Run-time entry ------------------- {{{1
+void GetPcSpBpAx(void *context,
+ uintptr_t *pc, uintptr_t *sp, uintptr_t *bp, uintptr_t *ax) {
+#ifndef ANDROID
+ ucontext_t *ucontext = (ucontext_t*)context;
+#endif
+#ifdef __APPLE__
+# if __WORDSIZE == 64
+ *pc = ucontext->uc_mcontext->__ss.__rip;
+ *bp = ucontext->uc_mcontext->__ss.__rbp;
+ *sp = ucontext->uc_mcontext->__ss.__rsp;
+ *ax = ucontext->uc_mcontext->__ss.__rax;
+# else
+ *pc = ucontext->uc_mcontext->__ss.__eip;
+ *bp = ucontext->uc_mcontext->__ss.__ebp;
+ *sp = ucontext->uc_mcontext->__ss.__esp;
+ *ax = ucontext->uc_mcontext->__ss.__eax;
+# endif // __WORDSIZE
+#else // assume linux
+# if defined (ANDROID)
+ *pc = *sp = *bp = *ax = 0;
+# elif defined(__arm__)
+ *pc = ucontext->uc_mcontext.arm_pc;
+ *bp = ucontext->uc_mcontext.arm_fp;
+ *sp = ucontext->uc_mcontext.arm_sp;
+ *ax = ucontext->uc_mcontext.arm_r0;
+# elif __WORDSIZE == 64
+ *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+ *ax = ucontext->uc_mcontext.gregs[REG_RAX];
+# else
+ *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+ *ax = ucontext->uc_mcontext.gregs[REG_EAX];
+# endif // __WORDSIZE
+#endif
+}
+
+static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
+ uintptr_t addr = (uintptr_t)siginfo->si_addr;
+ if (AddrIsInShadow(addr) && FLAG_lazy_shadow) {
+ // We traped on access to a shadow address. Just map a large chunk around
+ // this address.
+ const uintptr_t chunk_size = kPageSize << 10; // 4M
+ uintptr_t chunk = addr & ~(chunk_size - 1);
+ AsanMmapFixedReserve(chunk, chunk_size);
+ return;
+ }
+ // Write the first message using the bullet-proof write.
+ if (13 != AsanWrite(2, "ASAN:SIGSEGV\n", 13)) ASAN_DIE;
+ uintptr_t pc, sp, bp, ax;
+ GetPcSpBpAx(context, &pc, &sp, &bp, &ax);
+ Report("ERROR: AddressSanitizer crashed on unknown address %p"
+ " (pc %p sp %p bp %p ax %p T%d)\n",
+ addr, pc, sp, bp, ax,
+ asanThreadRegistry().GetCurrentTidOrMinusOne());
+ Printf("AddressSanitizer can not provide additional info. ABORTING\n");
+ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, false, pc, bp);
+ stack.PrintStack();
+ ShowStatsAndAbort();
+}
+
+static void ASAN_OnSIGILL(int, siginfo_t *siginfo, void *context) {
+ // Write the first message using the bullet-proof write.
+ if (12 != AsanWrite(2, "ASAN:SIGILL\n", 12)) ASAN_DIE;
+ uintptr_t pc, sp, bp, ax;
+ GetPcSpBpAx(context, &pc, &sp, &bp, &ax);
+
+ uintptr_t addr = ax;
+
+ uint8_t *insn = (uint8_t*)pc;
+ CHECK(insn[0] == 0x0f && insn[1] == 0x0b); // ud2
+ unsigned access_size_and_type = insn[2] - 0x50;
+ CHECK(access_size_and_type < 16);
+ bool is_write = access_size_and_type & 8;
+ int access_size = 1 << (access_size_and_type & 7);
+ __asan_report_error(pc, bp, sp, addr, is_write, access_size);
+}
+
+// exported functions
+#define ASAN_REPORT_ERROR(type, is_write, size) \
+extern "C" void __asan_report_ ## type ## size(uintptr_t addr) \
+ __attribute__((visibility("default"))) __attribute__((noinline)); \
+extern "C" void __asan_report_ ## type ## size(uintptr_t addr) { \
+ GET_BP_PC_SP; \
+ __asan_report_error(pc, bp, sp, addr, is_write, size); \
+}
+
+ASAN_REPORT_ERROR(load, false, 1)
+ASAN_REPORT_ERROR(load, false, 2)
+ASAN_REPORT_ERROR(load, false, 4)
+ASAN_REPORT_ERROR(load, false, 8)
+ASAN_REPORT_ERROR(load, false, 16)
+ASAN_REPORT_ERROR(store, true, 1)
+ASAN_REPORT_ERROR(store, true, 2)
+ASAN_REPORT_ERROR(store, true, 4)
+ASAN_REPORT_ERROR(store, true, 8)
+ASAN_REPORT_ERROR(store, true, 16)
+
+// Force the linker to keep the symbols for various ASan interface functions.
+// We want to keep those in the executable in order to let the instrumented
+// dynamic libraries access the symbol even if it is not used by the executable
+// itself. This should help if the build system is removing dead code at link
+// time.
+static void force_interface_symbols() {
+ volatile int fake_condition = 0; // prevent dead condition elimination.
+ if (fake_condition) {
+ __asan_report_load1(NULL);
+ __asan_report_load2(NULL);
+ __asan_report_load4(NULL);
+ __asan_report_load8(NULL);
+ __asan_report_load16(NULL);
+ __asan_report_store1(NULL);
+ __asan_report_store2(NULL);
+ __asan_report_store4(NULL);
+ __asan_report_store8(NULL);
+ __asan_report_store16(NULL);
+ __asan_register_global(0, 0, NULL);
+ __asan_register_globals(NULL, 0);
+ __asan_unregister_globals(NULL, 0);
+ }
+}
+
+// -------------------------- Init ------------------- {{{1
+static int64_t IntFlagValue(const char *flags, const char *flag,
+ int64_t default_val) {
+ if (!flags) return default_val;
+ const char *str = strstr(flags, flag);
+ if (!str) return default_val;
+ return atoll(str + internal_strlen(flag));
+}
+
+static void asan_atexit() {
+ Printf("AddressSanitizer exit stats:\n");
+ __asan_print_accumulated_stats();
+}
+
+void CheckFailed(const char *cond, const char *file, int line) {
+ Report("CHECK failed: %s at %s:%d, pthread_self=%p\n",
+ cond, file, line, pthread_self());
+ PRINT_CURRENT_STACK();
+ ShowStatsAndAbort();
+}
+
+} // namespace __asan
+
+// -------------------------- Interceptors ------------------- {{{1
+using namespace __asan; // NOLINT
+
+#define OPERATOR_NEW_BODY \
+ GET_STACK_TRACE_HERE_FOR_MALLOC;\
+ return asan_memalign(0, size, &stack);
+
+#ifdef ANDROID
+void *operator new(size_t size) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
+#else
+void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size, std::nothrow_t const&) throw()
+{ OPERATOR_NEW_BODY; }
+void *operator new[](size_t size, std::nothrow_t const&) throw()
+{ OPERATOR_NEW_BODY; }
+#endif
+
+#define OPERATOR_DELETE_BODY \
+ GET_STACK_TRACE_HERE_FOR_FREE(ptr);\
+ asan_free(ptr, &stack);
+
+void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; }
+void operator delete(void *ptr, std::nothrow_t const&) throw()
+{ OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr, std::nothrow_t const&) throw()
+{ OPERATOR_DELETE_BODY;}
+
+extern "C"
+#ifndef __APPLE__
+__attribute__((visibility("default")))
+#endif
+int WRAP(pthread_create)(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg) {
+ GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
+ AsanThread *t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack);
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
+ new(t) AsanThread(asanThreadRegistry().GetCurrentTidOrMinusOne(),
+ start_routine, arg, &stack);
+ return real_pthread_create(thread, attr, asan_thread_start, t);
+}
+
+static bool MySignal(int signum) {
+ if (FLAG_handle_sigill && signum == SIGILL) return true;
+ if (FLAG_handle_segv && signum == SIGSEGV) return true;
+#ifdef __APPLE__
+ if (FLAG_handle_segv && signum == SIGBUS) return true;
+#endif
+ return false;
+}
+
+static void MaybeInstallSigaction(int signum,
+ void (*handler)(int, siginfo_t *, void *)) {
+ if (!MySignal(signum))
+ return;
+ struct sigaction sigact;
+ real_memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = handler;
+ sigact.sa_flags = SA_SIGINFO;
+ CHECK(0 == real_sigaction(signum, &sigact, 0));
+}
+
+extern "C"
+sig_t WRAP(signal)(int signum, sig_t handler) {
+ if (!MySignal(signum)) {
+ return real_signal(signum, handler);
+ }
+ return NULL;
+}
+
+extern "C"
+int WRAP(sigaction)(int signum, const struct sigaction *act,
+ struct sigaction *oldact) {
+ if (!MySignal(signum)) {
+ return real_sigaction(signum, act, oldact);
+ }
+ return 0;
+}
+
+
+static void UnpoisonStackFromHereToTop() {
+ int local_stack;
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ CHECK(curr_thread);
+ uintptr_t top = curr_thread->stack_top();
+ uintptr_t bottom = ((uintptr_t)&local_stack - kPageSize) & ~(kPageSize-1);
+ PoisonShadow(bottom, top - bottom, 0);
+}
+
+extern "C" void WRAP(longjmp)(void *env, int val) {
+ UnpoisonStackFromHereToTop();
+ real_longjmp(env, val);
+}
+
+extern "C" void WRAP(_longjmp)(void *env, int val) {
+ UnpoisonStackFromHereToTop();
+ real__longjmp(env, val);
+}
+
+extern "C" void WRAP(siglongjmp)(void *env, int val) {
+ UnpoisonStackFromHereToTop();
+ real_siglongjmp(env, val);
+}
+
+extern "C" void __cxa_throw(void *a, void *b, void *c);
+
+#if ASAN_HAS_EXCEPTIONS == 1
+extern "C" void WRAP(__cxa_throw)(void *a, void *b, void *c) {
+ CHECK(&real___cxa_throw);
+ UnpoisonStackFromHereToTop();
+ real___cxa_throw(a, b, c);
+}
+#endif
+
+extern "C" {
+// intercept mlock and friends.
+// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
+// All functions return 0 (success).
+static void MlockIsUnsupported() {
+ static bool printed = 0;
+ if (printed) return;
+ printed = true;
+ Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n");
+}
+int mlock(const void *addr, size_t len) {
+ MlockIsUnsupported();
+ return 0;
+}
+int munlock(const void *addr, size_t len) {
+ MlockIsUnsupported();
+ return 0;
+}
+int mlockall(int flags) {
+ MlockIsUnsupported();
+ return 0;
+}
+int munlockall(void) {
+ MlockIsUnsupported();
+ return 0;
+}
+} // extern "C"
+
+// ---------------------- Interface ---------------- {{{1
+int __asan_set_error_exit_code(int exit_code) {
+ int old = FLAG_exitcode;
+ FLAG_exitcode = exit_code;
+ return old;
+}
+
+void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp,
+ uintptr_t addr, bool is_write, size_t access_size) {
+ // Do not print more than one report, otherwise they will mix up.
+ static int num_calls = 0;
+ if (AtomicInc(&num_calls) > 1) return;
+
+ Printf("=================================================================\n");
+ const char *bug_descr = "unknown-crash";
+ if (AddrIsInMem(addr)) {
+ uint8_t *shadow_addr = (uint8_t*)MemToShadow(addr);
+ // If we are accessing 16 bytes, look at the second shadow byte.
+ if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
+ shadow_addr++;
+ // If we are in the partial right redzone, look at the next shadow byte.
+ if (*shadow_addr > 0 && *shadow_addr < 128)
+ shadow_addr++;
+ switch (*shadow_addr) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanHeapRightRedzoneMagic:
+ bug_descr = "heap-buffer-overflow";
+ break;
+ case kAsanHeapFreeMagic:
+ bug_descr = "heap-use-after-free";
+ break;
+ case kAsanStackLeftRedzoneMagic:
+ bug_descr = "stack-buffer-underflow";
+ break;
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ case kAsanStackPartialRedzoneMagic:
+ bug_descr = "stack-buffer-overflow";
+ break;
+ case kAsanStackAfterReturnMagic:
+ bug_descr = "stack-use-after-return";
+ break;
+ case kAsanUserPoisonedMemoryMagic:
+ bug_descr = "use-after-poison";
+ break;
+ case kAsanGlobalRedzoneMagic:
+ bug_descr = "global-buffer-overflow";
+ break;
+ }
+ }
+
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ int curr_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
+
+ if (curr_thread) {
+ // We started reporting an error message. Stop using the fake stack
+ // in case we will call an instrumented function from a symbolizer.
+ curr_thread->fake_stack().StopUsingFakeStack();
+ }
+
+ Report("ERROR: AddressSanitizer %s on address "
+ "%p at pc 0x%lx bp 0x%lx sp 0x%lx\n",
+ bug_descr, addr, pc, bp, sp);
+
+ Printf("%s of size %d at %p thread T%d\n",
+ access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
+ access_size, addr, curr_tid);
+
+ if (FLAG_debug) {
+ PrintBytes("PC: ", (uintptr_t*)pc);
+ }
+
+ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax,
+ false, // FLAG_fast_unwind,
+ pc, bp);
+ stack.PrintStack();
+
+ CHECK(AddrIsInMem(addr));
+
+ DescribeAddress(addr, access_size);
+
+ uintptr_t shadow_addr = MemToShadow(addr);
+ Report("ABORTING\n");
+ __asan_print_accumulated_stats();
+ Printf("Shadow byte and word:\n");
+ Printf(" %p: %x\n", shadow_addr, *(unsigned char*)shadow_addr);
+ uintptr_t aligned_shadow = shadow_addr & ~(kWordSize - 1);
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow));
+ Printf("More shadow bytes:\n");
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow-4*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow-3*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow-2*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow-1*kWordSize));
+ PrintBytes("=>", (uintptr_t*)(aligned_shadow+0*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow+1*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow+2*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow+3*kWordSize));
+ PrintBytes(" ", (uintptr_t*)(aligned_shadow+4*kWordSize));
+ ASAN_DIE;
+}
+
+void __asan_init() {
+ if (asan_inited) return;
+ asan_init_is_running = true;
+
+ // Make sure we are not statically linked.
+ AsanDoesNotSupportStaticLinkage();
+
+ // flags
+ const char *options = GetEnvFromProcSelfEnviron("ASAN_OPTIONS");
+ FLAG_malloc_context_size =
+ IntFlagValue(options, "malloc_context_size=", kMallocContextSize);
+ CHECK(FLAG_malloc_context_size <= kMallocContextSize);
+
+ FLAG_max_malloc_fill_size =
+ IntFlagValue(options, "max_malloc_fill_size=", 0);
+
+ FLAG_v = IntFlagValue(options, "verbosity=", 0);
+
+ FLAG_redzone = IntFlagValue(options, "redzone=", 128);
+ CHECK(FLAG_redzone >= 32);
+ CHECK((FLAG_redzone & (FLAG_redzone - 1)) == 0);
+
+ FLAG_atexit = IntFlagValue(options, "atexit=", 0);
+ FLAG_poison_shadow = IntFlagValue(options, "poison_shadow=", 1);
+ FLAG_report_globals = IntFlagValue(options, "report_globals=", 1);
+ FLAG_lazy_shadow = IntFlagValue(options, "lazy_shadow=", 0);
+ FLAG_handle_segv = IntFlagValue(options, "handle_segv=", ASAN_NEEDS_SEGV);
+ FLAG_handle_sigill = IntFlagValue(options, "handle_sigill=", 0);
+ FLAG_symbolize = IntFlagValue(options, "symbolize=", 1);
+ FLAG_demangle = IntFlagValue(options, "demangle=", 1);
+ FLAG_debug = IntFlagValue(options, "debug=", 0);
+ FLAG_replace_cfallocator = IntFlagValue(options, "replace_cfallocator=", 1);
+ FLAG_fast_unwind = IntFlagValue(options, "fast_unwind=", 1);
+ FLAG_replace_str = IntFlagValue(options, "replace_str=", 1);
+ FLAG_replace_intrin = IntFlagValue(options, "replace_intrin=", 1);
+ FLAG_use_fake_stack = IntFlagValue(options, "use_fake_stack=", 1);
+ FLAG_exitcode = IntFlagValue(options, "exitcode=", EXIT_FAILURE);
+ FLAG_allow_user_poisoning = IntFlagValue(options,
+ "allow_user_poisoning=", 1);
+
+ if (FLAG_atexit) {
+ atexit(asan_atexit);
+ }
+
+ FLAG_quarantine_size =
+ IntFlagValue(options, "quarantine_size=", 1UL << 28);
+
+ // interceptors
+ InitializeAsanInterceptors();
+
+ ReplaceSystemMalloc();
+
+ INTERCEPT_FUNCTION(sigaction);
+ INTERCEPT_FUNCTION(signal);
+ INTERCEPT_FUNCTION(longjmp);
+ INTERCEPT_FUNCTION(_longjmp);
+ INTERCEPT_FUNCTION_IF_EXISTS(__cxa_throw);
+ INTERCEPT_FUNCTION(pthread_create);
+#ifdef __APPLE__
+ INTERCEPT_FUNCTION(dispatch_async_f);
+ INTERCEPT_FUNCTION(dispatch_sync_f);
+ INTERCEPT_FUNCTION(dispatch_after_f);
+ INTERCEPT_FUNCTION(dispatch_barrier_async_f);
+ INTERCEPT_FUNCTION(dispatch_group_async_f);
+ // We don't need to intercept pthread_workqueue_additem_np() to support the
+ // libdispatch API, but it helps us to debug the unsupported functions. Let's
+ // intercept it only during verbose runs.
+ if (FLAG_v >= 2) {
+ INTERCEPT_FUNCTION(pthread_workqueue_additem_np);
+ }
+#else
+ // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
+ // there.
+ INTERCEPT_FUNCTION(siglongjmp);
+#endif
+
+ MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV);
+ MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV);
+ MaybeInstallSigaction(SIGILL, ASAN_OnSIGILL);
+
+ if (FLAG_v) {
+ Printf("|| `[%p, %p]` || HighMem ||\n", kHighMemBeg, kHighMemEnd);
+ Printf("|| `[%p, %p]` || HighShadow ||\n",
+ kHighShadowBeg, kHighShadowEnd);
+ Printf("|| `[%p, %p]` || ShadowGap ||\n",
+ kShadowGapBeg, kShadowGapEnd);
+ Printf("|| `[%p, %p]` || LowShadow ||\n",
+ kLowShadowBeg, kLowShadowEnd);
+ Printf("|| `[%p, %p]` || LowMem ||\n", kLowMemBeg, kLowMemEnd);
+ Printf("MemToShadow(shadow): %p %p %p %p\n",
+ MEM_TO_SHADOW(kLowShadowBeg),
+ MEM_TO_SHADOW(kLowShadowEnd),
+ MEM_TO_SHADOW(kHighShadowBeg),
+ MEM_TO_SHADOW(kHighShadowEnd));
+ Printf("red_zone=%ld\n", FLAG_redzone);
+ Printf("malloc_context_size=%ld\n", (int)FLAG_malloc_context_size);
+ Printf("fast_unwind=%d\n", (int)FLAG_fast_unwind);
+
+ Printf("SHADOW_SCALE: %lx\n", SHADOW_SCALE);
+ Printf("SHADOW_GRANULARITY: %lx\n", SHADOW_GRANULARITY);
+ Printf("SHADOW_OFFSET: %lx\n", SHADOW_OFFSET);
+ CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
+ }
+
+ if (__WORDSIZE == 64) {
+ // Disable core dumper -- it makes little sense to dump 16T+ core.
+ struct rlimit nocore;
+ nocore.rlim_cur = 0;
+ nocore.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &nocore);
+ }
+
+ {
+ if (!FLAG_lazy_shadow) {
+ if (kLowShadowBeg != kLowShadowEnd) {
+ // mmap the low shadow plus one page.
+ ReserveShadowMemoryRange(kLowShadowBeg - kPageSize, kLowShadowEnd);
+ }
+ // mmap the high shadow.
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
+ }
+ // protect the gap
+ void *prot = AsanMprotect(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
+ CHECK(prot == (void*)kShadowGapBeg);
+ }
+
+ // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
+ // should be set to 1 prior to initializing the threads.
+ asan_inited = 1;
+ asan_init_is_running = false;
+
+ asanThreadRegistry().Init();
+ asanThreadRegistry().GetMain()->ThreadStart();
+ force_interface_symbols(); // no-op.
+
+ if (FLAG_v) {
+ Report("AddressSanitizer Init done\n");
+ }
+}
diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc
new file mode 100644
index 0000000..8163983
--- /dev/null
+++ b/lib/asan/asan_stack.cc
@@ -0,0 +1,280 @@
+//===-- asan_stack.cc -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Code for ASan stack trace.
+//===----------------------------------------------------------------------===//
+#include "asan_interceptors.h"
+#include "asan_lock.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+
+#include <string.h>
+
+#if ASAN_USE_SYSINFO == 1
+#include "sysinfo/sysinfo.h"
+#endif
+
+#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
+extern bool
+ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
+#endif
+
+namespace __asan {
+
+// ----------------------- ProcSelfMaps ----------------------------- {{{1
+#if ASAN_USE_SYSINFO == 1
+class ProcSelfMaps {
+ public:
+ void Init() {
+ ScopedLock lock(&mu_);
+ if (map_size_ != 0) return; // already inited
+ if (FLAG_v >= 2) {
+ Printf("ProcSelfMaps::Init()\n");
+ }
+ ProcMapsIterator it(0, &proc_self_maps_); // 0 means "current pid"
+
+ uint64 start, end, offset;
+ int64 inode;
+ char *flags, *filename;
+ CHECK(map_size_ == 0);
+ while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
+ CHECK(map_size_ < kMaxProcSelfMapsSize);
+ Mapping &mapping = memory_map[map_size_];
+ mapping.beg = start;
+ mapping.end = end;
+ mapping.offset = offset;
+ real_strncpy(mapping.name,
+ filename, ASAN_ARRAY_SIZE(mapping.name));
+ mapping.name[ASAN_ARRAY_SIZE(mapping.name) - 1] = 0;
+ if (FLAG_v >= 2) {
+ Printf("[%ld] [%p,%p] off %p %s\n", map_size_,
+ mapping.beg, mapping.end, mapping.offset, mapping.name);
+ }
+ map_size_++;
+ }
+ }
+
+ void Print() {
+ Printf("%s\n", proc_self_maps_);
+ }
+
+ void PrintPc(uintptr_t pc, int idx) {
+ for (size_t i = 0; i < map_size_; i++) {
+ Mapping &m = memory_map[i];
+ if (pc >= m.beg && pc < m.end) {
+ uintptr_t offset = pc - m.beg;
+ if (i == 0) offset = pc;
+ Printf(" #%d 0x%lx (%s+0x%lx)\n", idx, pc, m.name, offset);
+ return;
+ }
+ }
+ Printf(" #%d 0x%lx\n", idx, pc);
+ }
+
+ private:
+ void copy_until_new_line(const char *str, char *dest, size_t max_size) {
+ size_t i = 0;
+ for (; str[i] && str[i] != '\n' && i < max_size - 1; i++) {
+ dest[i] = str[i];
+ }
+ dest[i] = 0;
+ }
+
+
+ struct Mapping {
+ uintptr_t beg, end, offset;
+ char name[1000];
+ };
+ static const size_t kMaxNumMapEntries = 4096;
+ static const size_t kMaxProcSelfMapsSize = 1 << 20;
+ ProcMapsIterator::Buffer proc_self_maps_;
+ size_t map_size_;
+ Mapping memory_map[kMaxNumMapEntries];
+
+ static AsanLock mu_;
+};
+
+static ProcSelfMaps proc_self_maps;
+AsanLock ProcSelfMaps::mu_(LINKER_INITIALIZED);
+
+
+void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
+ proc_self_maps.Init();
+ for (size_t i = 0; i < size && addr[i]; i++) {
+ uintptr_t pc = addr[i];
+ // int line;
+ proc_self_maps.PrintPc(pc, i);
+ // Printf(" #%ld 0x%lx %s\n", i, pc, rtn.c_str());
+ }
+}
+#elif defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
+void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
+ for (size_t i = 0; i < size && addr[i]; i++) {
+ uintptr_t pc = addr[i];
+ char buff[4096];
+ ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
+ Printf(" #%ld 0x%lx %s\n", i, pc, buff);
+ }
+}
+
+#else // ASAN_USE_SYSINFO
+void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
+ for (size_t i = 0; i < size && addr[i]; i++) {
+ uintptr_t pc = addr[i];
+ Printf(" #%ld 0x%lx\n", i, pc);
+ }
+}
+#endif // ASAN_USE_SYSINFO
+
+#ifdef __arm__
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_OK
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+// ----------------------- AsanStackTrace ----------------------------- {{{1
+uintptr_t AsanStackTrace::GetCurrentPc() {
+ return GET_CALLER_PC();
+}
+
+void AsanStackTrace::FastUnwindStack(uintptr_t pc, uintptr_t bp) {
+ CHECK(size == 0 && trace[0] == pc);
+ size = 1;
+ if (!asan_inited) return;
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (!t) return;
+ uintptr_t *frame = (uintptr_t*)bp;
+ uintptr_t *prev_frame = frame;
+ uintptr_t *top = (uintptr_t*)t->stack_top();
+ uintptr_t *bottom = (uintptr_t*)t->stack_bottom();
+ while (frame >= prev_frame &&
+ frame < top &&
+ frame > bottom &&
+ size < max_size) {
+ uintptr_t pc1 = frame[1];
+ if (pc1 != pc) {
+ trace[size++] = pc1;
+ }
+ prev_frame = frame;
+ frame = (uintptr_t*)frame[0];
+ }
+}
+
+// On 32-bits we don't compress stack traces.
+// On 64-bits we compress stack traces: if a given pc differes slightly from
+// the previous one, we record a 31-bit offset instead of the full pc.
+size_t AsanStackTrace::CompressStack(AsanStackTrace *stack,
+ uint32_t *compressed, size_t size) {
+#if __WORDSIZE == 32
+ // Don't compress, just copy.
+ size_t res = 0;
+ for (size_t i = 0; i < stack->size && i < size; i++) {
+ compressed[i] = stack->trace[i];
+ res++;
+ }
+ if (stack->size < size)
+ compressed[stack->size] = 0;
+#else // 64 bits, compress.
+ uintptr_t prev_pc = 0;
+ const uintptr_t kMaxOffset = (1ULL << 30) - 1;
+ uintptr_t c_index = 0;
+ size_t res = 0;
+ for (size_t i = 0, n = stack->size; i < n; i++) {
+ uintptr_t pc = stack->trace[i];
+ if (!pc) break;
+ if ((int64_t)pc < 0) break;
+ // Printf("C pc[%ld] %lx\n", i, pc);
+ if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
+ uintptr_t offset = (int64_t)(pc - prev_pc);
+ offset |= (1U << 31);
+ if (c_index >= size) break;
+ // Printf("C co[%ld] offset %lx\n", i, offset);
+ compressed[c_index++] = offset;
+ } else {
+ uintptr_t hi = pc >> 32;
+ uintptr_t lo = (pc << 32) >> 32;
+ CHECK((hi & (1 << 31)) == 0);
+ if (c_index + 1 >= size) break;
+ // Printf("C co[%ld] hi/lo: %lx %lx\n", c_index, hi, lo);
+ compressed[c_index++] = hi;
+ compressed[c_index++] = lo;
+ }
+ res++;
+ prev_pc = pc;
+ }
+ if (c_index < size)
+ compressed[c_index] = 0;
+ if (c_index + 1 < size)
+ compressed[c_index + 1] = 0;
+#endif // __WORDSIZE
+
+ // debug-only code
+#if 0
+ AsanStackTrace check_stack;
+ UncompressStack(&check_stack, compressed, size);
+ if (res < check_stack.size) {
+ Printf("res %ld check_stack.size %ld; c_size %ld\n", res,
+ check_stack.size, size);
+ }
+ // |res| may be greater than check_stack.size, because
+ // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
+ CHECK(res >= check_stack.size);
+ CHECK(0 == real_memcmp(check_stack.trace, stack->trace,
+ check_stack.size * sizeof(uintptr_t)));
+#endif
+
+ return res;
+}
+
+void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
+ uint32_t *compressed, size_t size) {
+#if __WORDSIZE == 32
+ // Don't uncompress, just copy.
+ stack->size = 0;
+ for (size_t i = 0; i < size && i < kStackTraceMax; i++) {
+ if (!compressed[i]) break;
+ stack->size++;
+ stack->trace[i] = compressed[i];
+ }
+#else // 64 bits, uncompress
+ uintptr_t prev_pc = 0;
+ stack->size = 0;
+ for (size_t i = 0; i < size && stack->size < kStackTraceMax; i++) {
+ uint32_t x = compressed[i];
+ uintptr_t pc = 0;
+ if (x & (1U << 31)) {
+ // Printf("U co[%ld] offset: %x\n", i, x);
+ // this is an offset
+ int32_t offset = x;
+ offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend.
+ pc = prev_pc + offset;
+ CHECK(pc);
+ } else {
+ // CHECK(i + 1 < size);
+ if (i + 1 >= size) break;
+ uintptr_t hi = x;
+ uintptr_t lo = compressed[i+1];
+ // Printf("U co[%ld] hi/lo: %lx %lx\n", i, hi, lo);
+ i++;
+ pc = (hi << 32) | lo;
+ if (!pc) break;
+ }
+ // Printf("U pc[%ld] %lx\n", stack->size, pc);
+ stack->trace[stack->size++] = pc;
+ prev_pc = pc;
+ }
+#endif // __WORDSIZE
+}
+
+} // namespace __asan
diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h
new file mode 100644
index 0000000..97aefd6
--- /dev/null
+++ b/lib/asan/asan_stack.h
@@ -0,0 +1,94 @@
+//===-- asan_stack.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_stack.cc.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_STACK_H
+#define ASAN_STACK_H
+
+#include "asan_internal.h"
+
+namespace __asan {
+
+static const size_t kStackTraceMax = 64;
+
+struct AsanStackTrace {
+ size_t size;
+ size_t max_size;
+ uintptr_t trace[kStackTraceMax];
+ static void PrintStack(uintptr_t *addr, size_t size);
+ void PrintStack() {
+ PrintStack(this->trace, this->size);
+ }
+ void CopyTo(uintptr_t *dst, size_t dst_size) {
+ for (size_t i = 0; i < size && i < dst_size; i++)
+ dst[i] = trace[i];
+ for (size_t i = size; i < dst_size; i++)
+ dst[i] = 0;
+ }
+
+ void CopyFrom(uintptr_t *src, size_t src_size) {
+ size = src_size;
+ if (size > kStackTraceMax) size = kStackTraceMax;
+ for (size_t i = 0; i < size; i++) {
+ trace[i] = src[i];
+ }
+ }
+
+ void FastUnwindStack(uintptr_t pc, uintptr_t bp);
+// static _Unwind_Reason_Code Unwind_Trace(
+// struct _Unwind_Context *ctx, void *param);
+ static uintptr_t GetCurrentPc();
+
+ static size_t CompressStack(AsanStackTrace *stack,
+ uint32_t *compressed, size_t size);
+ static void UncompressStack(AsanStackTrace *stack,
+ uint32_t *compressed, size_t size);
+ size_t full_frame_count;
+};
+
+} // namespace __asan
+
+// Get the stack trace with the given pc and bp.
+// The pc will be in the position 0 of the resulting stack trace.
+// The bp may refer to the current frame or to the caller's frame.
+// fast_unwind is currently unused.
+#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, fast_unwind, pc, bp) \
+ AsanStackTrace stack; \
+ { \
+ uintptr_t saved_pc = pc; \
+ uintptr_t saved_bp = bp; \
+ stack.size = 0; \
+ stack.full_frame_count = 0; \
+ stack.trace[0] = saved_pc; \
+ if ((max_s) > 1) { \
+ stack.max_size = max_s; \
+ stack.FastUnwindStack(saved_pc, saved_bp); \
+ } \
+ } \
+
+#define GET_STACK_TRACE_HERE(max_size, fast_unwind) \
+ GET_STACK_TRACE_WITH_PC_AND_BP(max_size, fast_unwind, \
+ AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) \
+
+#define GET_STACK_TRACE_HERE_FOR_MALLOC \
+ GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind)
+
+#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \
+ GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind)
+
+#define PRINT_CURRENT_STACK() \
+ { \
+ GET_STACK_TRACE_HERE(kStackTraceMax, false); \
+ stack.PrintStack(); \
+ } \
+
+#endif // ASAN_STACK_H
diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc
new file mode 100644
index 0000000..3e4d1b4
--- /dev/null
+++ b/lib/asan/asan_stats.cc
@@ -0,0 +1,88 @@
+//===-- asan_stats.cc -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Code related to statistics collected by AddressSanitizer.
+//===----------------------------------------------------------------------===//
+#include "asan_interceptors.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_lock.h"
+#include "asan_stats.h"
+#include "asan_thread_registry.h"
+
+namespace __asan {
+
+AsanStats::AsanStats() {
+ CHECK(real_memset != NULL);
+ real_memset(this, 0, sizeof(AsanStats));
+}
+
+static void PrintMallocStatsArray(const char *prefix,
+ size_t (&array)[kNumberOfSizeClasses]) {
+ Printf("%s", prefix);
+ for (size_t i = 0; i < kNumberOfSizeClasses; i++) {
+ if (!array[i]) continue;
+ Printf("%ld:%ld; ", i, array[i]);
+ }
+ Printf("\n");
+}
+
+void AsanStats::Print() {
+ Printf("Stats: %ldM malloced (%ldM for red zones) by %ld calls\n",
+ malloced>>20, malloced_redzones>>20, mallocs);
+ Printf("Stats: %ldM realloced by %ld calls\n", realloced>>20, reallocs);
+ Printf("Stats: %ldM freed by %ld calls\n", freed>>20, frees);
+ Printf("Stats: %ldM really freed by %ld calls\n",
+ really_freed>>20, real_frees);
+ Printf("Stats: %ldM (%ld full pages) mmaped in %ld calls\n",
+ mmaped>>20, mmaped / kPageSize, mmaps);
+
+ PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
+ PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
+ PrintMallocStatsArray(" frees by size class: ", freed_by_size);
+ PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
+ Printf("Stats: malloc large: %ld small slow: %ld\n",
+ malloc_large, malloc_small_slow);
+}
+
+static AsanLock print_lock(LINKER_INITIALIZED);
+
+static void PrintAccumulatedStats() {
+ AsanStats stats = asanThreadRegistry().GetAccumulatedStats();
+ // Use lock to keep reports from mixing up.
+ ScopedLock lock(&print_lock);
+ stats.Print();
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+size_t __asan_get_current_allocated_bytes() {
+ return asanThreadRegistry().GetCurrentAllocatedBytes();
+}
+
+size_t __asan_get_heap_size() {
+ return asanThreadRegistry().GetHeapSize();
+}
+
+size_t __asan_get_free_bytes() {
+ return asanThreadRegistry().GetFreeBytes();
+}
+
+size_t __asan_get_unmapped_bytes() {
+ return 0;
+}
+
+void __asan_print_accumulated_stats() {
+ PrintAccumulatedStats();
+}
diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h
new file mode 100644
index 0000000..d6dd084
--- /dev/null
+++ b/lib/asan/asan_stats.h
@@ -0,0 +1,59 @@
+//===-- asan_stats.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for statistics.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_STATS_H
+#define ASAN_STATS_H
+
+#include "asan_allocator.h"
+#include "asan_internal.h"
+
+namespace __asan {
+
+// AsanStats struct is NOT thread-safe.
+// Each AsanThread has its own AsanStats, which are sometimes flushed
+// to the accumulated AsanStats.
+struct AsanStats {
+ // AsanStats must be a struct consisting of size_t fields only.
+ // When merging two AsanStats structs, we treat them as arrays of size_t.
+ size_t mallocs;
+ size_t malloced;
+ size_t malloced_redzones;
+ size_t frees;
+ size_t freed;
+ size_t real_frees;
+ size_t really_freed;
+ size_t really_freed_redzones;
+ size_t reallocs;
+ size_t realloced;
+ size_t mmaps;
+ size_t mmaped;
+ size_t mmaped_by_size[kNumberOfSizeClasses];
+ size_t malloced_by_size[kNumberOfSizeClasses];
+ size_t freed_by_size[kNumberOfSizeClasses];
+ size_t really_freed_by_size[kNumberOfSizeClasses];
+
+ size_t malloc_large;
+ size_t malloc_small_slow;
+
+ // Ctor for global AsanStats (accumulated stats and main thread stats).
+ explicit AsanStats(LinkerInitialized) { }
+ // Default ctor for thread-local stats.
+ AsanStats();
+
+ // Prints formatted stats to stderr.
+ void Print();
+};
+
+} // namespace __asan
+
+#endif // ASAN_STATS_H
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
new file mode 100644
index 0000000..329197d
--- /dev/null
+++ b/lib/asan/asan_thread.cc
@@ -0,0 +1,178 @@
+//===-- asan_thread.cc ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Thread-related code.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "asan_mapping.h"
+
+#if ASAN_USE_SYSINFO == 1
+#include "sysinfo/sysinfo.h"
+#endif
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace __asan {
+
+AsanThread::AsanThread(LinkerInitialized x)
+ : fake_stack_(x),
+ malloc_storage_(x),
+ stats_(x) { }
+
+AsanThread::AsanThread(int parent_tid, void *(*start_routine) (void *),
+ void *arg, AsanStackTrace *stack)
+ : start_routine_(start_routine),
+ arg_(arg) {
+ asanThreadRegistry().RegisterThread(this, parent_tid, stack);
+}
+
+AsanThread::~AsanThread() {
+ asanThreadRegistry().UnregisterThread(this);
+ fake_stack().Cleanup();
+ // We also clear the shadow on thread destruction because
+ // some code may still be executing in later TSD destructors
+ // and we don't want it to have any poisoned stack.
+ ClearShadowForThreadStack();
+}
+
+void AsanThread::ClearShadowForThreadStack() {
+ uintptr_t shadow_bot = MemToShadow(stack_bottom_);
+ uintptr_t shadow_top = MemToShadow(stack_top_);
+ real_memset((void*)shadow_bot, 0, shadow_top - shadow_bot);
+}
+
+void AsanThread::Init() {
+ SetThreadStackTopAndBottom();
+ fake_stack_.Init(stack_size());
+ if (FLAG_v >= 1) {
+ int local = 0;
+ Report("T%d: stack [%p,%p) size 0x%lx; local=%p, pthread_self=%p\n",
+ tid(), stack_bottom_, stack_top_,
+ stack_top_ - stack_bottom_, &local, pthread_self());
+ }
+
+ CHECK(AddrIsInMem(stack_bottom_));
+ CHECK(AddrIsInMem(stack_top_));
+
+ ClearShadowForThreadStack();
+}
+
+void *AsanThread::ThreadStart() {
+ Init();
+
+ if (!start_routine_) {
+ // start_routine_ == NULL if we're on the main thread or on one of the
+ // OS X libdispatch worker threads. But nobody is supposed to call
+ // ThreadStart() for the worker threads.
+ CHECK(tid() == 0);
+ return 0;
+ }
+
+ void *res = start_routine_(arg_);
+ malloc_storage().CommitBack();
+
+ if (FLAG_v >= 1) {
+ Report("T%d exited\n", tid());
+ }
+
+ return res;
+}
+
+const char *AsanThread::GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset) {
+ uintptr_t bottom = 0;
+ bool is_fake_stack = false;
+ if (AddrIsInStack(addr)) {
+ bottom = stack_bottom();
+ } else {
+ bottom = fake_stack().AddrIsInFakeStack(addr);
+ CHECK(bottom);
+ is_fake_stack = true;
+ }
+ uintptr_t aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr.
+ uintptr_t *ptr = (uintptr_t*)aligned_addr;
+ while (ptr >= (uintptr_t*)bottom) {
+ if (ptr[0] == kCurrentStackFrameMagic ||
+ (is_fake_stack && ptr[0] == kRetiredStackFrameMagic)) {
+ *offset = addr - (uintptr_t)ptr;
+ return (const char*)ptr[1];
+ }
+ ptr--;
+ }
+ *offset = 0;
+ return "UNKNOWN";
+}
+
+void AsanThread::SetThreadStackTopAndBottom() {
+#ifdef __APPLE__
+ size_t stacksize = pthread_get_stacksize_np(pthread_self());
+ void *stackaddr = pthread_get_stackaddr_np(pthread_self());
+ stack_top_ = (uintptr_t)stackaddr;
+ stack_bottom_ = stack_top_ - stacksize;
+ int local;
+ CHECK(AddrIsInStack((uintptr_t)&local));
+#else
+#if ASAN_USE_SYSINFO == 1
+ if (tid() == 0) {
+ // This is the main thread. Libpthread may not be initialized yet.
+ struct rlimit rl;
+ CHECK(getrlimit(RLIMIT_STACK, &rl) == 0);
+
+ // Find the mapping that contains a stack variable.
+ ProcMapsIterator it(0);
+ uint64_t start, end;
+ uint64_t prev_end = 0;
+ while (it.Next(&start, &end, NULL, NULL, NULL, NULL)) {
+ if ((uintptr_t)&rl < end)
+ break;
+ prev_end = end;
+ }
+ CHECK((uintptr_t)&rl >= start && (uintptr_t)&rl < end);
+
+ // Get stacksize from rlimit, but clip it so that it does not overlap
+ // with other mappings.
+ size_t stacksize = rl.rlim_cur;
+ if (stacksize > end - prev_end)
+ stacksize = end - prev_end;
+ if (stacksize > kMaxThreadStackSize)
+ stacksize = kMaxThreadStackSize;
+ stack_top_ = end;
+ stack_bottom_ = end - stacksize;
+ CHECK(AddrIsInStack((uintptr_t)&rl));
+ return;
+ }
+#endif
+ pthread_attr_t attr;
+ CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
+ size_t stacksize = 0;
+ void *stackaddr = NULL;
+ pthread_attr_getstack(&attr, &stackaddr, &stacksize);
+ pthread_attr_destroy(&attr);
+
+ stack_top_ = (uintptr_t)stackaddr + stacksize;
+ stack_bottom_ = (uintptr_t)stackaddr;
+ // When running with unlimited stack size, we still want to set some limit.
+ // The unlimited stack size is caused by 'ulimit -s unlimited'.
+ // Also, for some reason, GNU make spawns subrocesses with unlimited stack.
+ if (stacksize > kMaxThreadStackSize) {
+ stack_bottom_ = stack_top_ - kMaxThreadStackSize;
+ }
+ CHECK(AddrIsInStack((uintptr_t)&attr));
+#endif
+}
+
+} // namespace __asan
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
new file mode 100644
index 0000000..c382c85
--- /dev/null
+++ b/lib/asan/asan_thread.h
@@ -0,0 +1,108 @@
+//===-- asan_thread.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_thread.cc.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_THREAD_H
+#define ASAN_THREAD_H
+
+#include "asan_allocator.h"
+#include "asan_internal.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+
+namespace __asan {
+
+const size_t kMaxThreadStackSize = 16 * (1 << 20); // 16M
+
+class AsanThread;
+
+// These objects are created for every thread and are never deleted,
+// so we can find them by tid even if the thread is long dead.
+class AsanThreadSummary {
+ public:
+ explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
+ AsanThreadSummary(int tid, int parent_tid, AsanStackTrace *stack)
+ : tid_(tid),
+ parent_tid_(parent_tid),
+ announced_(false) {
+ if (stack) {
+ stack_ = *stack;
+ }
+ thread_ = 0;
+ }
+ void Announce() {
+ if (tid_ == 0) return; // no need to announce the main thread.
+ if (!announced_) {
+ announced_ = true;
+ Printf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
+ stack_.PrintStack();
+ }
+ }
+ int tid() { return tid_; }
+ AsanThread *thread() { return thread_; }
+ void set_thread(AsanThread *thread) { thread_ = thread; }
+ private:
+ int tid_;
+ int parent_tid_;
+ bool announced_;
+ AsanStackTrace stack_;
+ AsanThread *thread_;
+};
+
+// AsanThread are stored in TSD and destroyed when the thread dies.
+class AsanThread {
+ public:
+ explicit AsanThread(LinkerInitialized); // for T0.
+ AsanThread(int parent_tid, void *(*start_routine) (void *),
+ void *arg, AsanStackTrace *stack);
+ ~AsanThread();
+
+ void Init(); // Should be called from the thread itself.
+ void *ThreadStart();
+
+ uintptr_t stack_top() { return stack_top_; }
+ uintptr_t stack_bottom() { return stack_bottom_; }
+ size_t stack_size() { return stack_top_ - stack_bottom_; }
+ int tid() { return summary_->tid(); }
+ AsanThreadSummary *summary() { return summary_; }
+ void set_summary(AsanThreadSummary *summary) { summary_ = summary; }
+
+ const char *GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset);
+
+ bool AddrIsInStack(uintptr_t addr) {
+ return addr >= stack_bottom_ && addr < stack_top_;
+ }
+
+ FakeStack &fake_stack() { return fake_stack_; }
+ AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
+ AsanStats &stats() { return stats_; }
+
+ static const int kInvalidTid = -1;
+
+ private:
+
+ void SetThreadStackTopAndBottom();
+ void ClearShadowForThreadStack();
+ AsanThreadSummary *summary_;
+ void *(*start_routine_) (void *param);
+ void *arg_;
+ uintptr_t stack_top_;
+ uintptr_t stack_bottom_;
+
+ FakeStack fake_stack_;
+ AsanThreadLocalMallocStorage malloc_storage_;
+ AsanStats stats_;
+};
+
+} // namespace __asan
+
+#endif // ASAN_THREAD_H
diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc
new file mode 100644
index 0000000..39fba44
--- /dev/null
+++ b/lib/asan/asan_thread_registry.cc
@@ -0,0 +1,227 @@
+//===-- asan_thread_registry.cc ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// AsanThreadRegistry-related code. AsanThreadRegistry is a container
+// for summaries of all created threads.
+//===----------------------------------------------------------------------===//
+
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+
+#include <limits.h>
+
+namespace __asan {
+
+static AsanThreadRegistry asan_thread_registry(__asan::LINKER_INITIALIZED);
+
+AsanThreadRegistry &asanThreadRegistry() {
+ return asan_thread_registry;
+}
+
+#ifdef ANDROID
+#ifndef PTHREAD_DESTRUCTOR_ITERATIONS
+#define PTHREAD_DESTRUCTOR_ITERATIONS 4
+#endif
+#endif
+
+// Dark magic below. In order to be able to notice that we're not handling
+// some thread creation routines (e.g. on Mac OS) we want to distinguish the
+// thread that used to have a corresponding AsanThread object from the thread
+// that never had one. That's why upon AsanThread destruction we set the
+// pthread_key value to some odd number (that's not a valid pointer), instead
+// of NULL.
+// Because the TSD destructor for a non-NULL key value is called iteratively,
+// we increase the value by two, keeping it an invalid pointer.
+// Because the TSD implementations are allowed to call such a destructor
+// infinitely (see
+// http://pubs.opengroup.org/onlinepubs/009604499/functions/pthread_key_create.html
+// ), we exit the program after a certain number of iterations.
+static void DestroyAsanTsd(void *tsd) {
+ intptr_t iter = (intptr_t)tsd;
+ if (iter % 2 == 0) {
+ // The pointer is valid.
+ AsanThread *t = (AsanThread*)tsd;
+ if (t != asanThreadRegistry().GetMain()) {
+ delete t;
+ }
+ iter = 1;
+ } else {
+ // The pointer is invalid -- we've already destroyed the TSD before.
+ // If |iter| is too big, we're in the infinite loop. This should be
+ // impossible on the systems AddressSanitizer was tested on.
+ CHECK(iter < 4 * PTHREAD_DESTRUCTOR_ITERATIONS);
+ iter += 2;
+ }
+ CHECK(0 == pthread_setspecific(asanThreadRegistry().GetTlsKey(),
+ (void*)iter));
+ if (FLAG_v >= 2) {
+ Report("DestroyAsanTsd: writing %p to the TSD slot of thread %p\n",
+ (void*)iter, pthread_self());
+ }
+}
+
+AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
+ : main_thread_(x),
+ main_thread_summary_(x),
+ accumulated_stats_(x),
+ mu_(x) { }
+
+void AsanThreadRegistry::Init() {
+ CHECK(0 == pthread_key_create(&tls_key_, DestroyAsanTsd));
+ tls_key_created_ = true;
+ SetCurrent(&main_thread_);
+ main_thread_.set_summary(&main_thread_summary_);
+ main_thread_summary_.set_thread(&main_thread_);
+ thread_summaries_[0] = &main_thread_summary_;
+ n_threads_ = 1;
+}
+
+void AsanThreadRegistry::RegisterThread(AsanThread *thread, int parent_tid,
+ AsanStackTrace *stack) {
+ ScopedLock lock(&mu_);
+ CHECK(n_threads_ > 0);
+ int tid = n_threads_;
+ n_threads_++;
+ CHECK(n_threads_ < kMaxNumberOfThreads);
+ AsanThreadSummary *summary = new AsanThreadSummary(tid, parent_tid, stack);
+ summary->set_thread(thread);
+ thread_summaries_[tid] = summary;
+ thread->set_summary(summary);
+}
+
+void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
+ ScopedLock lock(&mu_);
+ FlushToAccumulatedStatsUnlocked(&thread->stats());
+ AsanThreadSummary *summary = thread->summary();
+ CHECK(summary);
+ summary->set_thread(NULL);
+}
+
+AsanThread *AsanThreadRegistry::GetMain() {
+ return &main_thread_;
+}
+
+AsanThread *AsanThreadRegistry::GetCurrent() {
+ CHECK(tls_key_created_);
+ AsanThread *thread = (AsanThread*)pthread_getspecific(tls_key_);
+ if ((!thread || (intptr_t)thread % 2) && FLAG_v >= 2) {
+ Report("GetCurrent: %p for thread %p\n", thread, pthread_self());
+ }
+ if ((intptr_t)thread % 2) {
+ // Invalid pointer -- we've deleted the AsanThread already. Return NULL as
+ // if the TSD was empty.
+ // TODO(glider): if the code in the client TSD destructor calls
+ // pthread_create(), we'll set the parent tid of the spawned thread to NULL,
+ // although the creation stack will belong to the current thread. This may
+ // confuse the user, but is quite unlikely.
+ return NULL;
+ } else {
+ // NULL or valid pointer to AsanThread.
+ return thread;
+ }
+}
+
+void AsanThreadRegistry::SetCurrent(AsanThread *t) {
+ if (FLAG_v >=2) {
+ Report("SetCurrent: %p for thread %p\n", t, pthread_self());
+ }
+ // Make sure we do not reset the current AsanThread.
+ intptr_t old_key = (intptr_t)pthread_getspecific(tls_key_);
+ CHECK(!old_key || old_key % 2);
+ CHECK(0 == pthread_setspecific(tls_key_, t));
+ CHECK(pthread_getspecific(tls_key_) == t);
+}
+
+pthread_key_t AsanThreadRegistry::GetTlsKey() {
+ return tls_key_;
+}
+
+// Returns true iff DestroyAsanTsd() was already called for this thread.
+bool AsanThreadRegistry::IsCurrentThreadDying() {
+ CHECK(tls_key_created_);
+ intptr_t thread = (intptr_t)pthread_getspecific(tls_key_);
+ return (bool)(thread % 2);
+}
+
+AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
+ AsanThread *t = GetCurrent();
+ return (t) ? t->stats() : main_thread_.stats();
+}
+
+AsanStats AsanThreadRegistry::GetAccumulatedStats() {
+ ScopedLock lock(&mu_);
+ UpdateAccumulatedStatsUnlocked();
+ return accumulated_stats_;
+}
+
+size_t AsanThreadRegistry::GetCurrentAllocatedBytes() {
+ ScopedLock lock(&mu_);
+ UpdateAccumulatedStatsUnlocked();
+ return accumulated_stats_.malloced - accumulated_stats_.freed;
+}
+
+size_t AsanThreadRegistry::GetHeapSize() {
+ ScopedLock lock(&mu_);
+ UpdateAccumulatedStatsUnlocked();
+ return accumulated_stats_.mmaped;
+}
+
+size_t AsanThreadRegistry::GetFreeBytes() {
+ ScopedLock lock(&mu_);
+ UpdateAccumulatedStatsUnlocked();
+ return accumulated_stats_.mmaped
+ - accumulated_stats_.malloced
+ - accumulated_stats_.malloced_redzones
+ + accumulated_stats_.really_freed
+ + accumulated_stats_.really_freed_redzones;
+}
+
+AsanThreadSummary *AsanThreadRegistry::FindByTid(int tid) {
+ CHECK(tid >= 0);
+ CHECK(tid < n_threads_);
+ CHECK(thread_summaries_[tid]);
+ return thread_summaries_[tid];
+}
+
+AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uintptr_t addr) {
+ ScopedLock lock(&mu_);
+ for (int tid = 0; tid < n_threads_; tid++) {
+ AsanThread *t = thread_summaries_[tid]->thread();
+ if (!t) continue;
+ if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) {
+ return t;
+ }
+ }
+ return 0;
+}
+
+void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
+ for (int tid = 0; tid < n_threads_; tid++) {
+ AsanThread *t = thread_summaries_[tid]->thread();
+ if (t != NULL) {
+ FlushToAccumulatedStatsUnlocked(&t->stats());
+ }
+ }
+}
+
+void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
+ // AsanStats consists of variables of type size_t only.
+ size_t *dst = (size_t*)&accumulated_stats_;
+ size_t *src = (size_t*)stats;
+ size_t num_fields = sizeof(AsanStats) / sizeof(size_t);
+ for (size_t i = 0; i < num_fields; i++) {
+ dst[i] += src[i];
+ src[i] = 0;
+ }
+}
+
+} // namespace __asan
diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h
new file mode 100644
index 0000000..b80dd4d
--- /dev/null
+++ b/lib/asan/asan_thread_registry.h
@@ -0,0 +1,88 @@
+//===-- asan_thread_registry.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_thread_registry.cc
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_THREAD_REGISTRY_H
+#define ASAN_THREAD_REGISTRY_H
+
+#include "asan_lock.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_thread.h"
+
+namespace __asan {
+
+// Stores summaries of all created threads, returns current thread,
+// thread by tid, thread by stack address. There is a single instance
+// of AsanThreadRegistry for the whole program.
+// AsanThreadRegistry is thread-safe.
+class AsanThreadRegistry {
+ public:
+ explicit AsanThreadRegistry(LinkerInitialized);
+ void Init();
+ void RegisterThread(AsanThread *thread, int parent_tid,
+ AsanStackTrace *stack);
+ void UnregisterThread(AsanThread *thread);
+
+ AsanThread *GetMain();
+ // Get the current thread. May return NULL.
+ AsanThread *GetCurrent();
+ void SetCurrent(AsanThread *t);
+ pthread_key_t GetTlsKey();
+ bool IsCurrentThreadDying();
+
+ int GetCurrentTidOrMinusOne() {
+ AsanThread *t = GetCurrent();
+ return t ? t->tid() : -1;
+ }
+
+ // Returns stats for GetCurrent(), or stats for
+ // T0 if GetCurrent() returns NULL.
+ AsanStats &GetCurrentThreadStats();
+ // Flushes all thread-local stats to accumulated stats, and returns
+ // a copy of accumulated stats.
+ AsanStats GetAccumulatedStats();
+ size_t GetCurrentAllocatedBytes();
+ size_t GetHeapSize();
+ size_t GetFreeBytes();
+
+ AsanThreadSummary *FindByTid(int tid);
+ AsanThread *FindThreadByStackAddress(uintptr_t addr);
+
+ private:
+ void UpdateAccumulatedStatsUnlocked();
+ // Adds values of all counters in "stats" to accumulated stats,
+ // and fills "stats" with zeroes.
+ void FlushToAccumulatedStatsUnlocked(AsanStats *stats);
+
+ static const int kMaxNumberOfThreads = (1 << 22); // 4M
+ AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads];
+ AsanThread main_thread_;
+ AsanThreadSummary main_thread_summary_;
+ AsanStats accumulated_stats_;
+ int n_threads_;
+ AsanLock mu_;
+ // For each thread tls_key_ stores the pointer to the corresponding
+ // AsanThread.
+ pthread_key_t tls_key_;
+ // This flag is updated only once at program startup, and then read
+ // by concurrent threads.
+ bool tls_key_created_;
+};
+
+// Returns a single instance of registry.
+AsanThreadRegistry &asanThreadRegistry();
+
+} // namespace __asan
+
+#endif // ASAN_THREAD_REGISTRY_H
diff --git a/lib/asan/mach_override/LICENSE.TXT b/lib/asan/mach_override/LICENSE.TXT
new file mode 100644
index 0000000..9446965
--- /dev/null
+++ b/lib/asan/mach_override/LICENSE.TXT
@@ -0,0 +1,3 @@
+Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
diff --git a/lib/asan/mach_override/Makefile.mk b/lib/asan/mach_override/Makefile.mk
new file mode 100644
index 0000000..78be0b3
--- /dev/null
+++ b/lib/asan/mach_override/Makefile.mk
@@ -0,0 +1,22 @@
+#===- lib/asan/mach_override/Makefile.mk -------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := asan
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
+ObjNames := $(Sources:%.c=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+
+# Define a convenience variable for all the asan functions.
+AsanFunctions += $(Sources:%.c=%)
diff --git a/lib/asan/mach_override/README.txt b/lib/asan/mach_override/README.txt
new file mode 100644
index 0000000..5f62ad7
--- /dev/null
+++ b/lib/asan/mach_override/README.txt
@@ -0,0 +1,9 @@
+-- mach_override.c is taken from upstream version at
+ https://github.com/rentzsch/mach_star/tree/f8e0c424b5be5cb641ded67c265e616157ae4bcf
+-- Added debugging code under DEBUG_DISASM.
+-- The files are guarded with #ifdef __APPLE__
+-- some opcodes are added in order to parse the library functions on Lion
+-- fixupInstructions() is extended to relocate relative calls, not only jumps
+-- mach_override_ptr is renamed to __asan_mach_override_ptr and
+ other functions are marked as hidden.
+
diff --git a/lib/asan/mach_override/mach_override.c b/lib/asan/mach_override/mach_override.c
new file mode 100644
index 0000000..640d03d5
--- /dev/null
+++ b/lib/asan/mach_override/mach_override.c
@@ -0,0 +1,862 @@
+/*******************************************************************************
+ mach_override.c
+ Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+ Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
+ ***************************************************************************/
+#ifdef __APPLE__
+
+#include "mach_override.h"
+
+#include <mach-o/dyld.h>
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#include <sys/mman.h>
+
+#include <CoreServices/CoreServices.h>
+
+//#define DEBUG_DISASM 1
+#undef DEBUG_DISASM
+
+/**************************
+*
+* Constants
+*
+**************************/
+#pragma mark -
+#pragma mark (Constants)
+
+#if defined(__ppc__) || defined(__POWERPC__)
+
+long kIslandTemplate[] = {
+ 0x9001FFFC, // stw r0,-4(SP)
+ 0x3C00DEAD, // lis r0,0xDEAD
+ 0x6000BEEF, // ori r0,r0,0xBEEF
+ 0x7C0903A6, // mtctr r0
+ 0x8001FFFC, // lwz r0,-4(SP)
+ 0x60000000, // nop ; optionally replaced
+ 0x4E800420 // bctr
+};
+
+#define kAddressHi 3
+#define kAddressLo 5
+#define kInstructionHi 10
+#define kInstructionLo 11
+
+#elif defined(__i386__)
+
+#define kOriginalInstructionsSize 16
+
+char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
+};
+
+#define kInstructions 0
+#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
+#elif defined(__x86_64__)
+
+#define kOriginalInstructionsSize 32
+
+#define kJumpAddress kOriginalInstructionsSize + 6
+
+char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+#endif
+
+#define kAllocateHigh 1
+#define kAllocateNormal 0
+
+/**************************
+*
+* Data Types
+*
+**************************/
+#pragma mark -
+#pragma mark (Data Types)
+
+typedef struct {
+ char instructions[sizeof(kIslandTemplate)];
+ int allocatedHigh;
+} BranchIsland;
+
+/**************************
+*
+* Funky Protos
+*
+**************************/
+#pragma mark -
+#pragma mark (Funky Protos)
+
+ mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ int allocateHigh,
+ void *originalFunctionAddress) __attribute__((visibility("hidden")));
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island ) __attribute__((visibility("hidden")));
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction ) __attribute__((visibility("hidden")));
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions ) __attribute__((visibility("hidden")));
+void
+atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value ) __attribute__((visibility("hidden")));
+
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions,
+ int *originalInstructionCount,
+ uint8_t *originalInstructionSizes ) __attribute__((visibility("hidden")));
+
+ static void
+fixupInstructions(
+ void *originalFunction,
+ void *escapeIsland,
+ void *instructionsToFix,
+ int instructionCount,
+ uint8_t *instructionSizes ) __attribute__((visibility("hidden")));
+#endif
+
+/*******************************************************************************
+*
+* Interface
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Interface)
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t makeIslandExecutable(void *address) {
+ mach_error_t err = err_none;
+ vm_size_t pageSize;
+ host_page_size( mach_host_self(), &pageSize );
+ uintptr_t page = (uintptr_t)address & ~(uintptr_t)(pageSize-1);
+ int e = err_none;
+ e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
+ e |= msync((void *)page, pageSize, MS_INVALIDATE );
+ if (e) {
+ err = err_cannot_override;
+ }
+ return err;
+}
+#endif
+
+ mach_error_t
+__asan_mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland )
+{
+ assert( originalFunctionAddress );
+ assert( overrideFunctionAddress );
+
+ // this addresses overriding such functions as AudioOutputUnitStart()
+ // test with modified DefaultOutputUnit project
+#if defined(__x86_64__)
+ for(;;){
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
+ originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
+ else break;
+ }
+#elif defined(__i386__)
+ for(;;){
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
+ originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
+ else break;
+ }
+#endif
+#ifdef DEBUG_DISASM
+ {
+ fprintf(stderr, "Replacing function at %p\n", originalFunctionAddress);
+ fprintf(stderr, "First 16 bytes of the function: ");
+ unsigned char *orig = (unsigned char *)originalFunctionAddress;
+ int i;
+ for (i = 0; i < 16; i++) {
+ fprintf(stderr, "%x ", (unsigned int) orig[i]);
+ }
+ fprintf(stderr, "\n");
+ fprintf(stderr,
+ "To disassemble, save the following function as disas.c"
+ " and run:\n gcc -c disas.c && gobjdump -d disas.o\n"
+ "The first 16 bytes of the original function will start"
+ " after four nop instructions.\n");
+ fprintf(stderr, "\nvoid foo() {\n asm volatile(\"nop;nop;nop;nop;\");\n");
+ int j = 0;
+ for (j = 0; j < 2; j++) {
+ fprintf(stderr, " asm volatile(\".byte ");
+ for (i = 8 * j; i < 8 * (j+1) - 1; i++) {
+ fprintf(stderr, "0x%x, ", (unsigned int) orig[i]);
+ }
+ fprintf(stderr, "0x%x;\");\n", (unsigned int) orig[8 * (j+1) - 1]);
+ }
+ fprintf(stderr, "}\n\n");
+ }
+#endif
+
+ long *originalFunctionPtr = (long*) originalFunctionAddress;
+ mach_error_t err = err_none;
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Ensure first instruction isn't 'mfctr'.
+ #define kMFCTRMask 0xfc1fffff
+ #define kMFCTRInstruction 0x7c0903a6
+
+ long originalInstruction = *originalFunctionPtr;
+ if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
+ err = err_cannot_override;
+#elif defined(__i386__) || defined(__x86_64__)
+ int eatenCount = 0;
+ int originalInstructionCount = 0;
+ char originalInstructions[kOriginalInstructionsSize];
+ uint8_t originalInstructionSizes[kOriginalInstructionsSize];
+ uint64_t jumpRelativeInstruction = 0; // JMP
+
+ Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
+ &jumpRelativeInstruction, &eatenCount,
+ originalInstructions, &originalInstructionCount,
+ originalInstructionSizes );
+#ifdef DEBUG_DISASM
+ if (!overridePossible) fprintf(stderr, "overridePossible = false @%d\n", __LINE__);
+#endif
+ if (eatenCount > kOriginalInstructionsSize) {
+#ifdef DEBUG_DISASM
+ fprintf(stderr, "Too many instructions eaten\n");
+#endif
+ overridePossible = false;
+ }
+ if (!overridePossible) err = err_cannot_override;
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+#endif
+
+ // Make the original function implementation writable.
+ if( !err ) {
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, 8, false,
+ (VM_PROT_ALL | VM_PROT_COPY) );
+ if( err )
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, 8, false,
+ (VM_PROT_DEFAULT | VM_PROT_COPY) );
+ }
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+ // Allocate and target the escape island to the overriding function.
+ BranchIsland *escapeIsland = NULL;
+ if( !err )
+ err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress );
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ if( !err )
+ err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
+
+ // Build the branch absolute instruction to the escape island.
+ long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
+ if( !err ) {
+ long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
+ branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+ if( !err )
+ err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
+
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+ // Build the jump relative instruction to the escape island
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (!err) {
+ uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
+ addressOffset = OSSwapInt32(addressOffset);
+
+ jumpRelativeInstruction |= 0xE900000000000000LL;
+ jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
+ jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
+ }
+#endif
+
+ // Optionally allocate & return the reentry island. This may contain relocated
+ // jmp instructions and so has all the same addressing reachability requirements
+ // the escape island has to the original function, except the escape island is
+ // technically our original function.
+ BranchIsland *reentryIsland = NULL;
+ if( !err && originalFunctionReentryIsland ) {
+ err = allocateBranchIsland( &reentryIsland, kAllocateHigh, escapeIsland);
+ if( !err )
+ *originalFunctionReentryIsland = reentryIsland;
+ }
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instruction into the reentry island.
+ // o Target the reentry island at the 2nd instruction of the
+ // original function.
+ // o Replace the original instruction with the branch absolute.
+ if( !err ) {
+ int escapeIslandEngaged = false;
+ do {
+ if( reentryIsland )
+ err = setBranchIslandTarget( reentryIsland,
+ (void*) (originalFunctionPtr+1), originalInstruction );
+ if( !err ) {
+ escapeIslandEngaged = CompareAndSwap( originalInstruction,
+ branchAbsoluteInstruction,
+ (UInt32*)originalFunctionPtr );
+ if( !escapeIslandEngaged ) {
+ // Someone replaced the instruction out from under us,
+ // re-read the instruction, make sure it's still not
+ // 'mfctr' and try again.
+ originalInstruction = *originalFunctionPtr;
+ if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
+ err = err_cannot_override;
+ }
+ }
+ } while( !err && !escapeIslandEngaged );
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instructions into the reentry island.
+ // o Target the reentry island at the first non-replaced
+ // instruction of the original function.
+ // o Replace the original first instructions with the jump relative.
+ //
+ // Note that on i386, we do not support someone else changing the code under our feet
+ if ( !err ) {
+ fixupInstructions(originalFunctionPtr, reentryIsland, originalInstructions,
+ originalInstructionCount, originalInstructionSizes );
+
+ if( reentryIsland )
+ err = setBranchIslandTarget_i386( reentryIsland,
+ (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
+ // try making islands executable before planting the jmp
+#if defined(__x86_64__) || defined(__i386__)
+ if( !err )
+ err = makeIslandExecutable(escapeIsland);
+ if( !err && reentryIsland )
+ err = makeIslandExecutable(reentryIsland);
+#endif
+ if ( !err )
+ atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
+ }
+#endif
+
+ // Clean up on error.
+ if( err ) {
+ if( reentryIsland )
+ freeBranchIsland( reentryIsland );
+ if( escapeIsland )
+ freeBranchIsland( escapeIsland );
+ }
+
+#ifdef DEBUG_DISASM
+ {
+ fprintf(stderr, "First 16 bytes of the function after slicing: ");
+ unsigned char *orig = (unsigned char *)originalFunctionAddress;
+ int i;
+ for (i = 0; i < 16; i++) {
+ fprintf(stderr, "%x ", (unsigned int) orig[i]);
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+ return err;
+}
+
+/*******************************************************************************
+*
+* Implementation
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Implementation)
+
+/***************************************************************************//**
+ Implementation: Allocates memory for a branch island.
+
+ @param island <- The allocated island.
+ @param allocateHigh -> Whether to allocate the island at the end of the
+ address space (for use with the branch absolute
+ instruction).
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+ mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ int allocateHigh,
+ void *originalFunctionAddress)
+{
+ assert( island );
+
+ mach_error_t err = err_none;
+
+ if( allocateHigh ) {
+ vm_size_t pageSize;
+ err = host_page_size( mach_host_self(), &pageSize );
+ if( !err ) {
+ assert( sizeof( BranchIsland ) <= pageSize );
+#if defined(__ppc__) || defined(__POWERPC__)
+ vm_address_t first = 0xfeffffff;
+ vm_address_t last = 0xfe000000 + pageSize;
+#elif defined(__x86_64__)
+ vm_address_t first = ((uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1)) | ((uint64_t)1 << 31); // start in the middle of the page?
+ vm_address_t last = 0x0;
+#else
+ vm_address_t first = 0xffc00000;
+ vm_address_t last = 0xfffe0000;
+#endif
+
+ vm_address_t page = first;
+ int allocated = 0;
+ vm_map_t task_self = mach_task_self();
+
+ while( !err && !allocated && page != last ) {
+
+ err = vm_allocate( task_self, &page, pageSize, 0 );
+ if( err == err_none )
+ allocated = 1;
+ else if( err == KERN_NO_SPACE ) {
+#if defined(__x86_64__)
+ page -= pageSize;
+#else
+ page += pageSize;
+#endif
+ err = err_none;
+ }
+ }
+ if( allocated )
+ *island = (BranchIsland*) page;
+ else if( !allocated && !err )
+ err = KERN_NO_SPACE;
+ }
+ } else {
+ void *block = malloc( sizeof( BranchIsland ) );
+ if( block )
+ *island = block;
+ else
+ err = KERN_NO_SPACE;
+ }
+ if( !err )
+ (**island).allocatedHigh = allocateHigh;
+
+ return err;
+}
+
+/***************************************************************************//**
+ Implementation: Deallocates memory for a branch island.
+
+ @param island -> The island to deallocate.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island )
+{
+ assert( island );
+ assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
+ assert( island->allocatedHigh );
+
+ mach_error_t err = err_none;
+
+ if( island->allocatedHigh ) {
+ vm_size_t pageSize;
+ err = host_page_size( mach_host_self(), &pageSize );
+ if( !err ) {
+ assert( sizeof( BranchIsland ) <= pageSize );
+ err = vm_deallocate(
+ mach_task_self(),
+ (vm_address_t) island, pageSize );
+ }
+ } else {
+ free( island );
+ }
+
+ return err;
+}
+
+/***************************************************************************//**
+ Implementation: Sets the branch island's target, with an optional
+ instruction.
+
+ @param island -> The branch island to insert target into.
+ @param branchTo -> The address of the target.
+ @param instruction -> Optional instruction to execute prior to branch. Set
+ to zero for nop.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Fill in the address.
+ ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
+ ((short*)island->instructions)[kAddressHi]
+ = (((long) branchTo) >> 16) & 0x0000FFFF;
+
+ // Fill in the (optional) instuction.
+ if( instruction != 0 ) {
+ ((short*)island->instructions)[kInstructionLo]
+ = instruction & 0x0000FFFF;
+ ((short*)island->instructions)[kInstructionHi]
+ = (instruction >> 16) & 0x0000FFFF;
+ }
+
+ //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+#if defined(__i386__)
+ mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // copy original instructions
+ if (instructions) {
+ bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
+ *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
+
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+ return err_none;
+}
+
+#elif defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Copy original instructions.
+ if (instructions) {
+ bcopy (instructions, island->instructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+// simplistic instruction matching
+typedef struct {
+ unsigned int length; // max 15
+ unsigned char mask[15]; // sequence of bytes in memory order
+ unsigned char constraint[15]; // sequence of bytes in memory order
+} AsmInstructionMatch;
+
+#if defined(__i386__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
+ { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %esp; mov %esp,%ebp; leave; ret
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xF8}, {0x50} }, // push %reg
+ { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x89, 0x1C, 0x24} }, // mov %ebx,(%esp)
+ { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
+ { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
+ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
+ { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
+ { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
+ { 0x3, {0xFF, 0xCF, 0x00}, {0x8B, 0x4D, 0x00} }, // mov $imm(%rpb), %reg
+ { 0x3, {0xFF, 0x4F, 0x00}, {0x8A, 0x4D, 0x00} }, // mov $imm(%ebp), %cl
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
+ { 0x4, {0xFF, 0x00, 0x00, 0x00}, {0x8B, 0x00, 0x00, 0x00} }, // mov r16,r/m16 or r32,r/m32
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB9, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %ecx
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
+ { 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE8, 0x00, 0x00, 0x00, 0x00} }, // call $imm
+ { 0x0 }
+};
+#elif defined(__x86_64__)
+// TODO(glider): disassembling the "0x48, 0x89" sequences is trickier than it's done below.
+// If it stops working, refer to http://ref.x86asm.net/geek.html#modrm_byte_32_64 to do it
+// more accurately.
+// Note: 0x48 is in fact the REX.W prefix, but it might be wrong to treat it as a separate
+// instruction.
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xF8}, {0x50} }, // push %rX
+ { 0x1, {0xFF}, {0x65} }, // GS prefix
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
+ { 0x4, {0xFB, 0xFF, 0x07, 0x00}, {0x48, 0x89, 0x05, 0x00} }, // move onto rbp
+ { 0x3, {0xFB, 0xFF, 0x00}, {0x48, 0x89, 0x00} }, // mov %reg, %reg
+ { 0x3, {0xFB, 0xFF, 0x00}, {0x49, 0x89, 0x00} }, // mov %reg, %reg (REX.WB)
+ { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
+ { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
+ { 0x2, {0xFF, 0x00}, {0x77, 0x00} }, // ja $i8
+ { 0x2, {0xFF, 0x00}, {0x74, 0x00} }, // je $i8
+ { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
+ { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
+ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %eax
+
+ { 0x8, {0xFF, 0xFF, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x00},
+ {0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00}, }, // mov $imm, %{rax,rdx,rsp,rsi}
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xFA, 0x00}, }, // cmp $i8, %rdx
+ { 0x4, {0xFF, 0xFF, 0x00, 0x00}, {0x83, 0x7f, 0x00, 0x00}, }, // cmpl $imm, $imm(%rdi)
+ { 0xa, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %rax
+ { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
+ {0x81, 0xE6, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %esi
+ { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
+ {0xFF, 0x25, 0x00, 0x00, 0x00, 0x00} }, // jmpq *(%rip)
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
+ { 0x2, {0xFF, 0x00}, {0x89, 0x00} }, // mov r/m32,r32 or r/m16,r16
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x49, 0x89, 0xF8} }, // mov %rdi,%r8
+ { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
+ { 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
+ { 0x0 }
+};
+#endif
+
+static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
+{
+ Boolean match = true;
+
+ size_t i;
+ assert(instruction);
+#ifdef DEBUG_DISASM
+ fprintf(stderr, "Matching: ");
+#endif
+ for (i=0; i<instruction->length; i++) {
+ unsigned char mask = instruction->mask[i];
+ unsigned char constraint = instruction->constraint[i];
+ unsigned char codeValue = code[i];
+#ifdef DEBUG_DISASM
+ fprintf(stderr, "%x ", (unsigned)codeValue);
+#endif
+ match = ((codeValue & mask) == constraint);
+ if (!match) break;
+ }
+#ifdef DEBUG_DISASM
+ if (match) {
+ fprintf(stderr, " OK\n");
+ } else {
+ fprintf(stderr, " FAIL\n");
+ }
+#endif
+ return match;
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions,
+ int *originalInstructionCount,
+ uint8_t *originalInstructionSizes )
+{
+ Boolean allInstructionsKnown = true;
+ int totalEaten = 0;
+ unsigned char* ptr = code;
+ int remainsToEat = 5; // a JMP instruction takes 5 bytes
+ int instructionIndex = 0;
+
+ if (howManyEaten) *howManyEaten = 0;
+ if (originalInstructionCount) *originalInstructionCount = 0;
+ while (remainsToEat > 0) {
+ Boolean curInstructionKnown = false;
+
+ // See if instruction matches one we know
+ AsmInstructionMatch* curInstr = possibleInstructions;
+ do {
+ if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
+ curInstr++;
+ } while (curInstr->length > 0);
+
+ // if all instruction matches failed, we don't know current instruction then, stop here
+ if (!curInstructionKnown) {
+ allInstructionsKnown = false;
+ fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
+ break;
+ }
+
+ // At this point, we've matched curInstr
+ int eaten = curInstr->length;
+ ptr += eaten;
+ remainsToEat -= eaten;
+ totalEaten += eaten;
+
+ if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
+ instructionIndex += 1;
+ if (originalInstructionCount) *originalInstructionCount = instructionIndex;
+ }
+
+
+ if (howManyEaten) *howManyEaten = totalEaten;
+
+ if (originalInstructions) {
+ Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
+
+ if (enoughSpaceForOriginalInstructions) {
+ memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
+ bcopy(code, originalInstructions, totalEaten);
+ } else {
+#ifdef DEBUG_DISASM
+ fprintf(stderr, "Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
+#endif
+ return false;
+ }
+ }
+
+ if (allInstructionsKnown) {
+ // save last 3 bytes of first 64bits of codre we'll replace
+ uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
+ currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
+ currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
+
+ // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
+ *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
+ *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
+ }
+
+ return allInstructionsKnown;
+}
+
+ static void
+fixupInstructions(
+ void *originalFunction,
+ void *escapeIsland,
+ void *instructionsToFix,
+ int instructionCount,
+ uint8_t *instructionSizes )
+{
+ int index;
+ for (index = 0;index < instructionCount;index += 1)
+ {
+ if ((*(uint8_t*)instructionsToFix == 0xE9) || // 32-bit jump relative
+ (*(uint8_t*)instructionsToFix == 0xE8)) // 32-bit call relative
+ {
+ uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland;
+ uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
+ *jumpOffsetPtr += offset;
+ }
+
+
+ originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]);
+ escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]);
+ instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
+ }
+}
+#endif
+
+#if defined(__i386__)
+__asm(
+ ".text;"
+ ".align 2, 0x90;"
+ "_atomic_mov64:;"
+ " pushl %ebp;"
+ " movl %esp, %ebp;"
+ " pushl %esi;"
+ " pushl %ebx;"
+ " pushl %ecx;"
+ " pushl %eax;"
+ " pushl %edx;"
+
+ // atomic push of value to an address
+ // we use cmpxchg8b, which compares content of an address with
+ // edx:eax. If they are equal, it atomically puts 64bit value
+ // ecx:ebx in address.
+ // We thus put contents of address in edx:eax to force ecx:ebx
+ // in address
+ " mov 8(%ebp), %esi;" // esi contains target address
+ " mov 12(%ebp), %ebx;"
+ " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
+ " mov (%esi), %eax;"
+ " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
+ " lock; cmpxchg8b (%esi);" // atomic move.
+
+ // restore registers
+ " popl %edx;"
+ " popl %eax;"
+ " popl %ecx;"
+ " popl %ebx;"
+ " popl %esi;"
+ " popl %ebp;"
+ " ret"
+);
+#elif defined(__x86_64__)
+void atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value )
+{
+ *targetAddress = value;
+}
+#endif
+#endif
+#endif // __APPLE__
diff --git a/lib/asan/mach_override/mach_override.h b/lib/asan/mach_override/mach_override.h
new file mode 100644
index 0000000..dcccbcd
--- /dev/null
+++ b/lib/asan/mach_override/mach_override.h
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ mach_override.h
+ Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+ Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
+ ***************************************************************************/
+
+/***************************************************************************//**
+ @mainpage mach_override
+ @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+
+ This package, coded in C to the Mach API, allows you to override ("patch")
+ program- and system-supplied functions at runtime. You can fully replace
+ functions with your implementations, or merely head- or tail-patch the
+ original implementations.
+
+ Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
+
+ @todo Discontinue use of Carbon's MakeDataExecutable() and
+ CompareAndSwap() calls and start using the Mach equivalents, if they
+ exist. If they don't, write them and roll them in. That way, this
+ code will be pure Mach, which will make it easier to use everywhere.
+ Update: MakeDataExecutable() has been replaced by
+ msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
+ I'm currently unsure if I can link against it. May have to roll in
+ my own version...
+ @todo Stop using an entire 4K high-allocated VM page per 28-byte escape
+ branch island. Done right, this will dramatically speed up escape
+ island allocations when they number over 250. Then again, if you're
+ overriding more than 250 functions, maybe speed isn't your main
+ concern...
+ @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
+ first-instructions. Initially, we should refuse to override
+ functions beginning with these instructions. Eventually, we should
+ dynamically rewrite them to make them position-independent.
+ @todo Write mach_unoverride(), which would remove an override placed on a
+ function. Must be multiple-override aware, which means an almost
+ complete rewrite under the covers, because the target address can't
+ be spread across two load instructions like it is now since it will
+ need to be atomically updatable.
+ @todo Add non-rentry variants of overrides to test_mach_override.
+
+ ***************************************************************************/
+
+#ifdef __APPLE__
+
+#ifndef _mach_override_
+#define _mach_override_
+
+#include <sys/types.h>
+#include <mach/error.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ Returned if the function to be overrided begins with a 'mfctr' instruction.
+*/
+#define err_cannot_override (err_local|1)
+
+/************************************************************************************//**
+ Dynamically overrides the function implementation referenced by
+ originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
+ Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
+ the original implementation.
+
+ @param originalFunctionAddress -> Required address of the function to
+ override (with overrideFunctionAddress).
+ @param overrideFunctionAddress -> Required address to the overriding
+ function.
+ @param originalFunctionReentryIsland <- Optional pointer to pointer to the
+ reentry island. Can be NULL.
+ @result <- err_cannot_override if the original
+ function's implementation begins with
+ the 'mfctr' instruction.
+
+ ************************************************************************************/
+
+// We're prefixing mach_override_ptr() with "__asan_" to avoid name conflicts with other
+// mach_override_ptr() implementations that may appear in the client program.
+ mach_error_t
+__asan_mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland );
+
+/************************************************************************************//**
+
+
+ ************************************************************************************/
+
+#ifdef __cplusplus
+
+#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
+ { \
+ static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
+ static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
+ class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
+ public: \
+ static kern_return_t override(void *originalFunctionPtr) { \
+ kern_return_t result = err_none; \
+ if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
+ ORIGINAL_FUNCTION_NAME##_overriden = true; \
+ result = mach_override_ptr( (void*)originalFunctionPtr, \
+ (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
+ (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
+ } \
+ return result; \
+ } \
+ static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
+
+#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
+ } \
+ }; \
+ \
+ err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
+ }
+
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+#endif // _mach_override_
+
+#endif // __APPLE__
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
new file mode 100755
index 0000000..80b5927
--- /dev/null
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+import os
+import re
+import sys
+import string
+import subprocess
+
+pipes = {}
+
+def patch_address(frameno, addr_s):
+ ''' Subtracts 1 or 2 from the top frame's address.
+ Top frame is normally the return address from asan_report*
+ call, which is not expected to return at all. Because of that, this
+ address often belongs to the next source code line, or even to a different
+ function. '''
+ if frameno == '0':
+ addr = int(addr_s, 16)
+ if os.uname()[4].startswith('arm'):
+ # Cancel the Thumb bit
+ addr = addr & (~1)
+ addr -= 1
+ return hex(addr)
+ return addr_s
+
+# TODO(glider): need some refactoring here
+def symbolize_addr2line(line):
+ #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
+ match = re.match('^( *#([0-9]+) *0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
+ if match:
+ frameno = match.group(2)
+ binary = match.group(3)
+ addr = match.group(4)
+ addr = patch_address(frameno, addr)
+ if not pipes.has_key(binary):
+ pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ p = pipes[binary]
+ try:
+ print >>p.stdin, addr
+ function_name = p.stdout.readline().rstrip()
+ file_name = p.stdout.readline().rstrip()
+ except:
+ function_name = ""
+ file_name = ""
+ for path_to_cut in sys.argv[1:]:
+ file_name = re.sub(".*" + path_to_cut, "", file_name)
+ file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
+ file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
+
+ print match.group(1), "in", function_name, file_name
+ else:
+ print line.rstrip()
+
+def symbolize_atos(line):
+ #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
+ match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
+ if match:
+ #print line
+ prefix = match.group(1)
+ frameno = match.group(2)
+ addr = match.group(3)
+ binary = match.group(4)
+ offset = match.group(5)
+ addr = patch_address(frameno, addr)
+ load_addr = int(addr, 16) - int(offset, 16)
+ if not pipes.has_key(binary):
+ #print "atos -o %s -l %s" % (binary, hex(load_addr))
+ pipes[binary] = subprocess.Popen(["atos", "-o", binary],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,)
+ p = pipes[binary]
+ # TODO(glider): how to tell if the address is absolute?
+ if ".app/" in binary and not ".framework" in binary:
+ print >>p.stdin, "%s" % addr
+ else:
+ print >>p.stdin, "%s" % offset
+ # TODO(glider): it's more efficient to make a batch atos run for each binary.
+ p.stdin.close()
+ atos_line = p.stdout.readline().rstrip()
+ del pipes[binary]
+
+ print "%s%s in %s" % (prefix, addr, atos_line)
+ else:
+ print line.rstrip()
+
+system = os.uname()[0]
+if system in ['Linux', 'Darwin']:
+ for line in sys.stdin:
+ if system == 'Linux':
+ symbolize_addr2line(line)
+ elif system == 'Darwin':
+ symbolize_atos(line)
+else:
+ print 'Unknown system: ', system
diff --git a/lib/asan/sysinfo/LICENSE.TXT b/lib/asan/sysinfo/LICENSE.TXT
new file mode 100644
index 0000000..b519af5
--- /dev/null
+++ b/lib/asan/sysinfo/LICENSE.TXT
@@ -0,0 +1,29 @@
+Copyright (c) 2005, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/lib/asan/sysinfo/Makefile.mk b/lib/asan/sysinfo/Makefile.mk
new file mode 100644
index 0000000..bc4a2ff
--- /dev/null
+++ b/lib/asan/sysinfo/Makefile.mk
@@ -0,0 +1,22 @@
+#===- lib/asan/sysinfo/Makefile.mk -------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := asan
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+ObjNames := $(Sources:%.c=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+
+# Define a convenience variable for all the asan functions.
+AsanFunctions += $(Sources:%.cc=%)
diff --git a/lib/asan/sysinfo/basictypes.h b/lib/asan/sysinfo/basictypes.h
new file mode 100644
index 0000000..ac21f8c
--- /dev/null
+++ b/lib/asan/sysinfo/basictypes.h
@@ -0,0 +1,321 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef _BASICTYPES_H_
+#define _BASICTYPES_H_
+
+#include <inttypes.h> // uint16_t might be here; PRId64 too.
+#include <stdint.h> // to get uint16_t (ISO naming madness)
+#include <sys/types.h> // our last best hope for uint16_t
+
+// Standard typedefs
+// All Google code is compiled with -funsigned-char to make "char"
+// unsigned. Google code therefore doesn't need a "uchar" type.
+// TODO(csilvers): how do we make sure unsigned-char works on non-gcc systems?
+typedef signed char schar;
+typedef int8_t int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+
+// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
+// places. Use the signed types unless your variable represents a bit
+// pattern (eg a hash value) or you really need the extra bit. Do NOT
+// use 'unsigned' to express "this value should always be positive";
+// use assertions for this.
+
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+const uint16 kuint16max = ( (uint16) 0xFFFF);
+const uint32 kuint32max = ( (uint32) 0xFFFFFFFF);
+const uint64 kuint64max = ( (((uint64) kuint32max) << 32) | kuint32max );
+
+const int8 kint8max = ( ( int8) 0x7F);
+const int16 kint16max = ( ( int16) 0x7FFF);
+const int32 kint32max = ( ( int32) 0x7FFFFFFF);
+const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max );
+
+const int8 kint8min = ( ( int8) 0x80);
+const int16 kint16min = ( ( int16) 0x8000);
+const int32 kint32min = ( ( int32) 0x80000000);
+const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 );
+
+// Define the "portable" printf and scanf macros, if they're not
+// already there (via the inttypes.h we #included above, hopefully).
+// Mostly it's old systems that don't support inttypes.h, so we assume
+// they're 32 bit.
+#ifndef PRIx64
+#define PRIx64 "llx"
+#endif
+#ifndef SCNx64
+#define SCNx64 "llx"
+#endif
+#ifndef PRId64
+#define PRId64 "lld"
+#endif
+#ifndef SCNd64
+#define SCNd64 "lld"
+#endif
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif
+#ifndef PRIxPTR
+#define PRIxPTR "lx"
+#endif
+
+// Also allow for printing of a pthread_t.
+#define GPRIuPTHREAD "lu"
+#define GPRIxPTHREAD "lx"
+#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__)
+#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast<uintptr_t>(pthreadt)
+#else
+#define PRINTABLE_PTHREAD(pthreadt) pthreadt
+#endif
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// An alternate name that leaves out the moral judgment... :-)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) DISALLOW_EVIL_CONSTRUCTORS(TypeName)
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// COMPILE_ASSERT(sizeof(num_content_type_names) == sizeof(int),
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+//
+// Implementation details of COMPILE_ASSERT:
+//
+// - COMPILE_ASSERT works by defining an array type that has -1
+// elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+// does not work, as gcc supports variable-length arrays whose sizes
+// are determined at run-time (this is gcc's extension and not part
+// of the C++ standard). As a result, gcc fails to reject the
+// following code with the simple definition:
+//
+// int foo;
+// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
+// // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+// expr is a compile-time constant. (Template arguments must be
+// determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
+//
+// CompileAssert<bool(expr)>
+//
+// instead, these compilers will refuse to compile
+//
+// COMPILE_ASSERT(5 > 0, some_message);
+//
+// (They seem to think the ">" in "5 > 0" marks the end of the
+// template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+// ((expr) ? 1 : -1).
+//
+// This is to avoid running into a bug in MS VC 7.1, which
+// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+#define arraysize(a) (sizeof(a) / sizeof(*(a)))
+
+#define OFFSETOF_MEMBER(strct, field) \
+ (reinterpret_cast<char*>(&reinterpret_cast<strct*>(16)->field) - \
+ reinterpret_cast<char*>(16))
+
+#ifdef HAVE___ATTRIBUTE__
+# define ATTRIBUTE_WEAK __attribute__((weak))
+# define ATTRIBUTE_NOINLINE __attribute__((noinline))
+#else
+# define ATTRIBUTE_WEAK
+# define ATTRIBUTE_NOINLINE
+#endif
+
+// Section attributes are supported for both ELF and Mach-O, but in
+// very different ways. Here's the API we provide:
+// 1) ATTRIBUTE_SECTION: put this with the declaration of all functions
+// you want to be in the same linker section
+// 2) DEFINE_ATTRIBUTE_SECTION_VARS: must be called once per unique
+// name. You want to make sure this is executed before any
+// DECLARE_ATTRIBUTE_SECTION_VARS; the easiest way is to put them
+// in the same .cc file. Put this call at the global level.
+// 3) INIT_ATTRIBUTE_SECTION_VARS: you can scatter calls to this in
+// multiple places to help ensure execution before any
+// DECLARE_ATTRIBUTE_SECTION_VARS. You must have at least one
+// DEFINE, but you can have many INITs. Put each in its own scope.
+// 4) DECLARE_ATTRIBUTE_SECTION_VARS: must be called before using
+// ATTRIBUTE_SECTION_START or ATTRIBUTE_SECTION_STOP on a name.
+// Put this call at the global level.
+// 5) ATTRIBUTE_SECTION_START/ATTRIBUTE_SECTION_STOP: call this to say
+// where in memory a given section is. All functions declared with
+// ATTRIBUTE_SECTION are guaranteed to be between START and STOP.
+
+#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__)
+# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name)))
+
+ // Weak section declaration to be used as a global declaration
+ // for ATTRIBUTE_SECTION_START|STOP(name) to compile and link
+ // even without functions with ATTRIBUTE_SECTION(name).
+# define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
+ extern char __start_##name[] ATTRIBUTE_WEAK; \
+ extern char __stop_##name[] ATTRIBUTE_WEAK
+# define INIT_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
+# define DEFINE_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
+
+ // Return void* pointers to start/end of a section of code with functions
+ // having ATTRIBUTE_SECTION(name), or 0 if no such function exists.
+ // One must DECLARE_ATTRIBUTE_SECTION(name) for this to compile and link.
+# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
+# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
+# define HAVE_ATTRIBUTE_SECTION_START 1
+
+#elif defined(HAVE___ATTRIBUTE__) && defined(__MACH__)
+# define ATTRIBUTE_SECTION(name) __attribute__ ((section ("__TEXT, " #name)))
+
+#include <mach-o/getsect.h>
+#include <mach-o/dyld.h>
+class AssignAttributeStartEnd {
+ public:
+ AssignAttributeStartEnd(const char* name, char** pstart, char** pend) {
+ // Find out what dynamic library name is defined in
+ if (_dyld_present()) {
+ for (int i = _dyld_image_count() - 1; i >= 0; --i) {
+ const mach_header* hdr = _dyld_get_image_header(i);
+#ifdef MH_MAGIC_64
+ if (hdr->magic == MH_MAGIC_64) {
+ uint64_t len;
+ *pstart = getsectdatafromheader_64((mach_header_64*)hdr,
+ "__TEXT", name, &len);
+ if (*pstart) { // NULL if not defined in this dynamic library
+ *pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
+ *pend = *pstart + len;
+ return;
+ }
+ }
+#endif
+ if (hdr->magic == MH_MAGIC) {
+ uint32_t len;
+ *pstart = getsectdatafromheader(hdr, "__TEXT", name, &len);
+ if (*pstart) { // NULL if not defined in this dynamic library
+ *pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
+ *pend = *pstart + len;
+ return;
+ }
+ }
+ }
+ }
+ // If we get here, not defined in a dll at all. See if defined statically.
+ unsigned long len; // don't ask me why this type isn't uint32_t too...
+ *pstart = getsectdata("__TEXT", name, &len);
+ *pend = *pstart + len;
+ }
+};
+
+#define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
+ extern char* __start_##name; \
+ extern char* __stop_##name
+
+#define INIT_ATTRIBUTE_SECTION_VARS(name) \
+ DECLARE_ATTRIBUTE_SECTION_VARS(name); \
+ static const AssignAttributeStartEnd __assign_##name( \
+ #name, &__start_##name, &__stop_##name)
+
+#define DEFINE_ATTRIBUTE_SECTION_VARS(name) \
+ char* __start_##name, *__stop_##name; \
+ INIT_ATTRIBUTE_SECTION_VARS(name)
+
+# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
+# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
+# define HAVE_ATTRIBUTE_SECTION_START 1
+
+#else // not HAVE___ATTRIBUTE__ && __ELF__, nor HAVE___ATTRIBUTE__ && __MACH__
+# define ATTRIBUTE_SECTION(name)
+# define DECLARE_ATTRIBUTE_SECTION_VARS(name)
+# define INIT_ATTRIBUTE_SECTION_VARS(name)
+# define DEFINE_ATTRIBUTE_SECTION_VARS(name)
+# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(0))
+# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(0))
+
+#endif // HAVE___ATTRIBUTE__ and __ELF__ or __MACH__
+
+#if defined(HAVE___ATTRIBUTE__) && (defined(__i386__) || defined(__x86_64__))
+# define CACHELINE_SIZE 64
+# define CACHELINE_ALIGNED __attribute__((aligned(CACHELINE_SIZE)))
+#else
+# define CACHELINE_ALIGNED
+#endif // defined(HAVE___ATTRIBUTE__) && (__i386__ || __x86_64__)
+
+
+// The following enum should be used only as a constructor argument to indicate
+// that the variable has static storage class, and that the constructor should
+// do nothing to its state. It indicates to the reader that it is legal to
+// declare a static nistance of the class, provided the constructor is given
+// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
+// static variable that has a constructor or a destructor because invocation
+// order is undefined. However, IF the type can be initialized by filling with
+// zeroes (which the loader does for static variables), AND the destructor also
+// does nothing to the storage, then a constructor declared as
+// explicit MyClass(base::LinkerInitialized x) {}
+// and invoked as
+// static MyClass my_variable_name(base::LINKER_INITIALIZED);
+namespace base {
+enum LinkerInitialized { LINKER_INITIALIZED };
+}
+
+#endif // _BASICTYPES_H_
diff --git a/lib/asan/sysinfo/sysinfo.cc b/lib/asan/sysinfo/sysinfo.cc
new file mode 100644
index 0000000..ee06735
--- /dev/null
+++ b/lib/asan/sysinfo/sysinfo.cc
@@ -0,0 +1,617 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <assert.h>
+#include <stdlib.h> // for getenv()
+#include <stdio.h> // for snprintf(), sscanf()
+#include <string.h> // for memmove(), memchr(), etc.
+#include <fcntl.h> // for open()
+#include <errno.h> // for errno
+#include <unistd.h> // for read()
+#if defined __MACH__ // Mac OS X, almost certainly
+#include <mach-o/dyld.h> // for iterating over dll's in ProcMapsIter
+#include <mach-o/loader.h> // for iterating over dll's in ProcMapsIter
+#include <sys/types.h>
+#include <sys/sysctl.h> // how we figure out numcpu's on OS X
+#elif defined __FreeBSD__
+#include <sys/sysctl.h>
+#elif defined __sun__ // Solaris
+#include <procfs.h> // for, e.g., prmap_t
+#elif defined(PLATFORM_WINDOWS)
+#include <process.h> // for getpid() (actually, _getpid())
+#include <shlwapi.h> // for SHGetValueA()
+#include <tlhelp32.h> // for Module32First()
+#endif
+#include "sysinfo.h"
+
+#ifdef PLATFORM_WINDOWS
+#ifdef MODULEENTRY32
+// In a change from the usual W-A pattern, there is no A variant of
+// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
+// In unicode mode, tlhelp32.h #defines MODULEENTRY32 to be
+// MODULEENTRY32W. These #undefs are the only way I see to get back
+// access to the original, ascii struct (and related functions).
+#undef MODULEENTRY32
+#undef Module32First
+#undef Module32Next
+#undef PMODULEENTRY32
+#undef LPMODULEENTRY32
+#endif /* MODULEENTRY32 */
+// MinGW doesn't seem to define this, perhaps some windowsen don't either.
+#ifndef TH32CS_SNAPMODULE32
+#define TH32CS_SNAPMODULE32 0
+#endif /* TH32CS_SNAPMODULE32 */
+#endif /* PLATFORM_WINDOWS */
+
+// Re-run fn until it doesn't cause EINTR.
+#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
+
+// open/read/close can set errno, which may be illegal at this
+// time, so prefer making the syscalls directly if we can.
+#ifdef HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+# define safeopen(filename, mode) syscall(SYS_open, filename, mode)
+# define saferead(fd, buffer, size) syscall(SYS_read, fd, buffer, size)
+# define safeclose(fd) syscall(SYS_close, fd)
+#else
+# define safeopen(filename, mode) open(filename, mode)
+# define saferead(fd, buffer, size) read(fd, buffer, size)
+# define safeclose(fd) close(fd)
+#endif
+
+
+// ----------------------------------------------------------------------
+// HasPosixThreads()
+// Return true if we're running POSIX (e.g., NPTL on Linux)
+// threads, as opposed to a non-POSIX thread libary. The thing
+// that we care about is whether a thread's pid is the same as
+// the thread that spawned it. If so, this function returns
+// true.
+// ----------------------------------------------------------------------
+bool HasPosixThreads() {
+#if defined(__linux__) and !defined(ANDROID)
+#ifndef _CS_GNU_LIBPTHREAD_VERSION
+#define _CS_GNU_LIBPTHREAD_VERSION 3
+#endif
+ char buf[32];
+ // We assume that, if confstr() doesn't know about this name, then
+ // the same glibc is providing LinuxThreads.
+ if (confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf)) == 0)
+ return false;
+ return strncmp(buf, "NPTL", 4) == 0;
+#elif defined(PLATFORM_WINDOWS) || defined(__CYGWIN__) || defined(__CYGWIN32__)
+ return false;
+#else // other OS
+ return true; // Assume that everything else has Posix
+#endif // else OS_LINUX
+}
+
+// ----------------------------------------------------------------------
+
+#define CHECK_LT(x, y) do { assert((x) < (y)); } while (0)
+
+#if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYGWIN__ || defined __CYGWIN32__
+static void ConstructFilename(const char* spec, pid_t pid,
+ char* buf, int buf_size) {
+ CHECK_LT(snprintf(buf, buf_size,
+ spec,
+ static_cast<int>(pid ? pid : getpid())), buf_size);
+}
+#endif
+
+// A templatized helper function instantiated for Mach (OS X) only.
+// It can handle finding info for both 32 bits and 64 bits.
+// Returns true if it successfully handled the hdr, false else.
+#ifdef __MACH__ // Mac OS X, almost certainly
+template<uint32_t kMagic, uint32_t kLCSegment,
+ typename MachHeader, typename SegmentCommand>
+static bool NextExtMachHelper(const mach_header* hdr,
+ int current_image, int current_load_cmd,
+ uint64 *start, uint64 *end, char **flags,
+ uint64 *offset, int64 *inode, char **filename,
+ uint64 *file_mapping, uint64 *file_pages,
+ uint64 *anon_mapping, uint64 *anon_pages,
+ dev_t *dev) {
+ static char kDefaultPerms[5] = "r-xp";
+ if (hdr->magic != kMagic)
+ return false;
+ const char* lc = (const char *)hdr + sizeof(MachHeader);
+ // TODO(csilvers): make this not-quadradic (increment and hold state)
+ for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd
+ lc += ((const load_command *)lc)->cmdsize;
+ if (((const load_command *)lc)->cmd == kLCSegment) {
+ const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image);
+ const SegmentCommand* sc = (const SegmentCommand *)lc;
+ if (start) *start = sc->vmaddr + dlloff;
+ if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
+ if (flags) *flags = kDefaultPerms; // can we do better?
+ if (offset) *offset = sc->fileoff;
+ if (inode) *inode = 0;
+ if (filename)
+ *filename = const_cast<char*>(_dyld_get_image_name(current_image));
+ if (file_mapping) *file_mapping = 0;
+ if (file_pages) *file_pages = 0; // could we use sc->filesize?
+ if (anon_mapping) *anon_mapping = 0;
+ if (anon_pages) *anon_pages = 0;
+ if (dev) *dev = 0;
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+ProcMapsIterator::ProcMapsIterator(pid_t pid) {
+ Init(pid, NULL, false);
+}
+
+ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) {
+ Init(pid, buffer, false);
+}
+
+ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer,
+ bool use_maps_backing) {
+ Init(pid, buffer, use_maps_backing);
+}
+
+void ProcMapsIterator::Init(pid_t pid, Buffer *buffer,
+ bool use_maps_backing) {
+ pid_ = pid;
+ using_maps_backing_ = use_maps_backing;
+ dynamic_buffer_ = NULL;
+ if (!buffer) {
+ // If the user didn't pass in any buffer storage, allocate it
+ // now. This is the normal case; the signal handler passes in a
+ // static buffer.
+ buffer = dynamic_buffer_ = new Buffer;
+ } else {
+ dynamic_buffer_ = NULL;
+ }
+
+ ibuf_ = buffer->buf_;
+
+ stext_ = etext_ = nextline_ = ibuf_;
+ ebuf_ = ibuf_ + Buffer::kBufSize - 1;
+ nextline_ = ibuf_;
+
+#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
+ if (use_maps_backing) { // don't bother with clever "self" stuff in this case
+ ConstructFilename("/proc/%d/maps_backing", pid, ibuf_, Buffer::kBufSize);
+ } else if (pid == 0) {
+ // We have to kludge a bit to deal with the args ConstructFilename
+ // expects. The 1 is never used -- it's only impt. that it's not 0.
+ ConstructFilename("/proc/self/maps", 1, ibuf_, Buffer::kBufSize);
+ } else {
+ ConstructFilename("/proc/%d/maps", pid, ibuf_, Buffer::kBufSize);
+ }
+ // No error logging since this can be called from the crash dump
+ // handler at awkward moments. Users should call Valid() before
+ // using.
+ NO_INTR(fd_ = open(ibuf_, O_RDONLY));
+#elif defined(__FreeBSD__)
+ // We don't support maps_backing on freebsd
+ if (pid == 0) {
+ ConstructFilename("/proc/curproc/map", 1, ibuf_, Buffer::kBufSize);
+ } else {
+ ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
+ }
+ NO_INTR(fd_ = open(ibuf_, O_RDONLY));
+#elif defined(__sun__)
+ if (pid == 0) {
+ ConstructFilename("/proc/self/map", 1, ibuf_, Buffer::kBufSize);
+ } else {
+ ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
+ }
+ NO_INTR(fd_ = open(ibuf_, O_RDONLY));
+#elif defined(__MACH__)
+ current_image_ = _dyld_image_count(); // count down from the top
+ current_load_cmd_ = -1;
+#elif defined(PLATFORM_WINDOWS)
+ snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE |
+ TH32CS_SNAPMODULE32,
+ GetCurrentProcessId());
+ memset(&module_, 0, sizeof(module_));
+#else
+ fd_ = -1; // so Valid() is always false
+#endif
+
+}
+
+ProcMapsIterator::~ProcMapsIterator() {
+#if defined(PLATFORM_WINDOWS)
+ if (snapshot_ != INVALID_HANDLE_VALUE) CloseHandle(snapshot_);
+#elif defined(__MACH__)
+ // no cleanup necessary!
+#else
+ if (fd_ >= 0) NO_INTR(close(fd_));
+#endif
+ delete dynamic_buffer_;
+}
+
+bool ProcMapsIterator::Valid() const {
+#if defined(PLATFORM_WINDOWS)
+ return snapshot_ != INVALID_HANDLE_VALUE;
+#elif defined(__MACH__)
+ return 1;
+#else
+ return fd_ != -1;
+#endif
+}
+
+bool ProcMapsIterator::Next(uint64 *start, uint64 *end, char **flags,
+ uint64 *offset, int64 *inode, char **filename) {
+ return NextExt(start, end, flags, offset, inode, filename, NULL, NULL,
+ NULL, NULL, NULL);
+}
+
+// This has too many arguments. It should really be building
+// a map object and returning it. The problem is that this is called
+// when the memory allocator state is undefined, hence the arguments.
+bool ProcMapsIterator::NextExt(uint64 *start, uint64 *end, char **flags,
+ uint64 *offset, int64 *inode, char **filename,
+ uint64 *file_mapping, uint64 *file_pages,
+ uint64 *anon_mapping, uint64 *anon_pages,
+ dev_t *dev) {
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
+ do {
+ // Advance to the start of the next line
+ stext_ = nextline_;
+
+ // See if we have a complete line in the buffer already
+ nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ - stext_));
+ if (!nextline_) {
+ // Shift/fill the buffer so we do have a line
+ int count = etext_ - stext_;
+
+ // Move the current text to the start of the buffer
+ memmove(ibuf_, stext_, count);
+ stext_ = ibuf_;
+ etext_ = ibuf_ + count;
+
+ int nread = 0; // fill up buffer with text
+ while (etext_ < ebuf_) {
+ NO_INTR(nread = read(fd_, etext_, ebuf_ - etext_));
+ if (nread > 0)
+ etext_ += nread;
+ else
+ break;
+ }
+
+ // Zero out remaining characters in buffer at EOF to avoid returning
+ // garbage from subsequent calls.
+ if (etext_ != ebuf_ && nread == 0) {
+ memset(etext_, 0, ebuf_ - etext_);
+ }
+ *etext_ = '\n'; // sentinel; safe because ibuf extends 1 char beyond ebuf
+ nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ + 1 - stext_));
+ }
+ *nextline_ = 0; // turn newline into nul
+ nextline_ += ((nextline_ < etext_)? 1 : 0); // skip nul if not end of text
+ // stext_ now points at a nul-terminated line
+ uint64 tmpstart, tmpend, tmpoffset;
+ int64 tmpinode;
+ int major, minor;
+ unsigned filename_offset = 0;
+#if defined(__linux__)
+ // for now, assume all linuxes have the same format
+ if (sscanf(stext_, "%"SCNx64"-%"SCNx64" %4s %"SCNx64" %x:%x %"SCNd64" %n",
+ (unsigned long long *)(start ? start : &tmpstart),
+ (unsigned long long *)(end ? end : &tmpend),
+ flags_,
+ (unsigned long long *)(offset ? offset : &tmpoffset),
+ &major, &minor,
+ (unsigned long long *)(inode ? inode : &tmpinode),
+ &filename_offset) != 7) continue;
+#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
+ // cygwin is like linux, except the third field is the "entry point"
+ // rather than the offset (see format_process_maps at
+ // http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/fhandler_process.cc?rev=1.89&content-type=text/x-cvsweb-markup&cvsroot=src
+ // Offset is always be 0 on cygwin: cygwin implements an mmap
+ // by loading the whole file and then calling NtMapViewOfSection.
+ // Cygwin also seems to set its flags kinda randomly; use windows default.
+ char tmpflags[5];
+ if (offset)
+ *offset = 0;
+ strcpy(flags_, "r-xp");
+ if (sscanf(stext_, "%llx-%llx %4s %llx %x:%x %lld %n",
+ start ? start : &tmpstart,
+ end ? end : &tmpend,
+ tmpflags,
+ &tmpoffset,
+ &major, &minor,
+ inode ? inode : &tmpinode, &filename_offset) != 7) continue;
+#elif defined(__FreeBSD__)
+ // For the format, see http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?rev=1.31&content-type=text/x-cvsweb-markup
+ tmpstart = tmpend = tmpoffset = 0;
+ tmpinode = 0;
+ major = minor = 0; // can't get this info in freebsd
+ if (inode)
+ *inode = 0; // nor this
+ if (offset)
+ *offset = 0; // seems like this should be in there, but maybe not
+ // start end resident privateresident obj(?) prot refcnt shadowcnt
+ // flags copy_on_write needs_copy type filename:
+ // 0x8048000 0x804a000 2 0 0xc104ce70 r-x 1 0 0x0 COW NC vnode /bin/cat
+ if (sscanf(stext_, "0x%"SCNx64" 0x%"SCNx64" %*d %*d %*p %3s %*d %*d 0x%*x %*s %*s %*s %n",
+ start ? start : &tmpstart,
+ end ? end : &tmpend,
+ flags_,
+ &filename_offset) != 3) continue;
+#endif
+
+ // Depending on the Linux kernel being used, there may or may not be a space
+ // after the inode if there is no filename. sscanf will in such situations
+ // nondeterministically either fill in filename_offset or not (the results
+ // differ on multiple calls in the same run even with identical arguments).
+ // We don't want to wander off somewhere beyond the end of the string.
+ size_t stext_length = strlen(stext_);
+ if (filename_offset == 0 || filename_offset > stext_length)
+ filename_offset = stext_length;
+
+ // We found an entry
+ if (flags) *flags = flags_;
+ if (filename) *filename = stext_ + filename_offset;
+ if (dev) *dev = minor | (major << 8);
+
+ if (using_maps_backing_) {
+ // Extract and parse physical page backing info.
+ char *backing_ptr = stext_ + filename_offset +
+ strlen(stext_+filename_offset);
+
+ // find the second '('
+ int paren_count = 0;
+ while (--backing_ptr > stext_) {
+ if (*backing_ptr == '(') {
+ ++paren_count;
+ if (paren_count >= 2) {
+ uint64 tmp_file_mapping;
+ uint64 tmp_file_pages;
+ uint64 tmp_anon_mapping;
+ uint64 tmp_anon_pages;
+
+ sscanf(backing_ptr+1, "F %"SCNx64" %"SCNd64") (A %"SCNx64" %"SCNd64")",
+ (unsigned long long *)(file_mapping ?
+ file_mapping : &tmp_file_mapping),
+ (unsigned long long *)(file_pages ?
+ file_pages : &tmp_file_pages),
+ (unsigned long long *)(anon_mapping
+ ? anon_mapping : &tmp_anon_mapping),
+ (unsigned long long *)(anon_pages
+ ? anon_pages : &tmp_anon_pages));
+ // null terminate the file name (there is a space
+ // before the first (.
+ backing_ptr[-1] = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+ } while (etext_ > ibuf_);
+#elif defined(__sun__)
+ // This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1
+ static char kPerms[8][4] = { "---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx" };
+ COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4);
+ COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2);
+ COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1);
+ Buffer object_path;
+ int nread = 0; // fill up buffer with text
+ NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t)));
+ if (nread == sizeof(prmap_t)) {
+ long inode_from_mapname = 0;
+ prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_);
+ // Best-effort attempt to get the inode from the filename. I think the
+ // two middle ints are major and minor device numbers, but I'm not sure.
+ sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname);
+
+ if (pid_ == 0) {
+ CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
+ "/proc/self/path/%s", mapinfo->pr_mapname),
+ Buffer::kBufSize);
+ } else {
+ CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
+ "/proc/%d/path/%s",
+ static_cast<int>(pid_), mapinfo->pr_mapname),
+ Buffer::kBufSize);
+ }
+ ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX);
+ CHECK_LT(len, PATH_MAX);
+ if (len < 0)
+ len = 0;
+ current_filename_[len] = '\0';
+
+ if (start) *start = mapinfo->pr_vaddr;
+ if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size;
+ if (flags) *flags = kPerms[mapinfo->pr_mflags & 7];
+ if (offset) *offset = mapinfo->pr_offset;
+ if (inode) *inode = inode_from_mapname;
+ if (filename) *filename = current_filename_;
+ if (file_mapping) *file_mapping = 0;
+ if (file_pages) *file_pages = 0;
+ if (anon_mapping) *anon_mapping = 0;
+ if (anon_pages) *anon_pages = 0;
+ if (dev) *dev = 0;
+ return true;
+ }
+#elif defined(__MACH__)
+ // We return a separate entry for each segment in the DLL. (TODO(csilvers):
+ // can we do better?) A DLL ("image") has load-commands, some of which
+ // talk about segment boundaries.
+ // cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912
+ for (; current_image_ >= 0; current_image_--) {
+ const mach_header* hdr = _dyld_get_image_header(current_image_);
+ if (!hdr) continue;
+ if (current_load_cmd_ < 0) // set up for this image
+ current_load_cmd_ = hdr->ncmds; // again, go from the top down
+
+ // We start with the next load command (we've already looked at this one).
+ for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) {
+#ifdef MH_MAGIC_64
+ if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64,
+ struct mach_header_64, struct segment_command_64>(
+ hdr, current_image_, current_load_cmd_,
+ start, end, flags, offset, inode, filename,
+ file_mapping, file_pages, anon_mapping,
+ anon_pages, dev)) {
+ return true;
+ }
+#endif
+ if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT,
+ struct mach_header, struct segment_command>(
+ hdr, current_image_, current_load_cmd_,
+ start, end, flags, offset, inode, filename,
+ file_mapping, file_pages, anon_mapping,
+ anon_pages, dev)) {
+ return true;
+ }
+ }
+ // If we get here, no more load_cmd's in this image talk about
+ // segments. Go on to the next image.
+ }
+#elif defined(PLATFORM_WINDOWS)
+ static char kDefaultPerms[5] = "r-xp";
+ BOOL ok;
+ if (module_.dwSize == 0) { // only possible before first call
+ module_.dwSize = sizeof(module_);
+ ok = Module32First(snapshot_, &module_);
+ } else {
+ ok = Module32Next(snapshot_, &module_);
+ }
+ if (ok) {
+ uint64 base_addr = reinterpret_cast<DWORD_PTR>(module_.modBaseAddr);
+ if (start) *start = base_addr;
+ if (end) *end = base_addr + module_.modBaseSize;
+ if (flags) *flags = kDefaultPerms;
+ if (offset) *offset = 0;
+ if (inode) *inode = 0;
+ if (filename) *filename = module_.szExePath;
+ if (file_mapping) *file_mapping = 0;
+ if (file_pages) *file_pages = 0;
+ if (anon_mapping) *anon_mapping = 0;
+ if (anon_pages) *anon_pages = 0;
+ if (dev) *dev = 0;
+ return true;
+ }
+#endif
+
+ // We didn't find anything
+ return false;
+}
+
+int ProcMapsIterator::FormatLine(char* buffer, int bufsize,
+ uint64 start, uint64 end, const char *flags,
+ uint64 offset, int64 inode,
+ const char *filename, dev_t dev) {
+ // We assume 'flags' looks like 'rwxp' or 'rwx'.
+ char r = (flags && flags[0] == 'r') ? 'r' : '-';
+ char w = (flags && flags[0] && flags[1] == 'w') ? 'w' : '-';
+ char x = (flags && flags[0] && flags[1] && flags[2] == 'x') ? 'x' : '-';
+ // p always seems set on linux, so we set the default to 'p', not '-'
+ char p = (flags && flags[0] && flags[1] && flags[2] && flags[3] != 'p')
+ ? '-' : 'p';
+
+ const int rc = snprintf(buffer, bufsize,
+ "%08"PRIx64"-%08"PRIx64" %c%c%c%c %08"PRIx64" %02x:%02x %-11"PRId64" %s\n",
+ (unsigned long long)start, (unsigned long long)end, r,w,x,p,
+ (unsigned long long)offset,
+ static_cast<int>(dev/256), static_cast<int>(dev%256),
+ (unsigned long long)inode, filename);
+ return (rc < 0 || rc >= bufsize) ? 0 : rc;
+}
+
+// Helper to add the list of mapped shared libraries to a profile.
+// Fill formatted "/proc/self/maps" contents into buffer 'buf' of size 'size'
+// and return the actual size occupied in 'buf'. We fill wrote_all to true
+// if we successfully wrote all proc lines to buf, false else.
+// We do not provision for 0-terminating 'buf'.
+int FillProcSelfMaps(char buf[], int size, bool* wrote_all) {
+ ProcMapsIterator::Buffer iterbuf;
+ ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
+
+ uint64 start, end, offset;
+ int64 inode;
+ char *flags, *filename;
+ int bytes_written = 0;
+ *wrote_all = true;
+ while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
+ const int line_length = it.FormatLine(buf + bytes_written,
+ size - bytes_written,
+ start, end, flags, offset,
+ inode, filename, 0);
+ if (line_length == 0)
+ *wrote_all = false; // failed to write this line out
+ else
+ bytes_written += line_length;
+
+ }
+ return bytes_written;
+}
+
+// Dump the same data as FillProcSelfMaps reads to fd.
+// It seems easier to repeat parts of FillProcSelfMaps here than to
+// reuse it via a call.
+void DumpProcSelfMaps(RawFD fd) {
+ ProcMapsIterator::Buffer iterbuf;
+ ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
+
+ uint64 start, end, offset;
+ int64 inode;
+ char *flags, *filename;
+ ProcMapsIterator::Buffer linebuf;
+ while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
+ int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_),
+ start, end, flags, offset, inode, filename,
+ 0);
+ RawWrite(fd, linebuf.buf_, written);
+ }
+}
+
+// Re-run fn until it doesn't cause EINTR.
+#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
+
+RawFD RawOpenForWriting(const char* filename) {
+ return open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+}
+
+void RawWrite(RawFD fd, const char* buf, size_t len) {
+ while (len > 0) {
+ ssize_t r;
+ NO_INTR(r = write(fd, buf, len));
+ if (r <= 0) break;
+ buf += r;
+ len -= r;
+ }
+}
+
+void RawClose(RawFD fd) {
+ NO_INTR(close(fd));
+}
diff --git a/lib/asan/sysinfo/sysinfo.h b/lib/asan/sysinfo/sysinfo.h
new file mode 100644
index 0000000..707687e
--- /dev/null
+++ b/lib/asan/sysinfo/sysinfo.h
@@ -0,0 +1,234 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// All functions here are thread-hostile due to file caching unless
+// commented otherwise.
+
+#ifndef _SYSINFO_H_
+#define _SYSINFO_H_
+
+#include <time.h>
+#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
+#include <windows.h> // for DWORD
+#include <TlHelp32.h> // for CreateToolhelp32Snapshot
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> // for pid_t
+#endif
+#include <stddef.h> // for size_t
+#include <limits.h> // for PATH_MAX
+#include "basictypes.h"
+
+// This getenv function is safe to call before the C runtime is initialized.
+// On Windows, it utilizes GetEnvironmentVariable() and on unix it uses
+// /proc/self/environ instead calling getenv(). It's intended to be used in
+// routines that run before main(), when the state required for getenv() may
+// not be set up yet. In particular, errno isn't set up until relatively late
+// (after the pthreads library has a chance to make it threadsafe), and
+// getenv() doesn't work until then.
+// On some platforms, this call will utilize the same, static buffer for
+// repeated GetenvBeforeMain() calls. Callers should not expect pointers from
+// this routine to be long lived.
+// Note that on unix, /proc only has the environment at the time the
+// application was started, so this routine ignores setenv() calls/etc. Also
+// note it only reads the first 16K of the environment.
+extern const char* GetenvBeforeMain(const char* name);
+
+// This takes as an argument an environment-variable name (like
+// CPUPROFILE) whose value is supposed to be a file-path, and sets
+// path to that path, and returns true. Non-trivial for surprising
+// reasons, as documented in sysinfo.cc. path must have space PATH_MAX.
+extern bool GetUniquePathFromEnv(const char* env_name, char* path);
+
+extern int NumCPUs();
+
+// processor cycles per second of each processor. Thread-safe.
+extern double CyclesPerSecond(void);
+
+
+// Return true if we're running POSIX (e.g., NPTL on Linux) threads,
+// as opposed to a non-POSIX thread libary. The thing that we care
+// about is whether a thread's pid is the same as the thread that
+// spawned it. If so, this function returns true.
+// Thread-safe.
+// Note: We consider false negatives to be OK.
+bool HasPosixThreads();
+
+#ifndef SWIG // SWIG doesn't like struct Buffer and variable arguments.
+
+// A ProcMapsIterator abstracts access to /proc/maps for a given
+// process. Needs to be stack-allocatable and avoid using stdio/malloc
+// so it can be used in the google stack dumper, heap-profiler, etc.
+//
+// On Windows and Mac OS X, this iterator iterates *only* over DLLs
+// mapped into this process space. For Linux, FreeBSD, and Solaris,
+// it iterates over *all* mapped memory regions, including anonymous
+// mmaps. For other O/Ss, it is unlikely to work at all, and Valid()
+// will always return false. Also note: this routine only works on
+// FreeBSD if procfs is mounted: make sure this is in your /etc/fstab:
+// proc /proc procfs rw 0 0
+class ProcMapsIterator {
+ public:
+ struct Buffer {
+#ifdef __FreeBSD__
+ // FreeBSD requires us to read all of the maps file at once, so
+ // we have to make a buffer that's "always" big enough
+ static const size_t kBufSize = 102400;
+#else // a one-line buffer is good enough
+ static const size_t kBufSize = PATH_MAX + 1024;
+#endif
+ char buf_[kBufSize];
+ };
+
+
+ // Create a new iterator for the specified pid. pid can be 0 for "self".
+ explicit ProcMapsIterator(pid_t pid);
+
+ // Create an iterator with specified storage (for use in signal
+ // handler). "buffer" should point to a ProcMapsIterator::Buffer
+ // buffer can be NULL in which case a bufer will be allocated.
+ ProcMapsIterator(pid_t pid, Buffer *buffer);
+
+ // Iterate through maps_backing instead of maps if use_maps_backing
+ // is true. Otherwise the same as above. buffer can be NULL and
+ // it will allocate a buffer itself.
+ ProcMapsIterator(pid_t pid, Buffer *buffer,
+ bool use_maps_backing);
+
+ // Returns true if the iterator successfully initialized;
+ bool Valid() const;
+
+ // Returns a pointer to the most recently parsed line. Only valid
+ // after Next() returns true, and until the iterator is destroyed or
+ // Next() is called again. This may give strange results on non-Linux
+ // systems. Prefer FormatLine() if that may be a concern.
+ const char *CurrentLine() const { return stext_; }
+
+ // Writes the "canonical" form of the /proc/xxx/maps info for a single
+ // line to the passed-in buffer. Returns the number of bytes written,
+ // or 0 if it was not able to write the complete line. (To guarantee
+ // success, buffer should have size at least Buffer::kBufSize.)
+ // Takes as arguments values set via a call to Next(). The
+ // "canonical" form of the line (taken from linux's /proc/xxx/maps):
+ // <start_addr(hex)>-<end_addr(hex)> <perms(rwxp)> <offset(hex)> +
+ // <major_dev(hex)>:<minor_dev(hex)> <inode> <filename> Note: the
+ // eg
+ // 08048000-0804c000 r-xp 00000000 03:01 3793678 /bin/cat
+ // If you don't have the dev_t (dev), feel free to pass in 0.
+ // (Next() doesn't return a dev_t, though NextExt does.)
+ //
+ // Note: if filename and flags were obtained via a call to Next(),
+ // then the output of this function is only valid if Next() returned
+ // true, and only until the iterator is destroyed or Next() is
+ // called again. (Since filename, at least, points into CurrentLine.)
+ static int FormatLine(char* buffer, int bufsize,
+ uint64 start, uint64 end, const char *flags,
+ uint64 offset, int64 inode, const char *filename,
+ dev_t dev);
+
+ // Find the next entry in /proc/maps; return true if found or false
+ // if at the end of the file.
+ //
+ // Any of the result pointers can be NULL if you're not interested
+ // in those values.
+ //
+ // If "flags" and "filename" are passed, they end up pointing to
+ // storage within the ProcMapsIterator that is valid only until the
+ // iterator is destroyed or Next() is called again. The caller may
+ // modify the contents of these strings (up as far as the first NUL,
+ // and only until the subsequent call to Next()) if desired.
+
+ // The offsets are all uint64 in order to handle the case of a
+ // 32-bit process running on a 64-bit kernel
+ //
+ // IMPORTANT NOTE: see top-of-class notes for details about what
+ // mapped regions Next() iterates over, depending on O/S.
+ // TODO(csilvers): make flags and filename const.
+ bool Next(uint64 *start, uint64 *end, char **flags,
+ uint64 *offset, int64 *inode, char **filename);
+
+ bool NextExt(uint64 *start, uint64 *end, char **flags,
+ uint64 *offset, int64 *inode, char **filename,
+ uint64 *file_mapping, uint64 *file_pages,
+ uint64 *anon_mapping, uint64 *anon_pages,
+ dev_t *dev);
+
+ ~ProcMapsIterator();
+
+ private:
+ void Init(pid_t pid, Buffer *buffer, bool use_maps_backing);
+
+ char *ibuf_; // input buffer
+ char *stext_; // start of text
+ char *etext_; // end of text
+ char *nextline_; // start of next line
+ char *ebuf_; // end of buffer (1 char for a nul)
+#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
+ HANDLE snapshot_; // filehandle on dll info
+ // In a change from the usual W-A pattern, there is no A variant of
+ // MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
+ // We want the original A variants, and this #undef is the only
+ // way I see to get them. Redefining it when we're done prevents us
+ // from affecting other .cc files.
+# ifdef MODULEENTRY32 // Alias of W
+# undef MODULEENTRY32
+ MODULEENTRY32 module_; // info about current dll (and dll iterator)
+# define MODULEENTRY32 MODULEENTRY32W
+# else // It's the ascii, the one we want.
+ MODULEENTRY32 module_; // info about current dll (and dll iterator)
+# endif
+#elif defined(__MACH__)
+ int current_image_; // dll's are called "images" in macos parlance
+ int current_load_cmd_; // the segment of this dll we're examining
+#elif defined(__sun__) // Solaris
+ int fd_;
+ char current_filename_[PATH_MAX];
+#else
+ int fd_; // filehandle on /proc/*/maps
+#endif
+ pid_t pid_;
+ char flags_[10];
+ Buffer* dynamic_buffer_; // dynamically-allocated Buffer
+ bool using_maps_backing_; // true if we are looking at maps_backing instead of maps.
+};
+
+#endif /* #ifndef SWIG */
+
+// Helper routines
+typedef int RawFD;
+const RawFD kIllegalRawFD = -1; // what open returns if it fails
+
+RawFD RawOpenForWriting(const char* filename); // uses default permissions
+void RawWrite(RawFD fd, const char* buf, size_t len);
+void RawClose(RawFD fd);
+
+int FillProcSelfMaps(char buf[], int size, bool* wrote_all);
+void DumpProcSelfMaps(RawFD fd);
+
+#endif /* #ifndef _SYSINFO_H_ */
diff --git a/lib/asan/tests/asan_benchmarks_test.cc b/lib/asan/tests/asan_benchmarks_test.cc
new file mode 100644
index 0000000..b72cc3f
--- /dev/null
+++ b/lib/asan/tests/asan_benchmarks_test.cc
@@ -0,0 +1,86 @@
+//===-- asan_benchmarks_test.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Some benchmarks for the instrumented code.
+//===----------------------------------------------------------------------===//
+
+#include "asan_test_config.h"
+#include "asan_test_utils.h"
+
+template<class T>
+__attribute__((noinline))
+static void ManyAccessFunc(T *x, size_t n_elements, size_t n_iter) {
+ for (size_t iter = 0; iter < n_iter; iter++) {
+ break_optimization(0);
+ // hand unroll the loop to stress the reg alloc.
+ for (size_t i = 0; i <= n_elements - 16; i += 16) {
+ x[i + 0] = i;
+ x[i + 1] = i;
+ x[i + 2] = i;
+ x[i + 3] = i;
+ x[i + 4] = i;
+ x[i + 5] = i;
+ x[i + 6] = i;
+ x[i + 7] = i;
+ x[i + 8] = i;
+ x[i + 9] = i;
+ x[i + 10] = i;
+ x[i + 11] = i;
+ x[i + 12] = i;
+ x[i + 13] = i;
+ x[i + 14] = i;
+ x[i + 15] = i;
+ }
+ }
+}
+
+TEST(AddressSanitizer, ManyAccessBenchmark) {
+ size_t kLen = 1024;
+ int *int_array = new int[kLen];
+ ManyAccessFunc(int_array, kLen, 1 << 24);
+ delete [] int_array;
+}
+
+// access 7 char elements in a 7 byte array (i.e. on the border).
+__attribute__((noinline))
+static void BorderAccessFunc(char *x, size_t n_iter) {
+ for (size_t iter = 0; iter < n_iter; iter++) {
+ break_optimization(x);
+ x[0] = 0;
+ x[1] = 0;
+ x[2] = 0;
+ x[3] = 0;
+ x[4] = 0;
+ x[5] = 0;
+ x[6] = 0;
+ }
+}
+
+TEST(AddressSanitizer, BorderAccessBenchmark) {
+ char *char_7_array = new char[7];
+ BorderAccessFunc(char_7_array, 1 << 30);
+ delete [] char_7_array;
+}
+
+static void FunctionWithLargeStack() {
+ int stack[1000];
+ Ident(stack);
+}
+
+TEST(AddressSanitizer, FakeStackBenchmark) {
+ for (int i = 0; i < 10000000; i++)
+ Ident(&FunctionWithLargeStack)();
+}
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/lib/asan/tests/asan_break_optimization.cc b/lib/asan/tests/asan_break_optimization.cc
new file mode 100644
index 0000000..acd0427
--- /dev/null
+++ b/lib/asan/tests/asan_break_optimization.cc
@@ -0,0 +1,18 @@
+//===-- asan_break_optimization.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+
+#include "asan_test_utils.h"
+// Have this function in a separate file to avoid inlining.
+// (Yes, we know about cross-file inlining, but let's assume we don't use it).
+extern "C" void break_optimization(void *x) {
+}
diff --git a/lib/asan/tests/asan_exceptions_test.cc b/lib/asan/tests/asan_exceptions_test.cc
new file mode 100644
index 0000000..ecd406d
--- /dev/null
+++ b/lib/asan/tests/asan_exceptions_test.cc
@@ -0,0 +1,27 @@
+// See http://llvm.org/bugs/show_bug.cgi?id=11468
+#include <stdio.h>
+#include <string>
+
+class Action {
+ public:
+ Action() {}
+ void PrintString(const std::string& msg) const {
+ fprintf(stderr, "%s\n", msg.c_str());
+ }
+ void Throw(const char& arg) const {
+ PrintString("PrintString called!"); // this line is important
+ throw arg;
+ }
+};
+
+int main() {
+ const Action a;
+ fprintf(stderr, "&a before = %p\n", &a);
+ try {
+ a.Throw('c');
+ } catch(const char&) {
+ fprintf(stderr, "&a in catch = %p\n", &a);
+ }
+ fprintf(stderr, "&a final = %p\n", &a);
+ return 0;
+}
diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc
new file mode 100644
index 0000000..2303f8b
--- /dev/null
+++ b/lib/asan/tests/asan_globals_test.cc
@@ -0,0 +1,24 @@
+//===-- asan_globals_test.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Some globals in a separate file.
+//===----------------------------------------------------------------------===//
+
+extern char glob5[5];
+static char static10[10];
+
+int GlobalsTest(int zero) {
+ static char func_static15[15];
+ glob5[zero] = 0;
+ static10[zero] = 0;
+ func_static15[zero] = 0;
+ return glob5[1] + func_static15[2];
+}
diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc
new file mode 100644
index 0000000..c26ed92
--- /dev/null
+++ b/lib/asan/tests/asan_interface_test.cc
@@ -0,0 +1,334 @@
+//===-- asan_interface_test.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asan_test_config.h"
+#include "asan_test_utils.h"
+#include "asan_interface.h"
+
+TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
+ EXPECT_EQ(1, __asan_get_estimated_allocated_size(0));
+ const size_t sizes[] = { 1, 30, 1<<30 };
+ for (size_t i = 0; i < 3; i++) {
+ EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i]));
+ }
+}
+
+static const char* kGetAllocatedSizeErrorMsg =
+ "__asan_get_allocated_size failed";
+
+TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
+ const size_t kArraySize = 100;
+ char *array = Ident((char*)malloc(kArraySize));
+ int *int_ptr = Ident(new int);
+
+ // Allocated memory is owned by allocator. Allocated size should be
+ // equal to requested size.
+ EXPECT_EQ(true, __asan_get_ownership(array));
+ EXPECT_EQ(kArraySize, __asan_get_allocated_size(array));
+ EXPECT_EQ(true, __asan_get_ownership(int_ptr));
+ EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr));
+
+ // We cannot call GetAllocatedSize from the memory we didn't map,
+ // and from the interior pointers (not returned by previous malloc).
+ void *wild_addr = (void*)0x1;
+ EXPECT_EQ(false, __asan_get_ownership(wild_addr));
+ EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg);
+ EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2));
+ EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2),
+ kGetAllocatedSizeErrorMsg);
+
+ // NULL is a valid argument and is owned.
+ EXPECT_EQ(true, __asan_get_ownership(NULL));
+ EXPECT_EQ(0, __asan_get_allocated_size(NULL));
+
+ // When memory is freed, it's not owned, and call to GetAllocatedSize
+ // is forbidden.
+ free(array);
+ EXPECT_EQ(false, __asan_get_ownership(array));
+ EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg);
+
+ delete int_ptr;
+}
+
+TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
+ size_t before_malloc, after_malloc, after_free;
+ char *array;
+ const size_t kMallocSize = 100;
+ before_malloc = __asan_get_current_allocated_bytes();
+
+ array = Ident((char*)malloc(kMallocSize));
+ after_malloc = __asan_get_current_allocated_bytes();
+ EXPECT_EQ(before_malloc + kMallocSize, after_malloc);
+
+ free(array);
+ after_free = __asan_get_current_allocated_bytes();
+ EXPECT_EQ(before_malloc, after_free);
+}
+
+static void DoDoubleFree() {
+ int *x = Ident(new int);
+ delete Ident(x);
+ delete Ident(x);
+}
+
+// This test is run in a separate process, so that large malloced
+// chunk won't remain in the free lists after the test.
+// Note: use ASSERT_* instead of EXPECT_* here.
+static void RunGetHeapSizeTestAndDie() {
+ size_t old_heap_size, new_heap_size, heap_growth;
+ // We unlikely have have chunk of this size in free list.
+ static const size_t kLargeMallocSize = 1 << 29; // 512M
+ old_heap_size = __asan_get_heap_size();
+ fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
+ free(Ident(malloc(kLargeMallocSize)));
+ new_heap_size = __asan_get_heap_size();
+ heap_growth = new_heap_size - old_heap_size;
+ fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth);
+ ASSERT_GE(heap_growth, kLargeMallocSize);
+ ASSERT_LE(heap_growth, 2 * kLargeMallocSize);
+
+ // Now large chunk should fall into free list, and can be
+ // allocated without increasing heap size.
+ old_heap_size = new_heap_size;
+ free(Ident(malloc(kLargeMallocSize)));
+ heap_growth = __asan_get_heap_size() - old_heap_size;
+ fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth);
+ ASSERT_LT(heap_growth, kLargeMallocSize);
+
+ // Test passed. Now die with expected double-free.
+ DoDoubleFree();
+}
+
+TEST(AddressSanitizerInterface, GetHeapSizeTest) {
+ EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free");
+}
+
+// Note: use ASSERT_* instead of EXPECT_* here.
+static void DoLargeMallocForGetFreeBytesTestAndDie() {
+ size_t old_free_bytes, new_free_bytes;
+ static const size_t kLargeMallocSize = 1 << 29; // 512M
+ // If we malloc and free a large memory chunk, it will not fall
+ // into quarantine and will be available for future requests.
+ old_free_bytes = __asan_get_free_bytes();
+ fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
+ fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes);
+ free(Ident(malloc(kLargeMallocSize)));
+ new_free_bytes = __asan_get_free_bytes();
+ fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes);
+ ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize);
+ // Test passed.
+ DoDoubleFree();
+}
+
+TEST(AddressSanitizerInterface, GetFreeBytesTest) {
+ static const size_t kNumOfChunks = 100;
+ static const size_t kChunkSize = 100;
+ char *chunks[kNumOfChunks];
+ size_t i;
+ size_t old_free_bytes, new_free_bytes;
+ // Allocate a small chunk. Now allocator probably has a lot of these
+ // chunks to fulfill future requests. So, future requests will decrease
+ // the number of free bytes.
+ chunks[0] = Ident((char*)malloc(kChunkSize));
+ old_free_bytes = __asan_get_free_bytes();
+ for (i = 1; i < kNumOfChunks; i++) {
+ chunks[i] = Ident((char*)malloc(kChunkSize));
+ new_free_bytes = __asan_get_free_bytes();
+ EXPECT_LT(new_free_bytes, old_free_bytes);
+ old_free_bytes = new_free_bytes;
+ }
+ // Deleting these chunks will move them to quarantine, number of free
+ // bytes won't increase.
+ for (i = 0; i < kNumOfChunks; i++) {
+ free(chunks[i]);
+ EXPECT_EQ(old_free_bytes, __asan_get_free_bytes());
+ }
+ EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free");
+}
+
+static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357};
+static const size_t kManyThreadsIterations = 250;
+static const size_t kManyThreadsNumThreads = 200;
+
+void *ManyThreadsWithStatsWorker(void *arg) {
+ for (size_t iter = 0; iter < kManyThreadsIterations; iter++) {
+ for (size_t size_index = 0; size_index < 4; size_index++) {
+ free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
+ }
+ }
+ return 0;
+}
+
+TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
+ size_t before_test, after_test, i;
+ pthread_t threads[kManyThreadsNumThreads];
+ before_test = __asan_get_current_allocated_bytes();
+ for (i = 0; i < kManyThreadsNumThreads; i++) {
+ pthread_create(&threads[i], 0,
+ (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
+ }
+ for (i = 0; i < kManyThreadsNumThreads; i++) {
+ pthread_join(threads[i], 0);
+ }
+ after_test = __asan_get_current_allocated_bytes();
+ // ASan stats also reflect memory usage of internal ASan RTL structs,
+ // so we can't check for equality here.
+ EXPECT_LT(after_test, before_test + (1UL<<20));
+}
+
+TEST(AddressSanitizerInterface, ExitCode) {
+ int original_exit_code = __asan_set_error_exit_code(7);
+ EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), "");
+ EXPECT_EQ(7, __asan_set_error_exit_code(8));
+ EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), "");
+ EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code));
+ EXPECT_EXIT(DoDoubleFree(),
+ ::testing::ExitedWithCode(original_exit_code), "");
+}
+
+static const char* kUseAfterPoisonErrorMessage = "use-after-poison";
+
+#define ACCESS(ptr, offset) Ident(*(ptr + offset))
+
+#define DIE_ON_ACCESS(ptr, offset) \
+ EXPECT_DEATH(Ident(*(ptr + offset)), kUseAfterPoisonErrorMessage)
+
+TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) {
+ char *array = Ident((char*)malloc(120));
+ // poison array[40..80)
+ ASAN_POISON_MEMORY_REGION(array + 40, 40);
+ ACCESS(array, 39);
+ ACCESS(array, 80);
+ DIE_ON_ACCESS(array, 40);
+ DIE_ON_ACCESS(array, 60);
+ DIE_ON_ACCESS(array, 79);
+ ASAN_UNPOISON_MEMORY_REGION(array + 40, 40);
+ // access previously poisoned memory.
+ ACCESS(array, 40);
+ ACCESS(array, 79);
+ free(array);
+}
+
+TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) {
+ char *array = Ident((char*)malloc(120));
+ // Poison [0..40) and [80..120)
+ ASAN_POISON_MEMORY_REGION(array, 40);
+ ASAN_POISON_MEMORY_REGION(array + 80, 40);
+ DIE_ON_ACCESS(array, 20);
+ ACCESS(array, 60);
+ DIE_ON_ACCESS(array, 100);
+ // Poison whole array - [0..120)
+ ASAN_POISON_MEMORY_REGION(array, 120);
+ DIE_ON_ACCESS(array, 60);
+ // Unpoison [24..96)
+ ASAN_UNPOISON_MEMORY_REGION(array + 24, 72);
+ DIE_ON_ACCESS(array, 23);
+ ACCESS(array, 24);
+ ACCESS(array, 60);
+ ACCESS(array, 95);
+ DIE_ON_ACCESS(array, 96);
+ free(array);
+}
+
+TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) {
+ // Vector of capacity 20
+ char *vec = Ident((char*)malloc(20));
+ ASAN_POISON_MEMORY_REGION(vec, 20);
+ for (size_t i = 0; i < 7; i++) {
+ // Simulate push_back.
+ ASAN_UNPOISON_MEMORY_REGION(vec + i, 1);
+ ACCESS(vec, i);
+ DIE_ON_ACCESS(vec, i + 1);
+ }
+ for (size_t i = 7; i > 0; i--) {
+ // Simulate pop_back.
+ ASAN_POISON_MEMORY_REGION(vec + i - 1, 1);
+ DIE_ON_ACCESS(vec, i - 1);
+ if (i > 1) ACCESS(vec, i - 2);
+ }
+ free(vec);
+}
+
+// Make sure that each aligned block of size "2^granularity" doesn't have
+// "true" value before "false" value.
+static void MakeShadowValid(bool *shadow, int length, int granularity) {
+ bool can_be_poisoned = true;
+ for (int i = length - 1; i >= 0; i--) {
+ can_be_poisoned &= shadow[i];
+ shadow[i] &= can_be_poisoned;
+ if (i % (1 << granularity) == 0) {
+ can_be_poisoned = true;
+ }
+ }
+}
+
+TEST(AddressSanitizerInterface, PoisoningStressTest) {
+ const size_t kSize = 24;
+ bool expected[kSize];
+ char *arr = Ident((char*)malloc(kSize));
+ for (size_t l1 = 0; l1 < kSize; l1++) {
+ for (size_t s1 = 1; l1 + s1 <= kSize; s1++) {
+ for (size_t l2 = 0; l2 < kSize; l2++) {
+ for (size_t s2 = 1; l2 + s2 <= kSize; s2++) {
+ // Poison [l1, l1+s1), [l2, l2+s2) and check result.
+ ASAN_UNPOISON_MEMORY_REGION(arr, kSize);
+ ASAN_POISON_MEMORY_REGION(arr + l1, s1);
+ ASAN_POISON_MEMORY_REGION(arr + l2, s2);
+ memset(expected, false, kSize);
+ memset(expected + l1, true, s1);
+ MakeShadowValid(expected, 24, /*granularity*/ 3);
+ memset(expected + l2, true, s2);
+ MakeShadowValid(expected, 24, /*granularity*/ 3);
+ for (size_t i = 0; i < kSize; i++) {
+ ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
+ }
+ // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result.
+ ASAN_POISON_MEMORY_REGION(arr, kSize);
+ ASAN_UNPOISON_MEMORY_REGION(arr + l1, s1);
+ ASAN_UNPOISON_MEMORY_REGION(arr + l2, s2);
+ memset(expected, true, kSize);
+ memset(expected + l1, false, s1);
+ MakeShadowValid(expected, 24, /*granularity*/ 3);
+ memset(expected + l2, false, s2);
+ MakeShadowValid(expected, 24, /*granularity*/ 3);
+ for (size_t i = 0; i < kSize; i++) {
+ ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
+ }
+ }
+ }
+ }
+ }
+}
+
+static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
+static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
+
+TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
+ char *array = Ident((char*)malloc(120));
+ ASAN_UNPOISON_MEMORY_REGION(array, 120);
+ // Try to unpoison not owned memory
+ EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array, 121),
+ kInvalidUnpoisonMessage);
+ EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array - 1, 120),
+ kInvalidUnpoisonMessage);
+
+ ASAN_POISON_MEMORY_REGION(array, 120);
+ // Try to poison not owned memory.
+ EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array, 121), kInvalidPoisonMessage);
+ EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array - 1, 120),
+ kInvalidPoisonMessage);
+ free(array);
+}
diff --git a/lib/asan/tests/asan_mac_test.h b/lib/asan/tests/asan_mac_test.h
new file mode 100644
index 0000000..e3ad827
--- /dev/null
+++ b/lib/asan/tests/asan_mac_test.h
@@ -0,0 +1,16 @@
+extern "C" {
+ void CFAllocatorDefaultDoubleFree();
+ void CFAllocatorSystemDefaultDoubleFree();
+ void CFAllocatorMallocDoubleFree();
+ void CFAllocatorMallocZoneDoubleFree();
+ void CallFreeOnWorkqueue(void *mem);
+ void TestGCDDispatchAsync();
+ void TestGCDDispatchSync();
+ void TestGCDReuseWqthreadsAsync();
+ void TestGCDReuseWqthreadsSync();
+ void TestGCDDispatchAfter();
+ void TestGCDInTSDDestructor();
+ void TestGCDSourceEvent();
+ void TestGCDSourceCancel();
+ void TestGCDGroupAsync();
+}
diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test.mm
new file mode 100644
index 0000000..b5dbbde
--- /dev/null
+++ b/lib/asan/tests/asan_mac_test.mm
@@ -0,0 +1,203 @@
+// Mac OS X 10.6 or higher only.
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#import <CoreFoundation/CFBase.h>
+#import <Foundation/NSObject.h>
+
+void CFAllocatorDefaultDoubleFree() {
+ void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
+ CFAllocatorDeallocate(kCFAllocatorDefault, mem);
+ CFAllocatorDeallocate(kCFAllocatorDefault, mem);
+}
+
+void CFAllocatorSystemDefaultDoubleFree() {
+ void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
+ CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
+ CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
+}
+
+void CFAllocatorMallocDoubleFree() {
+ void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
+ CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
+ CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
+}
+
+void CFAllocatorMallocZoneDoubleFree() {
+ void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
+ CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
+ CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
+}
+
+
+// Test the +load instrumentation.
+// Because the +load methods are invoked before anything else is initialized,
+// it makes little sense to wrap the code below into a gTest test case.
+// If AddressSanitizer doesn't instrument the +load method below correctly,
+// everything will just crash.
+
+char kStartupStr[] =
+ "If your test didn't crash, AddressSanitizer is instrumenting "
+ "the +load methods correctly.";
+
+@interface LoadSomething : NSObject {
+}
+@end
+
+@implementation LoadSomething
+
++(void) load {
+ for (int i = 0; i < strlen(kStartupStr); i++) {
+ volatile char ch = kStartupStr[i]; // make sure no optimizations occur.
+ }
+ // Don't print anything here not to interfere with the death tests.
+}
+
+@end
+
+void worker_do_alloc(int size) {
+ char * volatile mem = malloc(size);
+ mem[0] = 0; // Ok
+ free(mem);
+}
+
+void worker_do_crash(int size) {
+ char * volatile mem = malloc(size);
+ mem[size] = 0; // BOOM
+ free(mem);
+}
+
+// Tests for the Grand Central Dispatch. See
+// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
+// for the reference.
+
+void TestGCDDispatchAsync() {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_block_t block = ^{ worker_do_crash(1024); };
+ // dispatch_async() runs the task on a worker thread that does not go through
+ // pthread_create(). We need to verify that AddressSanitizer notices that the
+ // thread has started.
+ dispatch_async(queue, block);
+ // TODO(glider): this is hacky. Need to wait for the worker instead.
+ sleep(1);
+}
+
+void TestGCDDispatchSync() {
+ dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
+ dispatch_block_t block = ^{ worker_do_crash(1024); };
+ // dispatch_sync() runs the task on a worker thread that does not go through
+ // pthread_create(). We need to verify that AddressSanitizer notices that the
+ // thread has started.
+ dispatch_sync(queue, block);
+ // TODO(glider): this is hacky. Need to wait for the worker instead.
+ sleep(1);
+}
+
+// libdispatch spawns a rather small number of threads and reuses them. We need
+// to make sure AddressSanitizer handles the reusing correctly.
+void TestGCDReuseWqthreadsAsync() {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
+ dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
+ for (int i = 0; i < 100; i++) {
+ dispatch_async(queue, block_alloc);
+ }
+ dispatch_async(queue, block_crash);
+ // TODO(glider): this is hacky. Need to wait for the workers instead.
+ sleep(1);
+}
+
+// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
+void TestGCDReuseWqthreadsSync() {
+ dispatch_queue_t queue[4];
+ queue[0] = dispatch_get_global_queue(2, 0);
+ queue[1] = dispatch_get_global_queue(0, 0);
+ queue[2] = dispatch_get_global_queue(-2, 0);
+ queue[3] = dispatch_queue_create("my_queue", NULL);
+ dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
+ dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
+ for (int i = 0; i < 1000; i++) {
+ dispatch_sync(queue[i % 4], block_alloc);
+ }
+ dispatch_sync(queue[3], block_crash);
+ // TODO(glider): this is hacky. Need to wait for the workers instead.
+ sleep(1);
+}
+
+void TestGCDDispatchAfter() {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
+ // Schedule the event one second from the current time.
+ dispatch_time_t milestone =
+ dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
+ dispatch_after(milestone, queue, block_crash);
+ // Let's wait for a bit longer now.
+ // TODO(glider): this is still hacky.
+ sleep(2);
+}
+
+void worker_do_deallocate(void *ptr) {
+ free(ptr);
+}
+
+void CallFreeOnWorkqueue(void *tsd) {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
+ dispatch_async(queue, block_dealloc);
+ // Do not wait for the worker to free the memory -- nobody is going to touch
+ // it.
+}
+
+void TestGCDSourceEvent() {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_source_t timer =
+ dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
+ // Schedule the timer one second from the current time.
+ dispatch_time_t milestone =
+ dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
+
+ dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
+ char * volatile mem = malloc(10);
+ dispatch_source_set_event_handler(timer, ^{
+ mem[10] = 1;
+ });
+ dispatch_resume(timer);
+ sleep(2);
+}
+
+void TestGCDSourceCancel() {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_source_t timer =
+ dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
+ // Schedule the timer one second from the current time.
+ dispatch_time_t milestone =
+ dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
+
+ dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
+ char * volatile mem = malloc(10);
+ // Both dispatch_source_set_cancel_handler() and
+ // dispatch_source_set_event_handler() use dispatch_barrier_async_f().
+ // It's tricky to test dispatch_source_set_cancel_handler() separately,
+ // so we test both here.
+ dispatch_source_set_event_handler(timer, ^{
+ dispatch_source_cancel(timer);
+ });
+ dispatch_source_set_cancel_handler(timer, ^{
+ mem[10] = 1;
+ });
+ dispatch_resume(timer);
+ sleep(2);
+}
+
+void TestGCDGroupAsync() {
+ dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+ dispatch_group_t group = dispatch_group_create();
+ char * volatile mem = malloc(10);
+ dispatch_group_async(group, queue, ^{
+ mem[10] = 1;
+ });
+ dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+}
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
new file mode 100644
index 0000000..204c0da
--- /dev/null
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -0,0 +1,329 @@
+//===-- asan_noinst_test.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This test file should be compiled w/o asan instrumentation.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_interface.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_stack.h"
+#include "asan_test_utils.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+#include <algorithm>
+#include "gtest/gtest.h"
+
+// Simple stand-alone pseudorandom number generator.
+// Current algorithm is ANSI C linear congruential PRNG.
+static inline uint32_t my_rand(uint32_t* state) {
+ return (*state = *state * 1103515245 + 12345) >> 16;
+}
+
+static uint32_t global_seed = 0;
+
+
+TEST(AddressSanitizer, InternalSimpleDeathTest) {
+ EXPECT_DEATH(exit(1), "");
+}
+
+static void MallocStress(size_t n) {
+ uint32_t seed = my_rand(&global_seed);
+ __asan::AsanStackTrace stack1;
+ stack1.trace[0] = 0xa123;
+ stack1.trace[1] = 0xa456;
+ stack1.size = 2;
+
+ __asan::AsanStackTrace stack2;
+ stack2.trace[0] = 0xb123;
+ stack2.trace[1] = 0xb456;
+ stack2.size = 2;
+
+ __asan::AsanStackTrace stack3;
+ stack3.trace[0] = 0xc123;
+ stack3.trace[1] = 0xc456;
+ stack3.size = 2;
+
+ std::vector<void *> vec;
+ for (size_t i = 0; i < n; i++) {
+ if ((i % 3) == 0) {
+ if (vec.empty()) continue;
+ size_t idx = my_rand(&seed) % vec.size();
+ void *ptr = vec[idx];
+ vec[idx] = vec.back();
+ vec.pop_back();
+ __asan::asan_free(ptr, &stack1);
+ } else {
+ size_t size = my_rand(&seed) % 1000 + 1;
+ switch ((my_rand(&seed) % 128)) {
+ case 0: size += 1024; break;
+ case 1: size += 2048; break;
+ case 2: size += 4096; break;
+ }
+ size_t alignment = 1 << (my_rand(&seed) % 10 + 1);
+ char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2);
+ vec.push_back(ptr);
+ ptr[0] = 0;
+ ptr[size-1] = 0;
+ ptr[size/2] = 0;
+ }
+ }
+ for (size_t i = 0; i < vec.size(); i++)
+ __asan::asan_free(vec[i], &stack3);
+}
+
+
+TEST(AddressSanitizer, NoInstMallocTest) {
+#ifdef __arm__
+ MallocStress(300000);
+#else
+ MallocStress(1000000);
+#endif
+}
+
+static void PrintShadow(const char *tag, uintptr_t ptr, size_t size) {
+ fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size);
+ uintptr_t prev_shadow = 0;
+ for (intptr_t i = -32; i < (intptr_t)size + 32; i++) {
+ uintptr_t shadow = __asan::MemToShadow(ptr + i);
+ if (i == 0 || i == (intptr_t)size)
+ fprintf(stderr, ".");
+ if (shadow != prev_shadow) {
+ prev_shadow = shadow;
+ fprintf(stderr, "%02x", (int)*(uint8_t*)shadow);
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+TEST(AddressSanitizer, DISABLED_InternalPrintShadow) {
+ for (size_t size = 1; size <= 513; size++) {
+ char *ptr = new char[size];
+ PrintShadow("m", (uintptr_t)ptr, size);
+ delete [] ptr;
+ PrintShadow("f", (uintptr_t)ptr, size);
+ }
+}
+
+static uintptr_t pc_array[] = {
+#if __WORDSIZE == 64
+ 0x7effbf756068ULL,
+ 0x7effbf75e5abULL,
+ 0x7effc0625b7cULL,
+ 0x7effc05b8997ULL,
+ 0x7effbf990577ULL,
+ 0x7effbf990c56ULL,
+ 0x7effbf992f3cULL,
+ 0x7effbf950c22ULL,
+ 0x7effc036dba0ULL,
+ 0x7effc03638a3ULL,
+ 0x7effc035be4aULL,
+ 0x7effc0539c45ULL,
+ 0x7effc0539a65ULL,
+ 0x7effc03db9b3ULL,
+ 0x7effc03db100ULL,
+ 0x7effc037c7b8ULL,
+ 0x7effc037bfffULL,
+ 0x7effc038b777ULL,
+ 0x7effc038021cULL,
+ 0x7effc037c7d1ULL,
+ 0x7effc037bfffULL,
+ 0x7effc038b777ULL,
+ 0x7effc038021cULL,
+ 0x7effc037c7d1ULL,
+ 0x7effc037bfffULL,
+ 0x7effc038b777ULL,
+ 0x7effc038021cULL,
+ 0x7effc037c7d1ULL,
+ 0x7effc037bfffULL,
+ 0x7effc0520d26ULL,
+ 0x7effc009ddffULL,
+ 0x7effbf90bb50ULL,
+ 0x7effbdddfa69ULL,
+ 0x7effbdde1fe2ULL,
+ 0x7effbdde2424ULL,
+ 0x7effbdde27b3ULL,
+ 0x7effbddee53bULL,
+ 0x7effbdde1988ULL,
+ 0x7effbdde0904ULL,
+ 0x7effc106ce0dULL,
+ 0x7effbcc3fa04ULL,
+ 0x7effbcc3f6a4ULL,
+ 0x7effbcc3e726ULL,
+ 0x7effbcc40852ULL,
+ 0x7effb681ec4dULL,
+#endif // __WORDSIZE
+ 0xB0B5E768,
+ 0x7B682EC1,
+ 0x367F9918,
+ 0xAE34E13,
+ 0xBA0C6C6,
+ 0x13250F46,
+ 0xA0D6A8AB,
+ 0x2B07C1A8,
+ 0x6C844F4A,
+ 0x2321B53,
+ 0x1F3D4F8F,
+ 0x3FE2924B,
+ 0xB7A2F568,
+ 0xBD23950A,
+ 0x61020930,
+ 0x33E7970C,
+ 0x405998A1,
+ 0x59F3551D,
+ 0x350E3028,
+ 0xBC55A28D,
+ 0x361F3AED,
+ 0xBEAD0F73,
+ 0xAEF28479,
+ 0x757E971F,
+ 0xAEBA450,
+ 0x43AD22F5,
+ 0x8C2C50C4,
+ 0x7AD8A2E1,
+ 0x69EE4EE8,
+ 0xC08DFF,
+ 0x4BA6538,
+ 0x3708AB2,
+ 0xC24B6475,
+ 0x7C8890D7,
+ 0x6662495F,
+ 0x9B641689,
+ 0xD3596B,
+ 0xA1049569,
+ 0x44CBC16,
+ 0x4D39C39F
+};
+
+void CompressStackTraceTest(size_t n_iter) {
+ uint32_t seed = my_rand(&global_seed);
+ const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
+ uint32_t compressed[2 * kNumPcs];
+
+ for (size_t iter = 0; iter < n_iter; iter++) {
+ std::random_shuffle(pc_array, pc_array + kNumPcs);
+ __asan::AsanStackTrace stack0, stack1;
+ stack0.CopyFrom(pc_array, kNumPcs);
+ stack0.size = std::max((size_t)1, (size_t)my_rand(&seed) % stack0.size);
+ size_t compress_size =
+ std::max((size_t)2, (size_t)my_rand(&seed) % (2 * kNumPcs));
+ size_t n_frames =
+ __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
+ assert(n_frames <= stack0.size);
+ __asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size);
+ assert(stack1.size == n_frames);
+ for (size_t i = 0; i < stack1.size; i++) {
+ assert(stack0.trace[i] == stack1.trace[i]);
+ }
+ }
+}
+
+TEST(AddressSanitizer, CompressStackTraceTest) {
+ CompressStackTraceTest(10000);
+}
+
+void CompressStackTraceBenchmark(size_t n_iter) {
+ const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
+ uint32_t compressed[2 * kNumPcs];
+ std::random_shuffle(pc_array, pc_array + kNumPcs);
+
+ __asan::AsanStackTrace stack0;
+ stack0.CopyFrom(pc_array, kNumPcs);
+ stack0.size = kNumPcs;
+ for (size_t iter = 0; iter < n_iter; iter++) {
+ size_t compress_size = kNumPcs;
+ size_t n_frames =
+ __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
+ Ident(n_frames);
+ }
+}
+
+TEST(AddressSanitizer, CompressStackTraceBenchmark) {
+ CompressStackTraceBenchmark(1 << 24);
+}
+
+TEST(AddressSanitizer, QuarantineTest) {
+ __asan::AsanStackTrace stack;
+ stack.trace[0] = 0x890;
+ stack.size = 1;
+
+ const int size = 32;
+ void *p = __asan::asan_malloc(size, &stack);
+ __asan::asan_free(p, &stack);
+ size_t i;
+ size_t max_i = 1 << 30;
+ for (i = 0; i < max_i; i++) {
+ void *p1 = __asan::asan_malloc(size, &stack);
+ __asan::asan_free(p1, &stack);
+ if (p1 == p) break;
+ }
+ // fprintf(stderr, "i=%ld\n", i);
+ EXPECT_GE(i, 100000U);
+ EXPECT_LT(i, max_i);
+}
+
+void *ThreadedQuarantineTestWorker(void *unused) {
+ uint32_t seed = my_rand(&global_seed);
+ __asan::AsanStackTrace stack;
+ stack.trace[0] = 0x890;
+ stack.size = 1;
+
+ for (size_t i = 0; i < 1000; i++) {
+ void *p = __asan::asan_malloc(1 + (my_rand(&seed) % 4000), &stack);
+ __asan::asan_free(p, &stack);
+ }
+ return NULL;
+}
+
+// Check that the thread local allocators are flushed when threads are
+// destroyed.
+TEST(AddressSanitizer, ThreadedQuarantineTest) {
+ const int n_threads = 3000;
+ size_t mmaped1 = __asan_get_heap_size();
+ for (int i = 0; i < n_threads; i++) {
+ pthread_t t;
+ pthread_create(&t, NULL, ThreadedQuarantineTestWorker, 0);
+ pthread_join(t, 0);
+ size_t mmaped2 = __asan_get_heap_size();
+ EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
+ }
+}
+
+void *ThreadedOneSizeMallocStress(void *unused) {
+ __asan::AsanStackTrace stack;
+ stack.trace[0] = 0x890;
+ stack.size = 1;
+ const size_t kNumMallocs = 1000;
+ for (int iter = 0; iter < 1000; iter++) {
+ void *p[kNumMallocs];
+ for (size_t i = 0; i < kNumMallocs; i++) {
+ p[i] = __asan::asan_malloc(32, &stack);
+ }
+ for (size_t i = 0; i < kNumMallocs; i++) {
+ __asan::asan_free(p[i], &stack);
+ }
+ }
+ return NULL;
+}
+
+TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
+ const int kNumThreads = 4;
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_create(&t[i], 0, ThreadedOneSizeMallocStress, 0);
+ }
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_join(t[i], 0);
+ }
+}
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
new file mode 100644
index 0000000..0ff72d3
--- /dev/null
+++ b/lib/asan/tests/asan_test.cc
@@ -0,0 +1,2022 @@
+//===-- asan_test.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <assert.h>
+
+#if defined(__i386__) or defined(__x86_64__)
+#include <emmintrin.h>
+#endif
+
+#include "asan_test_config.h"
+#include "asan_test_utils.h"
+
+#ifndef __APPLE__
+#include <malloc.h>
+#endif // __APPLE__
+
+#ifdef __APPLE__
+static bool APPLE = true;
+#else
+static bool APPLE = false;
+#endif
+
+#if ASAN_HAS_EXCEPTIONS
+# define ASAN_THROW(x) throw (x)
+#else
+# define ASAN_THROW(x)
+#endif
+
+#include <sys/mman.h>
+
+typedef uint8_t U1;
+typedef uint16_t U2;
+typedef uint32_t U4;
+typedef uint64_t U8;
+
+static const char *progname;
+static const int kPageSize = 4096;
+
+// Simple stand-alone pseudorandom number generator.
+// Current algorithm is ANSI C linear congruential PRNG.
+static inline uint32_t my_rand(uint32_t* state) {
+ return (*state = *state * 1103515245 + 12345) >> 16;
+}
+
+static uint32_t global_seed = 0;
+
+class ObjdumpOfMyself {
+ public:
+ explicit ObjdumpOfMyself(const string &binary) {
+ is_correct = true;
+ string objdump_name = APPLE ? "gobjdump" : "objdump";
+ string prog = objdump_name + " -d " + binary;
+ // TODO(glider): popen() succeeds even if the file does not exist.
+ FILE *pipe = popen(prog.c_str(), "r");
+ string objdump;
+ if (pipe) {
+ const int kBuffSize = 4096;
+ char buff[kBuffSize+1];
+ int read_bytes;
+ while ((read_bytes = fread(buff, 1, kBuffSize, pipe)) > 0) {
+ buff[read_bytes] = 0;
+ objdump.append(buff);
+ }
+ pclose(pipe);
+ } else {
+ is_correct = false;
+ }
+ // cut the objdump into functions
+ string fn, next_fn;
+ size_t next_start;
+ for (size_t start = fn_start(objdump, 0, &fn);
+ start != string::npos;
+ start = next_start, fn = next_fn) {
+ next_start = fn_start(objdump, start, &next_fn);
+ // fprintf(stderr, "start: %d next_start = %d fn: %s\n",
+ // (int)start, (int)next_start, fn.c_str());
+ // Mac OS adds the "_" prefix to function names.
+ if (fn.find(APPLE ? "_Disasm" : "Disasm") == string::npos) {
+ continue;
+ }
+ string fn_body = objdump.substr(start, next_start - start);
+ // fprintf(stderr, "%s:\n%s", fn.c_str(), fn_body.c_str());
+ functions_[fn] = fn_body;
+ }
+ }
+
+ string &GetFuncDisasm(const string &fn) {
+ return functions_[fn];
+ }
+
+ int CountInsnInFunc(const string &fn, const vector<string> &insns) {
+ // Mac OS adds the "_" prefix to function names.
+ string fn_ref = APPLE ? "_" + fn : fn;
+ const string &disasm = GetFuncDisasm(fn_ref);
+ if (disasm.empty()) return -1;
+ size_t counter = 0;
+ for (size_t i = 0; i < insns.size(); i++) {
+ size_t pos = 0;
+ while ((pos = disasm.find(insns[i], pos)) != string::npos) {
+ counter++;
+ pos++;
+ }
+ }
+ return counter;
+ }
+
+ bool IsCorrect() { return is_correct; }
+
+ private:
+ size_t fn_start(const string &objdump, size_t start_pos, string *fn) {
+ size_t pos = objdump.find(">:\n", start_pos);
+ if (pos == string::npos)
+ return string::npos;
+ size_t beg = pos;
+ while (beg > 0 && objdump[beg - 1] != '<')
+ beg--;
+ *fn = objdump.substr(beg, pos - beg);
+ return pos + 3;
+ }
+
+ map<string, string> functions_;
+ bool is_correct;
+};
+
+static ObjdumpOfMyself *objdump_of_myself() {
+ static ObjdumpOfMyself *o = new ObjdumpOfMyself(progname);
+ return o;
+}
+
+const size_t kLargeMalloc = 1 << 24;
+
+template<class T>
+__attribute__((noinline))
+void asan_write(T *a) {
+ *a = 0;
+}
+
+__attribute__((noinline))
+void asan_write_sized_aligned(uint8_t *p, size_t size) {
+ EXPECT_EQ(0, ((uintptr_t)p % size));
+ if (size == 1) asan_write((uint8_t*)p);
+ else if (size == 2) asan_write((uint16_t*)p);
+ else if (size == 4) asan_write((uint32_t*)p);
+ else if (size == 8) asan_write((uint64_t*)p);
+}
+
+__attribute__((noinline)) void *malloc_fff(size_t size) {
+ void *res = malloc/**/(size); break_optimization(0); return res;}
+__attribute__((noinline)) void *malloc_eee(size_t size) {
+ void *res = malloc_fff(size); break_optimization(0); return res;}
+__attribute__((noinline)) void *malloc_ddd(size_t size) {
+ void *res = malloc_eee(size); break_optimization(0); return res;}
+__attribute__((noinline)) void *malloc_ccc(size_t size) {
+ void *res = malloc_ddd(size); break_optimization(0); return res;}
+__attribute__((noinline)) void *malloc_bbb(size_t size) {
+ void *res = malloc_ccc(size); break_optimization(0); return res;}
+__attribute__((noinline)) void *malloc_aaa(size_t size) {
+ void *res = malloc_bbb(size); break_optimization(0); return res;}
+
+#ifndef __APPLE__
+__attribute__((noinline)) void *memalign_fff(size_t alignment, size_t size) {
+ void *res = memalign/**/(alignment, size); break_optimization(0); return res;}
+__attribute__((noinline)) void *memalign_eee(size_t alignment, size_t size) {
+ void *res = memalign_fff(alignment, size); break_optimization(0); return res;}
+__attribute__((noinline)) void *memalign_ddd(size_t alignment, size_t size) {
+ void *res = memalign_eee(alignment, size); break_optimization(0); return res;}
+__attribute__((noinline)) void *memalign_ccc(size_t alignment, size_t size) {
+ void *res = memalign_ddd(alignment, size); break_optimization(0); return res;}
+__attribute__((noinline)) void *memalign_bbb(size_t alignment, size_t size) {
+ void *res = memalign_ccc(alignment, size); break_optimization(0); return res;}
+__attribute__((noinline)) void *memalign_aaa(size_t alignment, size_t size) {
+ void *res = memalign_bbb(alignment, size); break_optimization(0); return res;}
+#endif // __APPLE__
+
+
+__attribute__((noinline))
+ void free_ccc(void *p) { free(p); break_optimization(0);}
+__attribute__((noinline))
+ void free_bbb(void *p) { free_ccc(p); break_optimization(0);}
+__attribute__((noinline))
+ void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
+
+template<class T>
+__attribute__((noinline))
+void oob_test(int size, int off) {
+ char *p = (char*)malloc_aaa(size);
+ // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n",
+ // sizeof(T), p, p + size, off);
+ asan_write((T*)(p + off));
+ free_aaa(p);
+}
+
+
+template<class T>
+__attribute__((noinline))
+void uaf_test(int size, int off) {
+ char *p = (char *)malloc_aaa(size);
+ free_aaa(p);
+ for (int i = 1; i < 100; i++)
+ free_aaa(malloc_aaa(i));
+ fprintf(stderr, "writing %ld byte(s) at %p with offset %d\n",
+ (long)sizeof(T), p, off);
+ asan_write((T*)(p + off));
+}
+
+TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) {
+#if defined(__has_feature) && __has_feature(address_sanitizer)
+ bool asan = 1;
+#else
+ bool asan = 0;
+#endif
+ EXPECT_EQ(true, asan);
+}
+
+TEST(AddressSanitizer, SimpleDeathTest) {
+ EXPECT_DEATH(exit(1), "");
+}
+
+TEST(AddressSanitizer, VariousMallocsTest) {
+ // fprintf(stderr, "malloc:\n");
+ int *a = (int*)malloc(100 * sizeof(int));
+ a[50] = 0;
+ free(a);
+
+ // fprintf(stderr, "realloc:\n");
+ int *r = (int*)malloc(10);
+ r = (int*)realloc(r, 2000 * sizeof(int));
+ r[1000] = 0;
+ free(r);
+
+ // fprintf(stderr, "operator new []\n");
+ int *b = new int[100];
+ b[50] = 0;
+ delete [] b;
+
+ // fprintf(stderr, "operator new\n");
+ int *c = new int;
+ *c = 0;
+ delete c;
+
+#ifndef __APPLE__
+ // fprintf(stderr, "posix_memalign\n");
+ int *pm;
+ int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize);
+ EXPECT_EQ(0, pm_res);
+ free(pm);
+
+ int *ma = (int*)memalign(kPageSize, kPageSize);
+ EXPECT_EQ(0, (uintptr_t)ma % kPageSize);
+ ma[123] = 0;
+ free(ma);
+#endif // __APPLE__
+}
+
+TEST(AddressSanitizer, CallocTest) {
+ int *a = (int*)calloc(100, sizeof(int));
+ EXPECT_EQ(0, a[10]);
+ free(a);
+}
+
+TEST(AddressSanitizer, VallocTest) {
+ void *a = valloc(100);
+ EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ free(a);
+}
+
+#ifndef __APPLE__
+TEST(AddressSanitizer, PvallocTest) {
+ char *a = (char*)pvalloc(kPageSize + 100);
+ EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ a[kPageSize + 101] = 1; // we should not report an error here.
+ free(a);
+
+ a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page.
+ EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ a[101] = 1; // we should not report an error here.
+ free(a);
+}
+#endif // __APPLE__
+
+void NoOpSignalHandler(int unused) {
+ fprintf(stderr, "NoOpSignalHandler (should not happen). Aborting\n");
+ abort();
+}
+
+void NoOpSigaction(int, siginfo_t *siginfo, void *context) {
+ fprintf(stderr, "NoOpSigaction (should not happen). Aborting\n");
+ abort();
+}
+
+TEST(AddressSanitizer, SignalTest) {
+ signal(SIGSEGV, NoOpSignalHandler);
+ signal(SIGILL, NoOpSignalHandler);
+ // If asan did not intercept sigaction NoOpSigaction will fire.
+ char *x = Ident((char*)malloc(5));
+ EXPECT_DEATH(x[6]++, "is located 1 bytes to the right");
+ free(Ident(x));
+}
+
+TEST(AddressSanitizer, SigactionTest) {
+ {
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = NoOpSigaction;;
+ sigact.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &sigact, 0);
+ }
+
+ {
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = NoOpSigaction;;
+ sigact.sa_flags = SA_SIGINFO;
+ sigaction(SIGILL, &sigact, 0);
+ }
+
+ // If asan did not intercept sigaction NoOpSigaction will fire.
+ char *x = Ident((char*)malloc(5));
+ EXPECT_DEATH(x[6]++, "is located 1 bytes to the right");
+ free(Ident(x));
+}
+
+void *TSDWorker(void *test_key) {
+ if (test_key) {
+ pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface);
+ }
+ return NULL;
+}
+
+void TSDDestructor(void *tsd) {
+ // Spawning a thread will check that the current thread id is not -1.
+ pthread_t th;
+ pthread_create(&th, NULL, TSDWorker, NULL);
+ pthread_join(th, NULL);
+}
+
+// This tests triggers the thread-specific data destruction fiasco which occurs
+// if we don't manage the TSD destructors ourselves. We create a new pthread
+// key with a non-NULL destructor which is likely to be put after the destructor
+// of AsanThread in the list of destructors.
+// In this case the TSD for AsanThread will be destroyed before TSDDestructor
+// is called for the child thread, and a CHECK will fail when we call
+// pthread_create() to spawn the grandchild.
+TEST(AddressSanitizer, DISABLED_TSDTest) {
+ pthread_t th;
+ pthread_key_t test_key;
+ pthread_key_create(&test_key, TSDDestructor);
+ pthread_create(&th, NULL, TSDWorker, &test_key);
+ pthread_join(th, NULL);
+ pthread_key_delete(test_key);
+}
+
+template<class T>
+void OOBTest() {
+ char expected_str[100];
+ for (int size = sizeof(T); size < 20; size += 5) {
+ for (int i = -5; i < 0; i++) {
+ const char *str =
+ "is located.*%d byte.*to the left";
+ sprintf(expected_str, str, abs(i));
+ EXPECT_DEATH(oob_test<T>(size, i), expected_str);
+ }
+
+ for (int i = 0; i < size - sizeof(T) + 1; i++)
+ oob_test<T>(size, i);
+
+ for (int i = size - sizeof(T) + 1; i <= size + 3 * sizeof(T); i++) {
+ const char *str =
+ "is located.*%d byte.*to the right";
+ int off = i >= size ? (i - size) : 0;
+ // we don't catch unaligned partially OOB accesses.
+ if (i % sizeof(T)) continue;
+ sprintf(expected_str, str, off);
+ EXPECT_DEATH(oob_test<T>(size, i), expected_str);
+ }
+ }
+
+ EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1),
+ "is located.*1 byte.*to the left");
+ EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc),
+ "is located.*0 byte.*to the right");
+}
+
+// TODO(glider): the following tests are EXTREMELY slow on Darwin:
+// AddressSanitizer.OOB_char (125503 ms)
+// AddressSanitizer.OOB_int (126890 ms)
+// AddressSanitizer.OOBRightTest (315605 ms)
+// AddressSanitizer.SimpleStackTest (366559 ms)
+
+TEST(AddressSanitizer, OOB_char) {
+ OOBTest<U1>();
+}
+
+TEST(AddressSanitizer, OOB_int) {
+ OOBTest<U4>();
+}
+
+TEST(AddressSanitizer, OOBRightTest) {
+ for (size_t access_size = 1; access_size <= 8; access_size *= 2) {
+ for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) {
+ for (size_t offset = 0; offset <= 8; offset += access_size) {
+ void *p = malloc(alloc_size);
+ // allocated: [p, p + alloc_size)
+ // accessed: [p + offset, p + offset + access_size)
+ uint8_t *addr = (uint8_t*)p + offset;
+ if (offset + access_size <= alloc_size) {
+ asan_write_sized_aligned(addr, access_size);
+ } else {
+ int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0;
+ const char *str =
+ "is located.%d *byte.*to the right";
+ char expected_str[100];
+ sprintf(expected_str, str, outside_bytes);
+ EXPECT_DEATH(asan_write_sized_aligned(addr, access_size),
+ expected_str);
+ }
+ free(p);
+ }
+ }
+ }
+}
+
+TEST(AddressSanitizer, UAF_char) {
+ const char *uaf_string = "AddressSanitizer.*heap-use-after-free";
+ EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string);
+ EXPECT_DEATH(uaf_test<U1>(10, 0), uaf_string);
+ EXPECT_DEATH(uaf_test<U1>(10, 10), uaf_string);
+ EXPECT_DEATH(uaf_test<U1>(kLargeMalloc, 0), uaf_string);
+ EXPECT_DEATH(uaf_test<U1>(kLargeMalloc, kLargeMalloc / 2), uaf_string);
+}
+
+#if ASAN_HAS_BLACKLIST
+TEST(AddressSanitizer, IgnoreTest) {
+ int *x = Ident(new int);
+ delete Ident(x);
+ *x = 0;
+}
+#endif // ASAN_HAS_BLACKLIST
+
+struct StructWithBitField {
+ int bf1:1;
+ int bf2:1;
+ int bf3:1;
+ int bf4:29;
+};
+
+TEST(AddressSanitizer, BitFieldPositiveTest) {
+ StructWithBitField *x = new StructWithBitField;
+ delete Ident(x);
+ EXPECT_DEATH(x->bf1 = 0, "use-after-free");
+ EXPECT_DEATH(x->bf2 = 0, "use-after-free");
+ EXPECT_DEATH(x->bf3 = 0, "use-after-free");
+ EXPECT_DEATH(x->bf4 = 0, "use-after-free");
+};
+
+struct StructWithBitFields_8_24 {
+ int a:8;
+ int b:24;
+};
+
+TEST(AddressSanitizer, BitFieldNegativeTest) {
+ StructWithBitFields_8_24 *x = Ident(new StructWithBitFields_8_24);
+ x->a = 0;
+ x->b = 0;
+ delete Ident(x);
+}
+
+TEST(AddressSanitizer, OutOfMemoryTest) {
+ size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000);
+ EXPECT_EQ(0, realloc(0, size));
+ EXPECT_EQ(0, realloc(0, ~Ident(0)));
+ EXPECT_EQ(0, malloc(size));
+ EXPECT_EQ(0, malloc(~Ident(0)));
+ EXPECT_EQ(0, calloc(1, size));
+ EXPECT_EQ(0, calloc(1, ~Ident(0)));
+}
+
+#if ASAN_NEEDS_SEGV
+TEST(AddressSanitizer, WildAddressTest) {
+ char *c = (char*)0x123;
+ EXPECT_DEATH(*c = 0, "AddressSanitizer crashed on unknown address");
+}
+#endif
+
+static void MallocStress(size_t n) {
+ uint32_t seed = my_rand(&global_seed);
+ for (size_t iter = 0; iter < 10; iter++) {
+ vector<void *> vec;
+ for (size_t i = 0; i < n; i++) {
+ if ((i % 3) == 0) {
+ if (vec.empty()) continue;
+ size_t idx = my_rand(&seed) % vec.size();
+ void *ptr = vec[idx];
+ vec[idx] = vec.back();
+ vec.pop_back();
+ free_aaa(ptr);
+ } else {
+ size_t size = my_rand(&seed) % 1000 + 1;
+#ifndef __APPLE__
+ size_t alignment = 1 << (my_rand(&seed) % 7 + 3);
+ char *ptr = (char*)memalign_aaa(alignment, size);
+#else
+ char *ptr = (char*) malloc_aaa(size);
+#endif
+ vec.push_back(ptr);
+ ptr[0] = 0;
+ ptr[size-1] = 0;
+ ptr[size/2] = 0;
+ }
+ }
+ for (size_t i = 0; i < vec.size(); i++)
+ free_aaa(vec[i]);
+ }
+}
+
+TEST(AddressSanitizer, MallocStressTest) {
+ MallocStress(200000);
+}
+
+static void TestLargeMalloc(size_t size) {
+ char buff[1024];
+ sprintf(buff, "is located 1 bytes to the left of %lu-byte", (long)size);
+ EXPECT_DEATH(Ident((char*)malloc(size))[-1] = 0, buff);
+}
+
+TEST(AddressSanitizer, LargeMallocTest) {
+ for (int i = 113; i < (1 << 28); i = i * 2 + 13) {
+ TestLargeMalloc(i);
+ }
+}
+
+TEST(AddressSanitizer, HugeMallocTest) {
+#ifdef __APPLE__
+ // It was empirically found out that 1215 megabytes is the maximum amount of
+ // memory available to the process under AddressSanitizer on Darwin.
+ // (the libSystem malloc() allows allocating up to 2300 megabytes without
+ // ASan).
+ size_t n_megs = __WORDSIZE == 32 ? 1200 : 4100;
+#else
+ size_t n_megs = __WORDSIZE == 32 ? 2600 : 4100;
+#endif
+ TestLargeMalloc(n_megs << 20);
+}
+
+TEST(AddressSanitizer, ThreadedMallocStressTest) {
+ const int kNumThreads = 4;
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_create(&t[i], 0, (void* (*)(void *x))MallocStress, (void*)100000);
+ }
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_join(t[i], 0);
+ }
+}
+
+void *ManyThreadsWorker(void *a) {
+ for (int iter = 0; iter < 100; iter++) {
+ for (size_t size = 100; size < 2000; size *= 2) {
+ free(Ident(malloc(size)));
+ }
+ }
+ return 0;
+}
+
+TEST(AddressSanitizer, ManyThreadsTest) {
+ const size_t kNumThreads = __WORDSIZE == 32 ? 30 : 1000;
+ pthread_t t[kNumThreads];
+ for (size_t i = 0; i < kNumThreads; i++) {
+ pthread_create(&t[i], 0, (void* (*)(void *x))ManyThreadsWorker, (void*)i);
+ }
+ for (size_t i = 0; i < kNumThreads; i++) {
+ pthread_join(t[i], 0);
+ }
+}
+
+TEST(AddressSanitizer, ReallocTest) {
+ const int kMinElem = 5;
+ int *ptr = (int*)malloc(sizeof(int) * kMinElem);
+ ptr[3] = 3;
+ for (int i = 0; i < 10000; i++) {
+ ptr = (int*)realloc(ptr,
+ (my_rand(&global_seed) % 1000 + kMinElem) * sizeof(int));
+ EXPECT_EQ(3, ptr[3]);
+ }
+}
+
+void WrongFree() {
+ int *x = (int*)malloc(100 * sizeof(int));
+ // Use the allocated memory, otherwise Clang will optimize it out.
+ Ident(x);
+ free(x + 1);
+}
+
+TEST(AddressSanitizer, WrongFreeTest) {
+ EXPECT_DEATH(WrongFree(),
+ "ERROR: AddressSanitizer attempting free.*not malloc");
+}
+
+void DoubleFree() {
+ int *x = (int*)malloc(100 * sizeof(int));
+ fprintf(stderr, "DoubleFree: x=%p\n", x);
+ free(x);
+ free(x);
+ fprintf(stderr, "should have failed in the second free(%p)\n", x);
+ abort();
+}
+
+TEST(AddressSanitizer, DoubleFreeTest) {
+ EXPECT_DEATH(DoubleFree(), "ERROR: AddressSanitizer attempting double-free");
+}
+
+template<int kSize>
+__attribute__((noinline))
+void SizedStackTest() {
+ char a[kSize];
+ char *A = Ident((char*)&a);
+ for (size_t i = 0; i < kSize; i++)
+ A[i] = i;
+ EXPECT_DEATH(A[-1] = 0, "");
+ EXPECT_DEATH(A[-20] = 0, "");
+ EXPECT_DEATH(A[-31] = 0, "");
+ EXPECT_DEATH(A[kSize] = 0, "");
+ EXPECT_DEATH(A[kSize + 1] = 0, "");
+ EXPECT_DEATH(A[kSize + 10] = 0, "");
+ EXPECT_DEATH(A[kSize + 31] = 0, "");
+}
+
+TEST(AddressSanitizer, SimpleStackTest) {
+ SizedStackTest<1>();
+ SizedStackTest<2>();
+ SizedStackTest<3>();
+ SizedStackTest<4>();
+ SizedStackTest<5>();
+ SizedStackTest<6>();
+ SizedStackTest<7>();
+ SizedStackTest<16>();
+ SizedStackTest<25>();
+ SizedStackTest<34>();
+ SizedStackTest<43>();
+ SizedStackTest<51>();
+ SizedStackTest<62>();
+ SizedStackTest<64>();
+ SizedStackTest<128>();
+}
+
+TEST(AddressSanitizer, ManyStackObjectsTest) {
+ char XXX[10];
+ char YYY[20];
+ char ZZZ[30];
+ Ident(XXX);
+ Ident(YYY);
+ EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ");
+}
+
+__attribute__((noinline))
+static void Frame0(int frame, char *a, char *b, char *c) {
+ char d[4] = {0};
+ char *D = Ident(d);
+ switch (frame) {
+ case 3: a[5]++; break;
+ case 2: b[5]++; break;
+ case 1: c[5]++; break;
+ case 0: D[5]++; break;
+ }
+}
+__attribute__((noinline)) static void Frame1(int frame, char *a, char *b) {
+ char c[4] = {0}; Frame0(frame, a, b, c);
+ break_optimization(0);
+}
+__attribute__((noinline)) static void Frame2(int frame, char *a) {
+ char b[4] = {0}; Frame1(frame, a, b);
+ break_optimization(0);
+}
+__attribute__((noinline)) static void Frame3(int frame) {
+ char a[4] = {0}; Frame2(frame, a);
+ break_optimization(0);
+}
+
+TEST(AddressSanitizer, GuiltyStackFrame0Test) {
+ EXPECT_DEATH(Frame3(0), "located .*in frame <.*Frame0");
+}
+TEST(AddressSanitizer, GuiltyStackFrame1Test) {
+ EXPECT_DEATH(Frame3(1), "located .*in frame <.*Frame1");
+}
+TEST(AddressSanitizer, GuiltyStackFrame2Test) {
+ EXPECT_DEATH(Frame3(2), "located .*in frame <.*Frame2");
+}
+TEST(AddressSanitizer, GuiltyStackFrame3Test) {
+ EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3");
+}
+
+__attribute__((noinline))
+void LongJmpFunc1(jmp_buf buf) {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ longjmp(buf, 1);
+}
+
+__attribute__((noinline))
+void UnderscopeLongJmpFunc1(jmp_buf buf) {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ _longjmp(buf, 1);
+}
+
+__attribute__((noinline))
+void SigLongJmpFunc1(sigjmp_buf buf) {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ siglongjmp(buf, 1);
+}
+
+
+__attribute__((noinline))
+void TouchStackFunc() {
+ int a[100]; // long array will intersect with redzones from LongJmpFunc1.
+ int *A = Ident(a);
+ for (int i = 0; i < 100; i++)
+ A[i] = i*i;
+}
+
+// Test that we handle longjmp and do not report fals positives on stack.
+TEST(AddressSanitizer, LongJmpTest) {
+ static jmp_buf buf;
+ if (!setjmp(buf)) {
+ LongJmpFunc1(buf);
+ } else {
+ TouchStackFunc();
+ }
+}
+
+TEST(AddressSanitizer, UnderscopeLongJmpTest) {
+ static jmp_buf buf;
+ if (!_setjmp(buf)) {
+ UnderscopeLongJmpFunc1(buf);
+ } else {
+ TouchStackFunc();
+ }
+}
+
+TEST(AddressSanitizer, SigLongJmpTest) {
+ static sigjmp_buf buf;
+ if (!sigsetjmp(buf, 1)) {
+ SigLongJmpFunc1(buf);
+ } else {
+ TouchStackFunc();
+ }
+}
+
+#ifdef __EXCEPTIONS
+__attribute__((noinline))
+void ThrowFunc() {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ ASAN_THROW(1);
+}
+
+TEST(AddressSanitizer, CxxExceptionTest) {
+ if (ASAN_UAR) return;
+ // TODO(kcc): this test crashes on 32-bit for some reason...
+ if (__WORDSIZE == 32) return;
+ try {
+ ThrowFunc();
+ } catch(...) {}
+ TouchStackFunc();
+}
+#endif
+
+void *ThreadStackReuseFunc1(void *unused) {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ pthread_exit(0);
+ return 0;
+}
+
+void *ThreadStackReuseFunc2(void *unused) {
+ TouchStackFunc();
+ return 0;
+}
+
+TEST(AddressSanitizer, ThreadStackReuseTest) {
+ pthread_t t;
+ pthread_create(&t, 0, ThreadStackReuseFunc1, 0);
+ pthread_join(t, 0);
+ pthread_create(&t, 0, ThreadStackReuseFunc2, 0);
+ pthread_join(t, 0);
+}
+
+#if defined(__i386__) or defined(__x86_64__)
+TEST(AddressSanitizer, Store128Test) {
+ char *a = Ident((char*)malloc(Ident(12)));
+ char *p = a;
+ if (((uintptr_t)a % 16) != 0)
+ p = a + 8;
+ assert(((uintptr_t)p % 16) == 0);
+ __m128i value_wide = _mm_set1_epi16(0x1234);
+ EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
+ "AddressSanitizer heap-buffer-overflow");
+ EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
+ "WRITE of size 16");
+ EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
+ "located 0 bytes to the right of 12-byte");
+ free(a);
+}
+#endif
+
+static string RightOOBErrorMessage(int oob_distance) {
+ assert(oob_distance >= 0);
+ char expected_str[100];
+ sprintf(expected_str, "located %d bytes to the right", oob_distance);
+ return string(expected_str);
+}
+
+static string LeftOOBErrorMessage(int oob_distance) {
+ assert(oob_distance > 0);
+ char expected_str[100];
+ sprintf(expected_str, "located %d bytes to the left", oob_distance);
+ return string(expected_str);
+}
+
+template<class T>
+void MemSetOOBTestTemplate(size_t length) {
+ if (length == 0) return;
+ size_t size = Ident(sizeof(T) * length);
+ T *array = Ident((T*)malloc(size));
+ int element = Ident(42);
+ int zero = Ident(0);
+ // memset interval inside array
+ memset(array, element, size);
+ memset(array, element, size - 1);
+ memset(array + length - 1, element, sizeof(T));
+ memset(array, element, 1);
+
+ // memset 0 bytes
+ memset(array - 10, element, zero);
+ memset(array - 1, element, zero);
+ memset(array, element, zero);
+ memset(array + length, 0, zero);
+ memset(array + length + 1, 0, zero);
+
+ // try to memset bytes to the right of array
+ EXPECT_DEATH(memset(array, 0, size + 1),
+ RightOOBErrorMessage(0));
+ EXPECT_DEATH(memset((char*)(array + length) - 1, element, 6),
+ RightOOBErrorMessage(4));
+ EXPECT_DEATH(memset(array + 1, element, size + sizeof(T)),
+ RightOOBErrorMessage(2 * sizeof(T) - 1));
+ // whole interval is to the right
+ EXPECT_DEATH(memset(array + length + 1, 0, 10),
+ RightOOBErrorMessage(sizeof(T)));
+
+ // try to memset bytes to the left of array
+ EXPECT_DEATH(memset((char*)array - 1, element, size),
+ LeftOOBErrorMessage(1));
+ EXPECT_DEATH(memset((char*)array - 5, 0, 6),
+ LeftOOBErrorMessage(5));
+ EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
+ LeftOOBErrorMessage(5 * sizeof(T)));
+ // whole interval is to the left
+ EXPECT_DEATH(memset(array - 2, 0, sizeof(T)),
+ LeftOOBErrorMessage(2 * sizeof(T)));
+
+ // try to memset bytes both to the left & to the right
+ EXPECT_DEATH(memset((char*)array - 2, element, size + 4),
+ LeftOOBErrorMessage(2));
+
+ free(array);
+}
+
+TEST(AddressSanitizer, MemSetOOBTest) {
+ MemSetOOBTestTemplate<char>(100);
+ MemSetOOBTestTemplate<int>(5);
+ MemSetOOBTestTemplate<double>(256);
+ // We can test arrays of structres/classes here, but what for?
+}
+
+// Same test for memcpy and memmove functions
+template <class T, class M>
+void MemTransferOOBTestTemplate(size_t length) {
+ if (length == 0) return;
+ size_t size = Ident(sizeof(T) * length);
+ T *src = Ident((T*)malloc(size));
+ T *dest = Ident((T*)malloc(size));
+ int zero = Ident(0);
+
+ // valid transfer of bytes between arrays
+ M::transfer(dest, src, size);
+ M::transfer(dest + 1, src, size - sizeof(T));
+ M::transfer(dest, src + length - 1, sizeof(T));
+ M::transfer(dest, src, 1);
+
+ // transfer zero bytes
+ M::transfer(dest - 1, src, 0);
+ M::transfer(dest + length, src, zero);
+ M::transfer(dest, src - 1, zero);
+ M::transfer(dest, src, zero);
+
+ // try to change mem to the right of dest
+ EXPECT_DEATH(M::transfer(dest + 1, src, size),
+ RightOOBErrorMessage(sizeof(T) - 1));
+ EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
+ RightOOBErrorMessage(3));
+
+ // try to change mem to the left of dest
+ EXPECT_DEATH(M::transfer(dest - 2, src, size),
+ LeftOOBErrorMessage(2 * sizeof(T)));
+ EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
+ LeftOOBErrorMessage(3));
+
+ // try to access mem to the right of src
+ EXPECT_DEATH(M::transfer(dest, src + 2, size),
+ RightOOBErrorMessage(2 * sizeof(T) - 1));
+ EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
+ RightOOBErrorMessage(2));
+
+ // try to access mem to the left of src
+ EXPECT_DEATH(M::transfer(dest, src - 1, size),
+ LeftOOBErrorMessage(sizeof(T)));
+ EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
+ LeftOOBErrorMessage(6));
+
+ // Generally we don't need to test cases where both accessing src and writing
+ // to dest address to poisoned memory.
+
+ T *big_src = Ident((T*)malloc(size * 2));
+ T *big_dest = Ident((T*)malloc(size * 2));
+ // try to change mem to both sides of dest
+ EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2),
+ LeftOOBErrorMessage(sizeof(T)));
+ // try to access mem to both sides of src
+ EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
+ LeftOOBErrorMessage(2 * sizeof(T)));
+
+ free(src);
+ free(dest);
+ free(big_src);
+ free(big_dest);
+}
+
+class MemCpyWrapper {
+ public:
+ static void* transfer(void *to, const void *from, size_t size) {
+ return memcpy(to, from, size);
+ }
+};
+TEST(AddressSanitizer, MemCpyOOBTest) {
+ MemTransferOOBTestTemplate<char, MemCpyWrapper>(100);
+ MemTransferOOBTestTemplate<int, MemCpyWrapper>(1024);
+}
+
+class MemMoveWrapper {
+ public:
+ static void* transfer(void *to, const void *from, size_t size) {
+ return memmove(to, from, size);
+ }
+};
+TEST(AddressSanitizer, MemMoveOOBTest) {
+ MemTransferOOBTestTemplate<char, MemMoveWrapper>(100);
+ MemTransferOOBTestTemplate<int, MemMoveWrapper>(1024);
+}
+
+// Tests for string functions
+
+// Used for string functions tests
+static char global_string[] = "global";
+static size_t global_string_length = 6;
+
+// Input to a test is a zero-terminated string str with given length
+// Accesses to the bytes to the left and to the right of str
+// are presumed to produce OOB errors
+void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
+ // Normal strlen calls
+ EXPECT_EQ(strlen(str), length);
+ if (length > 0) {
+ EXPECT_EQ(strlen(str + 1), length - 1);
+ EXPECT_EQ(strlen(str + length), 0);
+ }
+ // Arg of strlen is not malloced, OOB access
+ if (!is_global) {
+ // We don't insert RedZones to the left of global variables
+ EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBErrorMessage(5));
+ }
+ EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBErrorMessage(0));
+ // Overwrite terminator
+ str[length] = 'a';
+ // String is not zero-terminated, strlen will lead to OOB access
+ EXPECT_DEATH(Ident(strlen(str)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strlen(str + length)), RightOOBErrorMessage(0));
+ // Restore terminator
+ str[length] = 0;
+}
+TEST(AddressSanitizer, StrLenOOBTest) {
+ // Check heap-allocated string
+ size_t length = Ident(10);
+ char *heap_string = Ident((char*)malloc(length + 1));
+ char stack_string[10 + 1];
+ for (int i = 0; i < length; i++) {
+ heap_string[i] = 'a';
+ stack_string[i] = 'b';
+ }
+ heap_string[length] = 0;
+ stack_string[length] = 0;
+ StrLenOOBTestTemplate(heap_string, length, false);
+ // TODO(samsonov): Fix expected messages in StrLenOOBTestTemplate to
+ // make test for stack_string work. Or move it to output tests.
+ // StrLenOOBTestTemplate(stack_string, length, false);
+ StrLenOOBTestTemplate(global_string, global_string_length, true);
+ free(heap_string);
+}
+
+static inline char* MallocAndMemsetString(size_t size) {
+ char *s = Ident((char*)malloc(size));
+ memset(s, 'z', size);
+ return s;
+}
+
+#ifndef __APPLE__
+TEST(AddressSanitizer, StrNLenOOBTest) {
+ size_t size = Ident(123);
+ char *str = MallocAndMemsetString(size);
+ // Normal strnlen calls.
+ Ident(strnlen(str - 1, 0));
+ Ident(strnlen(str, size));
+ Ident(strnlen(str + size - 1, 1));
+ str[size - 1] = '\0';
+ Ident(strnlen(str, 2 * size));
+ // Argument points to not allocated memory.
+ EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBErrorMessage(0));
+ // Overwrite the terminating '\0' and hit unallocated memory.
+ str[size - 1] = 'z';
+ EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBErrorMessage(0));
+ free(str);
+}
+#endif
+
+TEST(AddressSanitizer, StrDupOOBTest) {
+ size_t size = Ident(42);
+ char *str = MallocAndMemsetString(size);
+ char *new_str;
+ // Normal strdup calls.
+ str[size - 1] = '\0';
+ new_str = strdup(str);
+ free(new_str);
+ new_str = strdup(str + size - 1);
+ free(new_str);
+ // Argument points to not allocated memory.
+ EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strdup(str + size)), RightOOBErrorMessage(0));
+ // Overwrite the terminating '\0' and hit unallocated memory.
+ str[size - 1] = 'z';
+ EXPECT_DEATH(Ident(strdup(str)), RightOOBErrorMessage(0));
+ free(str);
+}
+
+TEST(AddressSanitizer, StrCpyOOBTest) {
+ size_t to_size = Ident(30);
+ size_t from_size = Ident(6); // less than to_size
+ char *to = Ident((char*)malloc(to_size));
+ char *from = Ident((char*)malloc(from_size));
+ // Normal strcpy calls.
+ strcpy(from, "hello");
+ strcpy(to, from);
+ strcpy(to + to_size - from_size, from);
+ // Length of "from" is too small.
+ EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBErrorMessage(0));
+ // "to" or "from" points to not allocated memory.
+ EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBErrorMessage(0));
+ // Overwrite the terminating '\0' character and hit unallocated memory.
+ from[from_size - 1] = '!';
+ EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBErrorMessage(0));
+ free(to);
+ free(from);
+}
+
+TEST(AddressSanitizer, StrNCpyOOBTest) {
+ size_t to_size = Ident(20);
+ size_t from_size = Ident(6); // less than to_size
+ char *to = Ident((char*)malloc(to_size));
+ // From is a zero-terminated string "hello\0" of length 6
+ char *from = Ident((char*)malloc(from_size));
+ strcpy(from, "hello");
+ // copy 0 bytes
+ strncpy(to, from, 0);
+ strncpy(to - 1, from - 1, 0);
+ // normal strncpy calls
+ strncpy(to, from, from_size);
+ strncpy(to, from, to_size);
+ strncpy(to, from + from_size - 1, to_size);
+ strncpy(to + to_size - 1, from, 1);
+ // One of {to, from} points to not allocated memory
+ EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)),
+ LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
+ LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
+ RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
+ RightOOBErrorMessage(0));
+ // Length of "to" is too small
+ EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
+ RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
+ RightOOBErrorMessage(0));
+ // Overwrite terminator in from
+ from[from_size - 1] = '!';
+ // normal strncpy call
+ strncpy(to, from, from_size);
+ // Length of "from" is too small
+ EXPECT_DEATH(Ident(strncpy(to, from, to_size)),
+ RightOOBErrorMessage(0));
+ free(to);
+ free(from);
+}
+
+typedef char*(*PointerToStrChr)(const char*, int);
+void RunStrChrTest(PointerToStrChr StrChr) {
+ size_t size = Ident(100);
+ char *str = MallocAndMemsetString(size);
+ str[10] = 'q';
+ str[11] = '\0';
+ EXPECT_EQ(str, StrChr(str, 'z'));
+ EXPECT_EQ(str + 10, StrChr(str, 'q'));
+ EXPECT_EQ(NULL, StrChr(str, 'a'));
+ // StrChr argument points to not allocated memory.
+ EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBErrorMessage(0));
+ // Overwrite the terminator and hit not allocated memory.
+ str[11] = 'z';
+ EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBErrorMessage(0));
+ free(str);
+}
+TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
+ RunStrChrTest(&strchr);
+ RunStrChrTest(&index);
+}
+
+TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
+ // strcmp
+ EXPECT_EQ(0, strcmp("", ""));
+ EXPECT_EQ(0, strcmp("abcd", "abcd"));
+ EXPECT_GT(0, strcmp("ab", "ac"));
+ EXPECT_GT(0, strcmp("abc", "abcd"));
+ EXPECT_LT(0, strcmp("acc", "abc"));
+ EXPECT_LT(0, strcmp("abcd", "abc"));
+
+ // strncmp
+ EXPECT_EQ(0, strncmp("a", "b", 0));
+ EXPECT_EQ(0, strncmp("abcd", "abcd", 10));
+ EXPECT_EQ(0, strncmp("abcd", "abcef", 3));
+ EXPECT_GT(0, strncmp("abcde", "abcfa", 4));
+ EXPECT_GT(0, strncmp("a", "b", 5));
+ EXPECT_GT(0, strncmp("bc", "bcde", 4));
+ EXPECT_LT(0, strncmp("xyz", "xyy", 10));
+ EXPECT_LT(0, strncmp("baa", "aaa", 1));
+ EXPECT_LT(0, strncmp("zyx", "", 2));
+
+ // strcasecmp
+ EXPECT_EQ(0, strcasecmp("", ""));
+ EXPECT_EQ(0, strcasecmp("zzz", "zzz"));
+ EXPECT_EQ(0, strcasecmp("abCD", "ABcd"));
+ EXPECT_GT(0, strcasecmp("aB", "Ac"));
+ EXPECT_GT(0, strcasecmp("ABC", "ABCd"));
+ EXPECT_LT(0, strcasecmp("acc", "abc"));
+ EXPECT_LT(0, strcasecmp("ABCd", "abc"));
+
+ // strncasecmp
+ EXPECT_EQ(0, strncasecmp("a", "b", 0));
+ EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10));
+ EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3));
+ EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4));
+ EXPECT_GT(0, strncasecmp("a", "B", 5));
+ EXPECT_GT(0, strncasecmp("bc", "BCde", 4));
+ EXPECT_LT(0, strncasecmp("xyz", "xyy", 10));
+ EXPECT_LT(0, strncasecmp("Baa", "aaa", 1));
+ EXPECT_LT(0, strncasecmp("zyx", "", 2));
+
+ // memcmp
+ EXPECT_EQ(0, memcmp("a", "b", 0));
+ EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4));
+ EXPECT_GT(0, memcmp("\0ab", "\0ac", 3));
+ EXPECT_GT(0, memcmp("abb\0", "abba", 4));
+ EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5));
+ EXPECT_LT(0, memcmp("zza", "zyx", 3));
+}
+
+typedef int(*PointerToStrCmp)(const char*, const char*);
+void RunStrCmpTest(PointerToStrCmp StrCmp) {
+ size_t size = Ident(100);
+ char *s1 = MallocAndMemsetString(size);
+ char *s2 = MallocAndMemsetString(size);
+ s1[size - 1] = '\0';
+ s2[size - 1] = '\0';
+ // Normal StrCmp calls
+ Ident(StrCmp(s1, s2));
+ Ident(StrCmp(s1, s2 + size - 1));
+ Ident(StrCmp(s1 + size - 1, s2 + size - 1));
+ s1[size - 1] = 'z';
+ s2[size - 1] = 'x';
+ Ident(StrCmp(s1, s2));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBErrorMessage(0));
+ // Hit unallocated memory and die.
+ s2[size - 1] = 'z';
+ EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBErrorMessage(0));
+ free(s1);
+ free(s2);
+}
+
+TEST(AddressSanitizer, StrCmpOOBTest) {
+ RunStrCmpTest(&strcmp);
+}
+
+TEST(AddressSanitizer, StrCaseCmpOOBTest) {
+ RunStrCmpTest(&strcasecmp);
+}
+
+typedef int(*PointerToStrNCmp)(const char*, const char*, size_t);
+void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
+ size_t size = Ident(100);
+ char *s1 = MallocAndMemsetString(size);
+ char *s2 = MallocAndMemsetString(size);
+ s1[size - 1] = '\0';
+ s2[size - 1] = '\0';
+ // Normal StrNCmp calls
+ Ident(StrNCmp(s1, s2, size + 2));
+ s1[size - 1] = 'z';
+ s2[size - 1] = 'x';
+ Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size));
+ s2[size - 1] = 'z';
+ Ident(StrNCmp(s1 - 1, s2 - 1, 0));
+ Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+ // Hit unallocated memory and die.
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+ free(s1);
+ free(s2);
+}
+
+TEST(AddressSanitizer, StrNCmpOOBTest) {
+ RunStrNCmpTest(&strncmp);
+}
+
+TEST(AddressSanitizer, StrNCaseCmpOOBTest) {
+ RunStrNCmpTest(&strncasecmp);
+}
+
+TEST(AddressSanitizer, MemCmpOOBTest) {
+ size_t size = Ident(100);
+ char *s1 = MallocAndMemsetString(size);
+ char *s2 = MallocAndMemsetString(size);
+ // Normal memcmp calls.
+ Ident(memcmp(s1, s2, size));
+ Ident(memcmp(s1 + size - 1, s2 + size - 1, 1));
+ Ident(memcmp(s1 - 1, s2 - 1, 0));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+ // Hit unallocated memory and die.
+ EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+ // Zero bytes are not terminators and don't prevent from OOB.
+ s1[size - 1] = '\0';
+ s2[size - 1] = '\0';
+ EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBErrorMessage(0));
+ free(s1);
+ free(s2);
+}
+
+TEST(AddressSanitizer, StrCatOOBTest) {
+ size_t to_size = Ident(100);
+ char *to = MallocAndMemsetString(to_size);
+ to[0] = '\0';
+ size_t from_size = Ident(20);
+ char *from = MallocAndMemsetString(from_size);
+ from[from_size - 1] = '\0';
+ // Normal strcat calls.
+ strcat(to, from);
+ strcat(to, from);
+ strcat(to + from_size, from + from_size - 2);
+ // Catenate empty string is not always an error.
+ strcat(to - 1, from + from_size - 1);
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(strcat(to - 1, from), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(strcat(to + to_size, from), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strcat(to, from + from_size), RightOOBErrorMessage(0));
+
+ // "from" is not zero-terminated.
+ from[from_size - 1] = 'z';
+ EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+ from[from_size - 1] = '\0';
+ // "to" is not zero-terminated.
+ memset(to, 'z', to_size);
+ EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+ // "to" is too short to fit "from".
+ to[to_size - from_size + 1] = '\0';
+ EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+ // length of "to" is just enough.
+ strcat(to, from + 1);
+}
+
+static string OverlapErrorMessage(const string &func) {
+ return func + "-param-overlap";
+}
+
+TEST(AddressSanitizer, StrArgsOverlapTest) {
+ size_t size = Ident(100);
+ char *str = Ident((char*)malloc(size));
+
+ // Check "memcpy". Use Ident() to avoid inlining.
+ memset(str, 'z', size);
+ Ident(memcpy)(str + 1, str + 11, 10);
+ Ident(memcpy)(str, str, 0);
+ EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy"));
+ EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy"));
+ EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1),
+ OverlapErrorMessage("memcpy"));
+
+ // Check "strcpy".
+ memset(str, 'z', size);
+ str[9] = '\0';
+ strcpy(str + 10, str);
+ EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy"));
+ EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy"));
+ strcpy(str, str + 5);
+
+ // Check "strncpy".
+ memset(str, 'z', size);
+ strncpy(str, str + 10, 10);
+ EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy"));
+ EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy"));
+ str[10] = '\0';
+ strncpy(str + 11, str, 20);
+ EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy"));
+
+ // Check "strcat".
+ memset(str, 'z', size);
+ str[10] = '\0';
+ str[20] = '\0';
+ strcat(str, str + 10);
+ strcat(str, str + 11);
+ str[10] = '\0';
+ strcat(str + 11, str);
+ EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
+ EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat"));
+ EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat"));
+
+ free(str);
+}
+
+// At the moment we instrument memcpy/memove/memset calls at compile time so we
+// can't handle OOB error if these functions are called by pointer, see disabled
+// MemIntrinsicCallByPointerTest below
+typedef void*(*PointerToMemTransfer)(void*, const void*, size_t);
+typedef void*(*PointerToMemSet)(void*, int, size_t);
+
+void CallMemSetByPointer(PointerToMemSet MemSet) {
+ size_t size = Ident(100);
+ char *array = Ident((char*)malloc(size));
+ EXPECT_DEATH(MemSet(array, 0, 101), RightOOBErrorMessage(0));
+ free(array);
+}
+
+void CallMemTransferByPointer(PointerToMemTransfer MemTransfer) {
+ size_t size = Ident(100);
+ char *src = Ident((char*)malloc(size));
+ char *dst = Ident((char*)malloc(size));
+ EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBErrorMessage(0));
+ free(src);
+ free(dst);
+}
+
+TEST(AddressSanitizer, DISABLED_MemIntrinsicCallByPointerTest) {
+ CallMemSetByPointer(&memset);
+ CallMemTransferByPointer(&memcpy);
+ CallMemTransferByPointer(&memmove);
+}
+
+// This test case fails
+// Clang optimizes memcpy/memset calls which lead to unaligned access
+TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) {
+ int size = Ident(4096);
+ char *s = Ident((char*)malloc(size));
+ EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBErrorMessage(0));
+ free(s);
+}
+
+// TODO(samsonov): Add a test with malloc(0)
+// TODO(samsonov): Add tests for str* and mem* functions.
+
+__attribute__((noinline))
+static int LargeFunction(bool do_bad_access) {
+ int *x = new int[100];
+ x[0]++;
+ x[1]++;
+ x[2]++;
+ x[3]++;
+ x[4]++;
+ x[5]++;
+ x[6]++;
+ x[7]++;
+ x[8]++;
+ x[9]++;
+
+ x[do_bad_access ? 100 : 0]++; int res = __LINE__;
+
+ x[10]++;
+ x[11]++;
+ x[12]++;
+ x[13]++;
+ x[14]++;
+ x[15]++;
+ x[16]++;
+ x[17]++;
+ x[18]++;
+ x[19]++;
+
+ delete x;
+ return res;
+}
+
+// Test the we have correct debug info for the failing instruction.
+// This test requires the in-process symbolizer to be enabled by default.
+TEST(AddressSanitizer, DISABLED_LargeFunctionSymbolizeTest) {
+ int failing_line = LargeFunction(false);
+ char expected_warning[128];
+ sprintf(expected_warning, "LargeFunction.*asan_test.cc:%d", failing_line);
+ EXPECT_DEATH(LargeFunction(true), expected_warning);
+}
+
+// Check that we unwind and symbolize correctly.
+TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) {
+ int *a = (int*)malloc_aaa(sizeof(int));
+ *a = 1;
+ free_aaa(a);
+ EXPECT_DEATH(*a = 1, "free_ccc.*free_bbb.*free_aaa.*"
+ "malloc_fff.*malloc_eee.*malloc_ddd");
+}
+
+void *ThreadedTestAlloc(void *a) {
+ int **p = (int**)a;
+ *p = new int;
+ return 0;
+}
+
+void *ThreadedTestFree(void *a) {
+ int **p = (int**)a;
+ delete *p;
+ return 0;
+}
+
+void *ThreadedTestUse(void *a) {
+ int **p = (int**)a;
+ **p = 1;
+ return 0;
+}
+
+void ThreadedTestSpawn() {
+ pthread_t t;
+ int *x;
+ pthread_create(&t, 0, ThreadedTestAlloc, &x);
+ pthread_join(t, 0);
+ pthread_create(&t, 0, ThreadedTestFree, &x);
+ pthread_join(t, 0);
+ pthread_create(&t, 0, ThreadedTestUse, &x);
+ pthread_join(t, 0);
+}
+
+TEST(AddressSanitizer, ThreadedTest) {
+ EXPECT_DEATH(ThreadedTestSpawn(),
+ ASAN_PCRE_DOTALL
+ "Thread T.*created"
+ ".*Thread T.*created"
+ ".*Thread T.*created");
+}
+
+#if ASAN_NEEDS_SEGV
+TEST(AddressSanitizer, ShadowGapTest) {
+#if __WORDSIZE == 32
+ char *addr = (char*)0x22000000;
+#else
+ char *addr = (char*)0x0000100000080000;
+#endif
+ EXPECT_DEATH(*addr = 1, "AddressSanitizer crashed on unknown");
+}
+#endif // ASAN_NEEDS_SEGV
+
+extern "C" {
+__attribute__((noinline))
+static void UseThenFreeThenUse() {
+ char *x = Ident((char*)malloc(8));
+ *x = 1;
+ free_aaa(x);
+ *x = 2;
+}
+}
+
+TEST(AddressSanitizer, UseThenFreeThenUseTest) {
+ EXPECT_DEATH(UseThenFreeThenUse(), "freed by thread");
+}
+
+TEST(AddressSanitizer, StrDupTest) {
+ free(strdup(Ident("123")));
+}
+
+TEST(AddressSanitizer, ObjdumpTest) {
+ ObjdumpOfMyself *o = objdump_of_myself();
+ EXPECT_TRUE(o->IsCorrect());
+}
+
+extern "C" {
+__attribute__((noinline))
+static void DisasmSimple() {
+ Ident(0);
+}
+
+__attribute__((noinline))
+static void DisasmParamWrite(int *a) {
+ *a = 1;
+}
+
+__attribute__((noinline))
+static void DisasmParamInc(int *a) {
+ (*a)++;
+}
+
+__attribute__((noinline))
+static void DisasmParamReadIfWrite(int *a) {
+ if (*a)
+ *a = 1;
+}
+
+__attribute__((noinline))
+static int DisasmParamIfReadWrite(int *a, int cond) {
+ int res = 0;
+ if (cond)
+ res = *a;
+ *a = 0;
+ return res;
+}
+
+static int GLOBAL;
+
+__attribute__((noinline))
+static void DisasmWriteGlob() {
+ GLOBAL = 1;
+}
+} // extern "C"
+
+TEST(AddressSanitizer, DisasmTest) {
+ int a;
+ DisasmSimple();
+ DisasmParamWrite(&a);
+ DisasmParamInc(&a);
+ Ident(DisasmWriteGlob)();
+ DisasmParamReadIfWrite(&a);
+
+ a = 7;
+ EXPECT_EQ(7, DisasmParamIfReadWrite(&a, Ident(1)));
+ EXPECT_EQ(0, a);
+
+ ObjdumpOfMyself *o = objdump_of_myself();
+ vector<string> insns;
+ insns.push_back("ud2");
+ insns.push_back("__asan_report_");
+ EXPECT_EQ(0, o->CountInsnInFunc("DisasmSimple", insns));
+ EXPECT_EQ(1, o->CountInsnInFunc("DisasmParamWrite", insns));
+ EXPECT_EQ(1, o->CountInsnInFunc("DisasmParamInc", insns));
+ EXPECT_EQ(0, o->CountInsnInFunc("DisasmWriteGlob", insns));
+
+ // TODO(kcc): implement these (needs just one __asan_report).
+ EXPECT_EQ(2, o->CountInsnInFunc("DisasmParamReadIfWrite", insns));
+ EXPECT_EQ(2, o->CountInsnInFunc("DisasmParamIfReadWrite", insns));
+}
+
+// Currently we create and poison redzone at right of global variables.
+char glob5[5];
+static char static110[110];
+const char ConstGlob[7] = {1, 2, 3, 4, 5, 6, 7};
+static const char StaticConstGlob[3] = {9, 8, 7};
+extern int GlobalsTest(int x);
+
+TEST(AddressSanitizer, GlobalTest) {
+ static char func_static15[15];
+
+ static char fs1[10];
+ static char fs2[10];
+ static char fs3[10];
+
+ glob5[Ident(0)] = 0;
+ glob5[Ident(1)] = 0;
+ glob5[Ident(2)] = 0;
+ glob5[Ident(3)] = 0;
+ glob5[Ident(4)] = 0;
+
+ EXPECT_DEATH(glob5[Ident(5)] = 0,
+ "0 bytes to the right of global variable.*glob5.* size 5");
+ EXPECT_DEATH(glob5[Ident(5+6)] = 0,
+ "6 bytes to the right of global variable.*glob5.* size 5");
+ Ident(static110); // avoid optimizations
+ static110[Ident(0)] = 0;
+ static110[Ident(109)] = 0;
+ EXPECT_DEATH(static110[Ident(110)] = 0,
+ "0 bytes to the right of global variable");
+ EXPECT_DEATH(static110[Ident(110+7)] = 0,
+ "7 bytes to the right of global variable");
+
+ Ident(func_static15); // avoid optimizations
+ func_static15[Ident(0)] = 0;
+ EXPECT_DEATH(func_static15[Ident(15)] = 0,
+ "0 bytes to the right of global variable");
+ EXPECT_DEATH(func_static15[Ident(15 + 9)] = 0,
+ "9 bytes to the right of global variable");
+
+ Ident(fs1);
+ Ident(fs2);
+ Ident(fs3);
+
+ // We don't create left redzones, so this is not 100% guaranteed to fail.
+ // But most likely will.
+ EXPECT_DEATH(fs2[Ident(-1)] = 0, "is located.*of global variable");
+
+ EXPECT_DEATH(Ident(Ident(ConstGlob)[8]),
+ "is located 1 bytes to the right of .*ConstGlob");
+ EXPECT_DEATH(Ident(Ident(StaticConstGlob)[5]),
+ "is located 2 bytes to the right of .*StaticConstGlob");
+
+ // call stuff from another file.
+ GlobalsTest(0);
+}
+
+TEST(AddressSanitizer, GlobalStringConstTest) {
+ static const char *zoo = "FOOBAR123";
+ const char *p = Ident(zoo);
+ EXPECT_DEATH(Ident(p[15]), "is ascii string 'FOOBAR123'");
+}
+
+TEST(AddressSanitizer, FileNameInGlobalReportTest) {
+ static char zoo[10];
+ const char *p = Ident(zoo);
+ // The file name should be present in the report.
+ EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test.cc");
+}
+
+int *ReturnsPointerToALocalObject() {
+ int a = 0;
+ return Ident(&a);
+}
+
+#if ASAN_UAR == 1
+TEST(AddressSanitizer, LocalReferenceReturnTest) {
+ int *(*f)() = Ident(ReturnsPointerToALocalObject);
+ int *p = f();
+ // Call 'f' a few more times, 'p' should still be poisoned.
+ for (int i = 0; i < 32; i++)
+ f();
+ EXPECT_DEATH(*p = 1, "AddressSanitizer stack-use-after-return");
+ EXPECT_DEATH(*p = 1, "is located.*in frame .*ReturnsPointerToALocal");
+}
+#endif
+
+template <int kSize>
+__attribute__((noinline))
+static void FuncWithStack() {
+ char x[kSize];
+ Ident(x)[0] = 0;
+ Ident(x)[kSize-1] = 0;
+}
+
+static void LotsOfStackReuse() {
+ int LargeStack[10000];
+ Ident(LargeStack)[0] = 0;
+ for (int i = 0; i < 10000; i++) {
+ FuncWithStack<128 * 1>();
+ FuncWithStack<128 * 2>();
+ FuncWithStack<128 * 4>();
+ FuncWithStack<128 * 8>();
+ FuncWithStack<128 * 16>();
+ FuncWithStack<128 * 32>();
+ FuncWithStack<128 * 64>();
+ FuncWithStack<128 * 128>();
+ FuncWithStack<128 * 256>();
+ FuncWithStack<128 * 512>();
+ Ident(LargeStack)[0] = 0;
+ }
+}
+
+TEST(AddressSanitizer, StressStackReuseTest) {
+ LotsOfStackReuse();
+}
+
+TEST(AddressSanitizer, ThreadedStressStackReuseTest) {
+ const int kNumThreads = 20;
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_create(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0);
+ }
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_join(t[i], 0);
+ }
+}
+
+#ifdef __EXCEPTIONS
+__attribute__((noinline))
+static void StackReuseAndException() {
+ int large_stack[1000];
+ Ident(large_stack);
+ ASAN_THROW(1);
+}
+
+// TODO(kcc): support exceptions with use-after-return.
+TEST(AddressSanitizer, DISABLED_StressStackReuseAndExceptionsTest) {
+ for (int i = 0; i < 10000; i++) {
+ try {
+ StackReuseAndException();
+ } catch(...) {
+ }
+ }
+}
+#endif
+
+TEST(AddressSanitizer, MlockTest) {
+ EXPECT_EQ(0, mlockall(MCL_CURRENT));
+ EXPECT_EQ(0, mlock((void*)0x12345, 0x5678));
+ EXPECT_EQ(0, munlockall());
+ EXPECT_EQ(0, munlock((void*)0x987, 0x654));
+}
+
+// ------------------ demo tests; run each one-by-one -------------
+// e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests
+TEST(AddressSanitizer, DISABLED_DemoThreadedTest) {
+ ThreadedTestSpawn();
+}
+
+void *SimpleBugOnSTack(void *x = 0) {
+ char a[20];
+ Ident(a)[20] = 0;
+ return 0;
+}
+
+TEST(AddressSanitizer, DISABLED_DemoStackTest) {
+ SimpleBugOnSTack();
+}
+
+TEST(AddressSanitizer, DISABLED_DemoThreadStackTest) {
+ pthread_t t;
+ pthread_create(&t, 0, SimpleBugOnSTack, 0);
+ pthread_join(t, 0);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoUAFLowIn) {
+ uaf_test<U1>(10, 0);
+}
+TEST(AddressSanitizer, DISABLED_DemoUAFLowLeft) {
+ uaf_test<U1>(10, -2);
+}
+TEST(AddressSanitizer, DISABLED_DemoUAFLowRight) {
+ uaf_test<U1>(10, 10);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoUAFHigh) {
+ uaf_test<U1>(kLargeMalloc, 0);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) {
+ oob_test<U1>(10, -1);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) {
+ oob_test<U1>(kLargeMalloc, -1);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) {
+ oob_test<U1>(10, 10);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) {
+ oob_test<U1>(kLargeMalloc, kLargeMalloc);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOM) {
+ size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000);
+ printf("%p\n", malloc(size));
+}
+
+TEST(AddressSanitizer, DISABLED_DemoDoubleFreeTest) {
+ DoubleFree();
+}
+
+TEST(AddressSanitizer, DISABLED_DemoNullDerefTest) {
+ int *a = 0;
+ Ident(a)[10] = 0;
+}
+
+TEST(AddressSanitizer, DISABLED_DemoFunctionStaticTest) {
+ static char a[100];
+ static char b[100];
+ static char c[100];
+ Ident(a);
+ Ident(b);
+ Ident(c);
+ Ident(a)[5] = 0;
+ Ident(b)[105] = 0;
+ Ident(a)[5] = 0;
+}
+
+TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
+ const size_t kAllocSize = (1 << 28) - 1024;
+ size_t total_size = 0;
+ while (true) {
+ char *x = (char*)malloc(kAllocSize);
+ memset(x, 0, kAllocSize);
+ total_size += kAllocSize;
+ fprintf(stderr, "total: %ldM\n", (long)total_size >> 20);
+ }
+}
+
+#ifdef __APPLE__
+#include "asan_mac_test.h"
+// TODO(glider): figure out whether we still need these tests. Is it correct
+// to intercept CFAllocator?
+TEST(AddressSanitizerMac, DISABLED_CFAllocatorDefaultDoubleFree) {
+ EXPECT_DEATH(
+ CFAllocatorDefaultDoubleFree(),
+ "attempting double-free");
+}
+
+TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {
+ EXPECT_DEATH(
+ CFAllocatorSystemDefaultDoubleFree(),
+ "attempting double-free");
+}
+
+TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocDoubleFree) {
+ EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
+}
+
+TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
+ EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
+}
+
+TEST(AddressSanitizerMac, GCDDispatchAsync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte and word");
+}
+
+TEST(AddressSanitizerMac, GCDDispatchSync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte and word");
+}
+
+
+TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte and word");
+}
+
+TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte and word");
+}
+
+TEST(AddressSanitizerMac, GCDDispatchAfter) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte and word");
+}
+
+TEST(AddressSanitizerMac, GCDSourceEvent) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte and word");
+}
+
+TEST(AddressSanitizerMac, GCDSourceCancel) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte and word");
+}
+
+TEST(AddressSanitizerMac, GCDGroupAsync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte and word");
+}
+
+void *MallocIntrospectionLockWorker(void *_) {
+ const int kNumPointers = 100;
+ int i;
+ void *pointers[kNumPointers];
+ for (i = 0; i < kNumPointers; i++) {
+ pointers[i] = malloc(i + 1);
+ }
+ for (i = 0; i < kNumPointers; i++) {
+ free(pointers[i]);
+ }
+
+ return NULL;
+}
+
+void *MallocIntrospectionLockForker(void *_) {
+ pid_t result = fork();
+ if (result == -1) {
+ perror("fork");
+ }
+ assert(result != -1);
+ if (result == 0) {
+ // Call malloc in the child process to make sure we won't deadlock.
+ void *ptr = malloc(42);
+ free(ptr);
+ exit(0);
+ } else {
+ // Return in the parent process.
+ return NULL;
+ }
+}
+
+TEST(AddressSanitizerMac, MallocIntrospectionLock) {
+ // Incorrect implementation of force_lock and force_unlock in our malloc zone
+ // will cause forked processes to deadlock.
+ // TODO(glider): need to detect that none of the child processes deadlocked.
+ const int kNumWorkers = 5, kNumIterations = 100;
+ int i, iter;
+ for (iter = 0; iter < kNumIterations; iter++) {
+ pthread_t workers[kNumWorkers], forker;
+ for (i = 0; i < kNumWorkers; i++) {
+ pthread_create(&workers[i], 0, MallocIntrospectionLockWorker, 0);
+ }
+ pthread_create(&forker, 0, MallocIntrospectionLockForker, 0);
+ for (i = 0; i < kNumWorkers; i++) {
+ pthread_join(workers[i], 0);
+ }
+ pthread_join(forker, 0);
+ }
+}
+
+void *TSDAllocWorker(void *test_key) {
+ if (test_key) {
+ void *mem = malloc(10);
+ pthread_setspecific(*(pthread_key_t*)test_key, mem);
+ }
+ return NULL;
+}
+
+TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
+ pthread_t th;
+ pthread_key_t test_key;
+ pthread_key_create(&test_key, CallFreeOnWorkqueue);
+ pthread_create(&th, NULL, TSDAllocWorker, &test_key);
+ pthread_join(th, NULL);
+ pthread_key_delete(test_key);
+}
+#endif // __APPLE__
+
+int main(int argc, char **argv) {
+ progname = argv[0];
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/lib/asan/tests/asan_test.ignore b/lib/asan/tests/asan_test.ignore
new file mode 100644
index 0000000..7bafa83
--- /dev/null
+++ b/lib/asan/tests/asan_test.ignore
@@ -0,0 +1,2 @@
+fun:*IgnoreTest*
+fun:*SomeOtherFunc*
diff --git a/lib/asan/tests/asan_test_config.h b/lib/asan/tests/asan_test_config.h
new file mode 100644
index 0000000..de4ae95
--- /dev/null
+++ b/lib/asan/tests/asan_test_config.h
@@ -0,0 +1,44 @@
+//===-- asan_test_config.h ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_TEST_CONFIG_H
+#define ASAN_TEST_CONFIG_H
+
+#include <vector>
+#include <string>
+#include <map>
+
+#include "gtest/gtest.h"
+
+using std::string;
+using std::vector;
+using std::map;
+
+#ifndef ASAN_UAR
+# error "please define ASAN_UAR"
+#endif
+
+#ifndef ASAN_HAS_EXCEPTIONS
+# error "please define ASAN_HAS_EXCEPTIONS"
+#endif
+
+#ifndef ASAN_HAS_BLACKLIST
+# error "please define ASAN_HAS_BLACKLIST"
+#endif
+
+#ifndef ASAN_NEEDS_SEGV
+# error "please define ASAN_NEEDS_SEGV"
+#endif
+
+#define ASAN_PCRE_DOTALL ""
+
+#endif // ASAN_TEST_CONFIG_H
diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h
new file mode 100644
index 0000000..a480981
--- /dev/null
+++ b/lib/asan/tests/asan_test_utils.h
@@ -0,0 +1,30 @@
+//===-- asan_test_utils.h ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_TEST_UTILS_H
+#define ASAN_TEST_UTILS_H
+
+// Make the compiler think that something is going on there.
+extern "C" void break_optimization(void *);
+
+// This function returns its parameter but in such a way that compiler
+// can not prove it.
+template<class T>
+__attribute__((noinline))
+static T Ident(T t) {
+ T ret = t;
+ break_optimization(&ret);
+ return ret;
+}
+
+#endif // ASAN_TEST_UTILS_H
diff --git a/lib/asan/tests/dlclose-test-so.cc b/lib/asan/tests/dlclose-test-so.cc
new file mode 100644
index 0000000..fae2f81
--- /dev/null
+++ b/lib/asan/tests/dlclose-test-so.cc
@@ -0,0 +1,33 @@
+//===-- asan_rtl.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Regression test for
+// http://code.google.com/p/address-sanitizer/issues/detail?id=19
+//===----------------------------------------------------------------------===//
+#include <stdio.h>
+
+static int pad1;
+static int static_var;
+static int pad2;
+
+extern "C"
+int *get_address_of_static_var() {
+ return &static_var;
+}
+
+__attribute__((constructor))
+void at_dlopen() {
+ printf("%s: I am being dlopened\n", __FILE__);
+}
+__attribute__((destructor))
+void at_dlclose() {
+ printf("%s: I am being dlclosed\n", __FILE__);
+}
diff --git a/lib/asan/tests/dlclose-test.cc b/lib/asan/tests/dlclose-test.cc
new file mode 100644
index 0000000..3078866
--- /dev/null
+++ b/lib/asan/tests/dlclose-test.cc
@@ -0,0 +1,73 @@
+//===-- asan_rtl.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Regression test for
+// http://code.google.com/p/address-sanitizer/issues/detail?id=19
+// Bug description:
+// 1. application dlopens foo.so
+// 2. asan registers all globals from foo.so
+// 3. application dlcloses foo.so
+// 4. application mmaps some memory to the location where foo.so was before
+// 5. application starts using this mmaped memory, but asan still thinks there
+// are globals.
+// 6. BOOM
+//===----------------------------------------------------------------------===//
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <string>
+
+using std::string;
+
+static const int kPageSize = 4096;
+
+typedef int *(fun_t)();
+
+int main(int argc, char *argv[]) {
+ string path = string(argv[0]) + "-so.so";
+ printf("opening %s ... \n", path.c_str());
+ void *lib = dlopen(path.c_str(), RTLD_NOW);
+ if (!lib) {
+ printf("error in dlopen(): %s\n", dlerror());
+ return 1;
+ }
+ fun_t *get = (fun_t*)dlsym(lib, "get_address_of_static_var");
+ if (!get) {
+ printf("failed dlsym\n");
+ return 1;
+ }
+ int *addr = get();
+ assert(((size_t)addr % 32) == 0); // should be 32-byte aligned.
+ printf("addr: %p\n", addr);
+ addr[0] = 1; // make sure we can write there.
+
+ // Now dlclose the shared library.
+ printf("attempting to dlclose\n");
+ if (dlclose(lib)) {
+ printf("failed to dlclose\n");
+ return 1;
+ }
+ // Now, the page where 'addr' is unmapped. Map it.
+ size_t page_beg = ((size_t)addr) & ~(kPageSize - 1);
+ void *res = mmap((void*)(page_beg), kPageSize,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0);
+ if (res == (char*)-1L) {
+ printf("failed to mmap\n");
+ return 1;
+ }
+ addr[1] = 2; // BOOM (if the bug is not fixed).
+ printf("PASS\n");
+ return 0;
+}
diff --git a/lib/asan/tests/dlclose-test.tmpl b/lib/asan/tests/dlclose-test.tmpl
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/lib/asan/tests/dlclose-test.tmpl
@@ -0,0 +1 @@
+PASS
diff --git a/lib/asan/tests/global-overflow.cc b/lib/asan/tests/global-overflow.cc
new file mode 100644
index 0000000..b85c4d2
--- /dev/null
+++ b/lib/asan/tests/global-overflow.cc
@@ -0,0 +1,12 @@
+#include <string.h>
+int main(int argc, char **argv) {
+ static char XXX[10];
+ static char YYY[10];
+ static char ZZZ[10];
+ memset(XXX, 0, 10);
+ memset(YYY, 0, 10);
+ memset(ZZZ, 0, 10);
+ int res = YYY[argc * 10]; // BOOOM
+ res += XXX[argc] + ZZZ[argc];
+ return res;
+}
diff --git a/lib/asan/tests/global-overflow.tmpl b/lib/asan/tests/global-overflow.tmpl
new file mode 100644
index 0000000..c5d5442
--- /dev/null
+++ b/lib/asan/tests/global-overflow.tmpl
@@ -0,0 +1,3 @@
+READ of size 1 at 0x.* thread T0
+ #0 0x.* in main .*global-overflow.cc:9
+0x.* is located 0 bytes to the right of global variable .*YYY.* of size 10
diff --git a/lib/asan/tests/heap-overflow.cc b/lib/asan/tests/heap-overflow.cc
new file mode 100644
index 0000000..475d163
--- /dev/null
+++ b/lib/asan/tests/heap-overflow.cc
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ char *x = (char*)malloc(10 * sizeof(char));
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ free(x);
+ return res;
+}
diff --git a/lib/asan/tests/heap-overflow.tmpl b/lib/asan/tests/heap-overflow.tmpl
new file mode 100644
index 0000000..e2ab65f
--- /dev/null
+++ b/lib/asan/tests/heap-overflow.tmpl
@@ -0,0 +1,6 @@
+READ of size 1 at 0x.* thread T0
+ #0 0x.* in main .*heap-overflow.cc:6
+0x.* is located 0 bytes to the right of 10-byte region
+allocated by thread T0 here:
+ #0 0x.* in malloc
+ #1 0x.* in main .*heap-overflow.cc:[45]
diff --git a/lib/asan/tests/heap-overflow.tmpl.Darwin b/lib/asan/tests/heap-overflow.tmpl.Darwin
new file mode 100644
index 0000000..e4611d0
--- /dev/null
+++ b/lib/asan/tests/heap-overflow.tmpl.Darwin
@@ -0,0 +1,8 @@
+READ of size 1 at 0x.* thread T0
+ #0 0x.* in main .*heap-overflow.cc:6
+0x.* is located 0 bytes to the right of 10-byte region
+allocated by thread T0 here:
+ #0 0x.* in .*mz_malloc.* _asan_rtl_
+ #1 0x.* in malloc_zone_malloc.*
+ #2 0x.* in malloc.*
+ #3 0x.* in main heap-overflow.cc:4
diff --git a/lib/asan/tests/large_func_test.cc b/lib/asan/tests/large_func_test.cc
new file mode 100644
index 0000000..70bc36f
--- /dev/null
+++ b/lib/asan/tests/large_func_test.cc
@@ -0,0 +1,33 @@
+#include <stdlib.h>
+__attribute__((noinline))
+static void LargeFunction(int *x, int zero) {
+ x[0]++;
+ x[1]++;
+ x[2]++;
+ x[3]++;
+ x[4]++;
+ x[5]++;
+ x[6]++;
+ x[7]++;
+ x[8]++;
+ x[9]++;
+
+ x[zero + 111]++; // we should report this exact line
+
+ x[10]++;
+ x[11]++;
+ x[12]++;
+ x[13]++;
+ x[14]++;
+ x[15]++;
+ x[16]++;
+ x[17]++;
+ x[18]++;
+ x[19]++;
+}
+
+int main(int argc, char **argv) {
+ int *x = new int[100];
+ LargeFunction(x, argc - 1);
+ delete x;
+}
diff --git a/lib/asan/tests/large_func_test.tmpl b/lib/asan/tests/large_func_test.tmpl
new file mode 100644
index 0000000..45a13d0
--- /dev/null
+++ b/lib/asan/tests/large_func_test.tmpl
@@ -0,0 +1,8 @@
+.*ERROR: AddressSanitizer heap-buffer-overflow on address 0x.* at pc 0x.* bp 0x.* sp 0x.*
+READ of size 4 at 0x.* thread T0
+ #0 0x.* in LargeFunction .*large_func_test.cc:15
+ #1 0x.* in main .*large_func_test.cc:3[012]
+0x.* is located 44 bytes to the right of 400-byte region
+allocated by thread T0 here:
+ #0 0x.* in operator new.*
+ #1 0x.* in main .*large_func_test.cc:30
diff --git a/lib/asan/tests/match_output.py b/lib/asan/tests/match_output.py
new file mode 100755
index 0000000..31095f3
--- /dev/null
+++ b/lib/asan/tests/match_output.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+
+import re
+import sys
+
+def matchFile(f, f_re):
+ for line_re in f_re:
+ line_re = line_re.rstrip()
+ if not line_re:
+ continue
+ if line_re[0] == '#':
+ continue
+ match = False
+ for line in f:
+ line = line.rstrip()
+ # print line
+ if re.search(line_re, line):
+ match = True
+ #print 'match: %s =~ %s' % (line, line_re)
+ break
+ if not match:
+ print 'no match for: %s' % (line_re)
+ return False
+ return True
+
+if len(sys.argv) != 2:
+ print >>sys.stderr, 'Usage: %s <template file>'
+ sys.exit(1)
+
+f = sys.stdin
+f_re = open(sys.argv[1])
+
+if not matchFile(f, f_re):
+ print >>sys.stderr, 'File does not match the template'
+ sys.exit(1)
diff --git a/lib/asan/tests/null_deref.cc b/lib/asan/tests/null_deref.cc
new file mode 100644
index 0000000..f7ba4dd
--- /dev/null
+++ b/lib/asan/tests/null_deref.cc
@@ -0,0 +1,7 @@
+__attribute__((noinline))
+static void NullDeref(int *ptr) {
+ ptr[10]++;
+}
+int main() {
+ NullDeref((int*)0);
+}
diff --git a/lib/asan/tests/null_deref.tmpl b/lib/asan/tests/null_deref.tmpl
new file mode 100644
index 0000000..d27cccc
--- /dev/null
+++ b/lib/asan/tests/null_deref.tmpl
@@ -0,0 +1,4 @@
+.*ERROR: AddressSanitizer crashed on unknown address 0x0*00028 .*pc 0x.*
+AddressSanitizer can not provide additional info. ABORTING
+ #0 0x.* in NullDeref.*null_deref.cc:3
+ #1 0x.* in main.*null_deref.cc:[67]
diff --git a/lib/asan/tests/shared-lib-test-so.cc b/lib/asan/tests/shared-lib-test-so.cc
new file mode 100644
index 0000000..c3b3bc2
--- /dev/null
+++ b/lib/asan/tests/shared-lib-test-so.cc
@@ -0,0 +1,21 @@
+//===-- asan_rtl.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+#include <stdio.h>
+
+int pad[10];
+int GLOB[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+extern "C"
+void inc(int index) {
+ GLOB[index]++;
+}
diff --git a/lib/asan/tests/shared-lib-test.cc b/lib/asan/tests/shared-lib-test.cc
new file mode 100644
index 0000000..e492572
--- /dev/null
+++ b/lib/asan/tests/shared-lib-test.cc
@@ -0,0 +1,37 @@
+//===-- asan_rtl.cc ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+//===----------------------------------------------------------------------===//
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+using std::string;
+
+typedef void (fun_t)(int x);
+
+int main(int argc, char *argv[]) {
+ string path = string(argv[0]) + "-so.so";
+ printf("opening %s ... \n", path.c_str());
+ void *lib = dlopen(path.c_str(), RTLD_NOW);
+ if (!lib) {
+ printf("error in dlopen(): %s\n", dlerror());
+ return 1;
+ }
+ fun_t *inc = (fun_t*)dlsym(lib, "inc");
+ if (!inc) return 1;
+ printf("ok\n");
+ inc(1);
+ inc(-1);
+ return 0;
+}
diff --git a/lib/asan/tests/shared-lib-test.tmpl b/lib/asan/tests/shared-lib-test.tmpl
new file mode 100644
index 0000000..564e3eb
--- /dev/null
+++ b/lib/asan/tests/shared-lib-test.tmpl
@@ -0,0 +1,7 @@
+#.*ERROR: AddressSanitizer global-buffer-overflow on address 0x.* at pc 0x.* bp 0x.* sp 0x.*
+#READ of size 4 at 0x.* thread T0
+# #0 0x.* in inc .*shared-lib-test-so.cc:11
+# #1 0x.* in main .*shared-lib-test.cc:33
+# #2 0x.* in __libc_start_main.*
+#0x.* is located 4 bytes to the left of global variable 'GLOB' (.*) of size 40
+#0x.* is located 52 bytes to the right of global variable 'pad' (.*) of size 40
diff --git a/lib/asan/tests/stack-overflow.cc b/lib/asan/tests/stack-overflow.cc
new file mode 100644
index 0000000..dd86aa3
--- /dev/null
+++ b/lib/asan/tests/stack-overflow.cc
@@ -0,0 +1,7 @@
+#include <string.h>
+int main(int argc, char **argv) {
+ char x[10];
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ return res;
+}
diff --git a/lib/asan/tests/stack-overflow.tmpl b/lib/asan/tests/stack-overflow.tmpl
new file mode 100644
index 0000000..6aa717a
--- /dev/null
+++ b/lib/asan/tests/stack-overflow.tmpl
@@ -0,0 +1,3 @@
+READ of size 1 at 0x.* thread T0
+ #0 0x.* in main .*stack-overflow.cc:5
+Address 0x.* is .* frame <main>
diff --git a/lib/asan/tests/stack-use-after-return.cc b/lib/asan/tests/stack-use-after-return.cc
new file mode 100644
index 0000000..9098edf
--- /dev/null
+++ b/lib/asan/tests/stack-use-after-return.cc
@@ -0,0 +1,24 @@
+#include <stdio.h>
+
+__attribute__((noinline))
+char *Ident(char *x) {
+ fprintf(stderr, "1: %p\n", x);
+ return x;
+}
+
+__attribute__((noinline))
+char *Func1() {
+ char local;
+ return Ident(&local);
+}
+
+__attribute__((noinline))
+void Func2(char *x) {
+ fprintf(stderr, "2: %p\n", x);
+ *x = 1;
+}
+
+int main(int argc, char **argv) {
+ Func2(Func1());
+ return 0;
+}
diff --git a/lib/asan/tests/stack-use-after-return.disabled b/lib/asan/tests/stack-use-after-return.disabled
new file mode 100644
index 0000000..02729bc
--- /dev/null
+++ b/lib/asan/tests/stack-use-after-return.disabled
@@ -0,0 +1,3 @@
+WRITE of size 1 .* thread T0
+#0.*Func2.*stack-use-after-return.cc:18
+is located in frame <.*Func1.*> of T0's stack
diff --git a/lib/asan/tests/strncpy-overflow.cc b/lib/asan/tests/strncpy-overflow.cc
new file mode 100644
index 0000000..044f649
--- /dev/null
+++ b/lib/asan/tests/strncpy-overflow.cc
@@ -0,0 +1,9 @@
+#include <string.h>
+#include <stdlib.h>
+int main(int argc, char **argv) {
+ char *hello = (char*)malloc(6);
+ strcpy(hello, "hello");
+ char *short_buffer = (char*)malloc(9);
+ strncpy(short_buffer, hello, 10); // BOOM
+ return short_buffer[8];
+}
diff --git a/lib/asan/tests/strncpy-overflow.tmpl b/lib/asan/tests/strncpy-overflow.tmpl
new file mode 100644
index 0000000..3780aa8
--- /dev/null
+++ b/lib/asan/tests/strncpy-overflow.tmpl
@@ -0,0 +1,7 @@
+WRITE of size 1 at 0x.* thread T0
+ #0 0x.* in strncpy
+ #1 0x.* in main .*strncpy-overflow.cc:[78]
+0x.* is located 0 bytes to the right of 9-byte region
+allocated by thread T0 here:
+ #0 0x.* in malloc
+ #1 0x.* in main .*strncpy-overflow.cc:6
diff --git a/lib/asan/tests/test_output.sh b/lib/asan/tests/test_output.sh
new file mode 100755
index 0000000..c54b2364
--- /dev/null
+++ b/lib/asan/tests/test_output.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -e # fail on any error
+
+OS=`uname`
+CXX=$1
+CC=$2
+CXXFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer"
+SYMBOLIZER=../scripts/asan_symbolize.py
+
+C_TEST=use-after-free
+echo "Sanity checking a test in pure C"
+$CC -g -faddress-sanitizer -O2 $C_TEST.c
+./a.out 2>&1 | grep "heap-use-after-free" > /dev/null
+rm ./a.out
+
+echo "Sanity checking a test in pure C with -pie"
+$CC -g -faddress-sanitizer -O2 $C_TEST.c -pie
+./a.out 2>&1 | grep "heap-use-after-free" > /dev/null
+rm ./a.out
+
+for t in *.tmpl; do
+ for b in 32 64; do
+ for O in 0 1 2 3; do
+ c=`basename $t .tmpl`
+ c_so=$c-so
+ exe=$c.$b.O$O
+ so=$c.$b.O$O-so.so
+ echo testing $exe
+ $CXX $CXXFLAGS -g -m$b -faddress-sanitizer -O$O $c.cc -o $exe
+ [ -e "$c_so.cc" ] && $CXX $CXXFLAGS -g -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so
+ # If there's an OS-specific template, use it.
+ # Please minimize the use of OS-specific templates.
+ if [ -e "$t.$OS" ]
+ then
+ actual_t="$t.$OS"
+ else
+ actual_t="$t"
+ fi
+ ./$exe 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt | ./match_output.py $actual_t
+ rm ./$exe
+ [ -e "$so" ] && rm ./$so
+ done
+ done
+done
+
+exit 0
diff --git a/lib/asan/tests/use-after-free.c b/lib/asan/tests/use-after-free.c
new file mode 100644
index 0000000..60626bf
--- /dev/null
+++ b/lib/asan/tests/use-after-free.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+}
diff --git a/lib/asan/tests/use-after-free.cc b/lib/asan/tests/use-after-free.cc
new file mode 100644
index 0000000..60626bf
--- /dev/null
+++ b/lib/asan/tests/use-after-free.cc
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+}
diff --git a/lib/asan/tests/use-after-free.tmpl b/lib/asan/tests/use-after-free.tmpl
new file mode 100644
index 0000000..c4b5c74
--- /dev/null
+++ b/lib/asan/tests/use-after-free.tmpl
@@ -0,0 +1,10 @@
+.*ERROR: AddressSanitizer heap-use-after-free on address 0x.* at pc 0x.* bp 0x.* sp 0x.*
+READ of size 1 at 0x.* thread T0
+ #0 0x.* in main .*use-after-free.cc:5
+0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*
+freed by thread T0 here:
+ #0 0x.* in free
+ #1 0x.* in main .*use-after-free.cc:[45]
+previously allocated by thread T0 here:
+ #0 0x.* in malloc
+ #1 0x.* in main .*use-after-free.cc:3
diff --git a/lib/ashldi3.c b/lib/ashldi3.c
index adce4e2..6c558fe 100644
--- a/lib/ashldi3.c
+++ b/lib/ashldi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/ashrdi3.c b/lib/ashrdi3.c
index 03692a3..38ab716 100644
--- a/lib/ashrdi3.c
+++ b/lib/ashrdi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/assembly.h b/lib/assembly.h
index 0ce83ac..83bed12 100644
--- a/lib/assembly.h
+++ b/lib/assembly.h
@@ -35,15 +35,16 @@
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)
#ifdef VISIBILITY_HIDDEN
-#define DEFINE_COMPILERRT_FUNCTION(name) \
- .globl SYMBOL_NAME(name) SEPARATOR \
- HIDDEN_DIRECTIVE SYMBOL_NAME(name) SEPARATOR \
- SYMBOL_NAME(name):
+#define DECLARE_SYMBOL_VISIBILITY(name) \
+ HIDDEN_DIRECTIVE SYMBOL_NAME(name) SEPARATOR
#else
+#define DECLARE_SYMBOL_VISIBILITY(name)
+#endif
+
#define DEFINE_COMPILERRT_FUNCTION(name) \
.globl SYMBOL_NAME(name) SEPARATOR \
+ DECLARE_SYMBOL_VISIBILITY(name) \
SYMBOL_NAME(name):
-#endif
#define DEFINE_COMPILERRT_PRIVATE_FUNCTION(name) \
.globl SYMBOL_NAME(name) SEPARATOR \
diff --git a/lib/clear_cache.c b/lib/clear_cache.c
index 099b76e..fdd3316 100644
--- a/lib/clear_cache.c
+++ b/lib/clear_cache.c
@@ -8,8 +8,6 @@
* ===----------------------------------------------------------------------===
*/
-#include <stdlib.h>
-
#if __APPLE__
#include <libkern/OSCacheControl.h>
#endif
diff --git a/lib/clzdi2.c b/lib/clzdi2.c
index c281945..b9e64da 100644
--- a/lib/clzdi2.c
+++ b/lib/clzdi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/clzsi2.c b/lib/clzsi2.c
index d0a6aea..25b8ed2 100644
--- a/lib/clzsi2.c
+++ b/lib/clzsi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/cmpdi2.c b/lib/cmpdi2.c
index 999c3d2..c2b1f69 100644
--- a/lib/cmpdi2.c
+++ b/lib/cmpdi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/ctzdi2.c b/lib/ctzdi2.c
index b3d37d0..db3c6fd 100644
--- a/lib/ctzdi2.c
+++ b/lib/ctzdi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/ctzsi2.c b/lib/ctzsi2.c
index 2ff0e5d..c69486e 100644
--- a/lib/ctzsi2.c
+++ b/lib/ctzsi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/divdc3.c b/lib/divdc3.c
index 5f63298..cfbc498 100644
--- a/lib/divdc3.c
+++ b/lib/divdc3.c
@@ -13,8 +13,7 @@
*/
#include "int_lib.h"
-#include <math.h>
-#include <complex.h>
+#include "int_math.h"
/* Returns: the quotient of (a + ib) / (c + id) */
@@ -22,35 +21,37 @@ double _Complex
__divdc3(double __a, double __b, double __c, double __d)
{
int __ilogbw = 0;
- double __logbw = logb(fmax(fabs(__c), fabs(__d)));
- if (isfinite(__logbw))
+ double __logbw = crt_logb(crt_fmax(crt_fabs(__c), crt_fabs(__d)));
+ if (crt_isfinite(__logbw))
{
__ilogbw = (int)__logbw;
- __c = scalbn(__c, -__ilogbw);
- __d = scalbn(__d, -__ilogbw);
+ __c = crt_scalbn(__c, -__ilogbw);
+ __d = crt_scalbn(__d, -__ilogbw);
}
double __denom = __c * __c + __d * __d;
double _Complex z;
- __real__ z = scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
- __imag__ z = scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
- if (isnan(__real__ z) && isnan(__imag__ z))
+ __real__ z = crt_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
+ __imag__ z = crt_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
+ if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
{
- if ((__denom == 0.0) && (!isnan(__a) || !isnan(__b)))
+ if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b)))
{
- __real__ z = copysign(INFINITY, __c) * __a;
- __imag__ z = copysign(INFINITY, __c) * __b;
+ __real__ z = crt_copysign(CRT_INFINITY, __c) * __a;
+ __imag__ z = crt_copysign(CRT_INFINITY, __c) * __b;
}
- else if ((isinf(__a) || isinf(__b)) && isfinite(__c) && isfinite(__d))
+ else if ((crt_isinf(__a) || crt_isinf(__b)) &&
+ crt_isfinite(__c) && crt_isfinite(__d))
{
- __a = copysign(isinf(__a) ? 1.0 : 0.0, __a);
- __b = copysign(isinf(__b) ? 1.0 : 0.0, __b);
- __real__ z = INFINITY * (__a * __c + __b * __d);
- __imag__ z = INFINITY * (__b * __c - __a * __d);
+ __a = crt_copysign(crt_isinf(__a) ? 1.0 : 0.0, __a);
+ __b = crt_copysign(crt_isinf(__b) ? 1.0 : 0.0, __b);
+ __real__ z = CRT_INFINITY * (__a * __c + __b * __d);
+ __imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
}
- else if (isinf(__logbw) && __logbw > 0.0 && isfinite(__a) && isfinite(__b))
+ else if (crt_isinf(__logbw) && __logbw > 0.0 &&
+ crt_isfinite(__a) && crt_isfinite(__b))
{
- __c = copysign(isinf(__c) ? 1.0 : 0.0, __c);
- __d = copysign(isinf(__d) ? 1.0 : 0.0, __d);
+ __c = crt_copysign(crt_isinf(__c) ? 1.0 : 0.0, __c);
+ __d = crt_copysign(crt_isinf(__d) ? 1.0 : 0.0, __d);
__real__ z = 0.0 * (__a * __c + __b * __d);
__imag__ z = 0.0 * (__b * __c - __a * __d);
}
diff --git a/lib/divdf3.c b/lib/divdf3.c
index 925abd5..cc034dd 100644
--- a/lib/divdf3.c
+++ b/lib/divdf3.c
@@ -15,7 +15,6 @@
// underflow with correct rounding.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/divdi3.c b/lib/divdi3.c
index d62df56..2c2bcc2 100644
--- a/lib/divdi3.c
+++ b/lib/divdi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/divmoddi4.c b/lib/divmoddi4.c
index d3ca745..a2b8714 100644
--- a/lib/divmoddi4.c
+++ b/lib/divmoddi4.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/divmodsi4.c b/lib/divmodsi4.c
index 4dc1978..c7f7b1a 100644
--- a/lib/divmodsi4.c
+++ b/lib/divmodsi4.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/divsc3.c b/lib/divsc3.c
index a05f429..caa0c407 100644
--- a/lib/divsc3.c
+++ b/lib/divsc3.c
@@ -13,8 +13,7 @@
*/
#include "int_lib.h"
-#include <math.h>
-#include <complex.h>
+#include "int_math.h"
/* Returns: the quotient of (a + ib) / (c + id) */
@@ -22,35 +21,37 @@ float _Complex
__divsc3(float __a, float __b, float __c, float __d)
{
int __ilogbw = 0;
- float __logbw = logbf(fmaxf(fabsf(__c), fabsf(__d)));
- if (isfinite(__logbw))
+ float __logbw = crt_logbf(crt_fmaxf(crt_fabsf(__c), crt_fabsf(__d)));
+ if (crt_isfinite(__logbw))
{
__ilogbw = (int)__logbw;
- __c = scalbnf(__c, -__ilogbw);
- __d = scalbnf(__d, -__ilogbw);
+ __c = crt_scalbnf(__c, -__ilogbw);
+ __d = crt_scalbnf(__d, -__ilogbw);
}
float __denom = __c * __c + __d * __d;
float _Complex z;
- __real__ z = scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw);
- __imag__ z = scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw);
- if (isnan(__real__ z) && isnan(__imag__ z))
+ __real__ z = crt_scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw);
+ __imag__ z = crt_scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw);
+ if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
{
- if ((__denom == 0) && (!isnan(__a) || !isnan(__b)))
+ if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b)))
{
- __real__ z = copysignf(INFINITY, __c) * __a;
- __imag__ z = copysignf(INFINITY, __c) * __b;
+ __real__ z = crt_copysignf(CRT_INFINITY, __c) * __a;
+ __imag__ z = crt_copysignf(CRT_INFINITY, __c) * __b;
}
- else if ((isinf(__a) || isinf(__b)) && isfinite(__c) && isfinite(__d))
+ else if ((crt_isinf(__a) || crt_isinf(__b)) &&
+ crt_isfinite(__c) && crt_isfinite(__d))
{
- __a = copysignf(isinf(__a) ? 1 : 0, __a);
- __b = copysignf(isinf(__b) ? 1 : 0, __b);
- __real__ z = INFINITY * (__a * __c + __b * __d);
- __imag__ z = INFINITY * (__b * __c - __a * __d);
+ __a = crt_copysignf(crt_isinf(__a) ? 1 : 0, __a);
+ __b = crt_copysignf(crt_isinf(__b) ? 1 : 0, __b);
+ __real__ z = CRT_INFINITY * (__a * __c + __b * __d);
+ __imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
}
- else if (isinf(__logbw) && __logbw > 0 && isfinite(__a) && isfinite(__b))
+ else if (crt_isinf(__logbw) && __logbw > 0 &&
+ crt_isfinite(__a) && crt_isfinite(__b))
{
- __c = copysignf(isinf(__c) ? 1 : 0, __c);
- __d = copysignf(isinf(__d) ? 1 : 0, __d);
+ __c = crt_copysignf(crt_isinf(__c) ? 1 : 0, __c);
+ __d = crt_copysignf(crt_isinf(__d) ? 1 : 0, __d);
__real__ z = 0 * (__a * __c + __b * __d);
__imag__ z = 0 * (__b * __c - __a * __d);
}
diff --git a/lib/divsf3.c b/lib/divsf3.c
index b733307..a8230e4 100644
--- a/lib/divsf3.c
+++ b/lib/divsf3.c
@@ -15,7 +15,6 @@
// underflow with correct rounding.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/divsi3.c b/lib/divsi3.c
index 01ef274..0d81cb8 100644
--- a/lib/divsi3.c
+++ b/lib/divsi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/divxc3.c b/lib/divxc3.c
index f054d40..5f240e9 100644
--- a/lib/divxc3.c
+++ b/lib/divxc3.c
@@ -14,8 +14,7 @@
#if !_ARCH_PPC
#include "int_lib.h"
-#include <math.h>
-#include <complex.h>
+#include "int_math.h"
/* Returns: the quotient of (a + ib) / (c + id) */
@@ -23,35 +22,37 @@ long double _Complex
__divxc3(long double __a, long double __b, long double __c, long double __d)
{
int __ilogbw = 0;
- long double __logbw = logbl(fmaxl(fabsl(__c), fabsl(__d)));
- if (isfinite(__logbw))
+ long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
+ if (crt_isfinite(__logbw))
{
__ilogbw = (int)__logbw;
- __c = scalbnl(__c, -__ilogbw);
- __d = scalbnl(__d, -__ilogbw);
+ __c = crt_scalbnl(__c, -__ilogbw);
+ __d = crt_scalbnl(__d, -__ilogbw);
}
long double __denom = __c * __c + __d * __d;
long double _Complex z;
- __real__ z = scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw);
- __imag__ z = scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw);
- if (isnan(__real__ z) && isnan(__imag__ z))
+ __real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw);
+ __imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw);
+ if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
{
- if ((__denom == 0) && (!isnan(__a) || !isnan(__b)))
+ if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b)))
{
- __real__ z = copysignl(INFINITY, __c) * __a;
- __imag__ z = copysignl(INFINITY, __c) * __b;
+ __real__ z = crt_copysignl(CRT_INFINITY, __c) * __a;
+ __imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b;
}
- else if ((isinf(__a) || isinf(__b)) && isfinite(__c) && isfinite(__d))
+ else if ((crt_isinf(__a) || crt_isinf(__b)) &&
+ crt_isfinite(__c) && crt_isfinite(__d))
{
- __a = copysignl(isinf(__a) ? 1 : 0, __a);
- __b = copysignl(isinf(__b) ? 1 : 0, __b);
- __real__ z = INFINITY * (__a * __c + __b * __d);
- __imag__ z = INFINITY * (__b * __c - __a * __d);
+ __a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a);
+ __b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b);
+ __real__ z = CRT_INFINITY * (__a * __c + __b * __d);
+ __imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
}
- else if (isinf(__logbw) && __logbw > 0 && isfinite(__a) && isfinite(__b))
+ else if (crt_isinf(__logbw) && __logbw > 0 &&
+ crt_isfinite(__a) && crt_isfinite(__b))
{
- __c = copysignl(isinf(__c) ? 1 : 0, __c);
- __d = copysignl(isinf(__d) ? 1 : 0, __d);
+ __c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c);
+ __d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d);
__real__ z = 0 * (__a * __c + __b * __d);
__imag__ z = 0 * (__b * __c - __a * __d);
}
diff --git a/lib/enable_execute_stack.c b/lib/enable_execute_stack.c
index 7ab8e8d..278ca24 100644
--- a/lib/enable_execute_stack.c
+++ b/lib/enable_execute_stack.c
@@ -8,7 +8,8 @@
* ===----------------------------------------------------------------------===
*/
-#include <stdint.h>
+#include "int_lib.h"
+
#include <sys/mman.h>
/* #include "config.h"
diff --git a/lib/eprintf.c b/lib/eprintf.c
index 7c79174..b07d624 100644
--- a/lib/eprintf.c
+++ b/lib/eprintf.c
@@ -12,7 +12,6 @@
#include "int_lib.h"
#include <stdio.h>
-#include <stdlib.h>
/*
diff --git a/lib/extendsfdf2.c b/lib/extendsfdf2.c
index c0b628d..9466de7 100644
--- a/lib/extendsfdf2.c
+++ b/lib/extendsfdf2.c
@@ -38,10 +38,7 @@
//
//===----------------------------------------------------------------------===//
-#include <stdint.h>
-#include <limits.h>
-
-#include "abi.h"
+#include "int_lib.h"
typedef float src_t;
typedef uint32_t src_rep_t;
diff --git a/lib/ffsdi2.c b/lib/ffsdi2.c
index 89f1b7b..a5ac990 100644
--- a/lib/ffsdi2.c
+++ b/lib/ffsdi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/fixdfdi.c b/lib/fixdfdi.c
index 85a456d..c6732db 100644
--- a/lib/fixdfdi.c
+++ b/lib/fixdfdi.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/fixdfsi.c b/lib/fixdfsi.c
index fbcf147..3d4379e 100644
--- a/lib/fixdfsi.c
+++ b/lib/fixdfsi.c
@@ -12,7 +12,6 @@
// conversion is undefined for out of range values in the C standard.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/fixsfdi.c b/lib/fixsfdi.c
index d80e33e..81ceab0 100644
--- a/lib/fixsfdi.c
+++ b/lib/fixsfdi.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/fixsfsi.c b/lib/fixsfsi.c
index 67749a5..f6de609 100644
--- a/lib/fixsfsi.c
+++ b/lib/fixsfsi.c
@@ -12,7 +12,6 @@
// conversion is undefined for out of range values in the C standard.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/fixunsdfdi.c b/lib/fixunsdfdi.c
index d80b84a..c0ff160 100644
--- a/lib/fixunsdfdi.c
+++ b/lib/fixunsdfdi.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/fixunsdfsi.c b/lib/fixunsdfsi.c
index ecdfb5d..2ce4999 100644
--- a/lib/fixunsdfsi.c
+++ b/lib/fixunsdfsi.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/fixunssfdi.c b/lib/fixunssfdi.c
index 1506420..09078db 100644
--- a/lib/fixunssfdi.c
+++ b/lib/fixunssfdi.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
/* Returns: convert a to a unsigned long long, rounding toward zero.
diff --git a/lib/fixunssfsi.c b/lib/fixunssfsi.c
index dbaa511..d80ed18 100644
--- a/lib/fixunssfsi.c
+++ b/lib/fixunssfsi.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/floatdidf.c b/lib/floatdidf.c
index 5ba9526..2af9e10 100644
--- a/lib/floatdidf.c
+++ b/lib/floatdidf.c
@@ -11,10 +11,8 @@
*
*===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a double, rounding toward even. */
@@ -30,7 +28,6 @@ ARM_EABI_FNALIAS(l2d, floatdidf);
/* Support for systems that have hardware floating-point; we'll set the inexact flag
* as a side-effect of this computation.
*/
-#include <stdint.h>
COMPILER_RT_ABI double
__floatdidf(di_int a)
diff --git a/lib/floatdisf.c b/lib/floatdisf.c
index 4dc13ca..6607307 100644
--- a/lib/floatdisf.c
+++ b/lib/floatdisf.c
@@ -12,9 +12,6 @@
*===----------------------------------------------------------------------===
*/
-#include "abi.h"
-#include <float.h>
-
/* Returns: convert a to a float, rounding toward even.*/
/* Assumption: float is a IEEE 32 bit floating point type
diff --git a/lib/floatsidf.c b/lib/floatsidf.c
index 7227335..74cb66b 100644
--- a/lib/floatsidf.c
+++ b/lib/floatsidf.c
@@ -12,7 +12,6 @@
// mode.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/floatsisf.c b/lib/floatsisf.c
index e5250ff..a981391 100644
--- a/lib/floatsisf.c
+++ b/lib/floatsisf.c
@@ -12,7 +12,6 @@
// mode.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/floattidf.c b/lib/floattidf.c
index f61844d..3cafea8 100644
--- a/lib/floattidf.c
+++ b/lib/floattidf.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a double, rounding toward even.*/
diff --git a/lib/floattisf.c b/lib/floattisf.c
index 7eb761d..ab33e4a 100644
--- a/lib/floattisf.c
+++ b/lib/floattisf.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a float, rounding toward even. */
diff --git a/lib/floattixf.c b/lib/floattixf.c
index e4bcb5f..852acc7 100644
--- a/lib/floattixf.c
+++ b/lib/floattixf.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a long double, rounding toward even. */
diff --git a/lib/floatundidf.c b/lib/floatundidf.c
index e74e9d8..6791701 100644
--- a/lib/floatundidf.c
+++ b/lib/floatundidf.c
@@ -12,9 +12,6 @@
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
-#include <float.h>
-
/* Returns: convert a to a double, rounding toward even. */
/* Assumption: double is a IEEE 64 bit floating point type
@@ -32,7 +29,6 @@ ARM_EABI_FNALIAS(ul2d, floatundidf);
* as a side-effect of this computation.
*/
-#include <stdint.h>
COMPILER_RT_ABI double
__floatundidf(du_int a)
diff --git a/lib/floatundisf.c b/lib/floatundisf.c
index eea45a7..1bf5fbb 100644
--- a/lib/floatundisf.c
+++ b/lib/floatundisf.c
@@ -12,9 +12,6 @@
*===----------------------------------------------------------------------===
*/
-#include "abi.h"
-#include <float.h>
-
/* Returns: convert a to a float, rounding toward even. */
/* Assumption: float is a IEEE 32 bit floating point type
diff --git a/lib/floatunsidf.c b/lib/floatunsidf.c
index 3756299..0722248 100644
--- a/lib/floatunsidf.c
+++ b/lib/floatunsidf.c
@@ -12,7 +12,6 @@
// mode.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/floatunsisf.c b/lib/floatunsisf.c
index 14ef103..3dc1cd4 100644
--- a/lib/floatunsisf.c
+++ b/lib/floatunsisf.c
@@ -12,7 +12,6 @@
// mode.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/floatuntidf.c b/lib/floatuntidf.c
index bab7483..d0889a0 100644
--- a/lib/floatuntidf.c
+++ b/lib/floatuntidf.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a double, rounding toward even. */
diff --git a/lib/floatuntisf.c b/lib/floatuntisf.c
index 0ab6d7e..f552758 100644
--- a/lib/floatuntisf.c
+++ b/lib/floatuntisf.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a float, rounding toward even. */
diff --git a/lib/floatuntixf.c b/lib/floatuntixf.c
index a84709a..00c07d8 100644
--- a/lib/floatuntixf.c
+++ b/lib/floatuntixf.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <float.h>
/* Returns: convert a to a long double, rounding toward even. */
diff --git a/lib/fp_lib.h b/lib/fp_lib.h
index 6c9455a..de5f17f 100644
--- a/lib/fp_lib.h
+++ b/lib/fp_lib.h
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>
+#include "int_lib.h"
#if defined SINGLE_PRECISION
diff --git a/lib/gcc_personality_v0.c b/lib/gcc_personality_v0.c
index c840eef..8a708ca 100644
--- a/lib/gcc_personality_v0.c
+++ b/lib/gcc_personality_v0.c
@@ -9,10 +9,6 @@
*
*/
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
#include "int_lib.h"
/*
@@ -202,7 +198,7 @@ _Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions,
/* There is nothing to do if there is no LSDA for this frame. */
const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context);
- if ( lsda == NULL )
+ if ( lsda == (uint8_t*) 0 )
return _URC_CONTINUE_UNWIND;
uintptr_t pc = _Unwind_GetIP(context)-1;
diff --git a/lib/i386/Makefile.mk b/lib/i386/Makefile.mk
index 20f95e6..1f5c680 100644
--- a/lib/i386/Makefile.mk
+++ b/lib/i386/Makefile.mk
@@ -7,6 +7,7 @@
#
#===------------------------------------------------------------------------===#
+ModuleName := builtins
SubDirs :=
OnlyArchs := i386
diff --git a/lib/endianness.h b/lib/int_endianness.h
index 9b7e7c1..9466ed4 100644
--- a/lib/endianness.h
+++ b/lib/int_endianness.h
@@ -1,4 +1,4 @@
-/* ===-- endianness.h - configuration header for compiler-rt ---------------===
+/* ===-- int_endianness.h - configuration header for compiler-rt ------------===
*
* The LLVM Compiler Infrastructure
*
@@ -13,13 +13,8 @@
* ===----------------------------------------------------------------------===
*/
-#ifndef ENDIANNESS_H
-#define ENDIANNESS_H
-
-/*
- * Known limitations:
- * Middle endian systems are not handled currently.
- */
+#ifndef INT_ENDIANNESS_H
+#define INT_ENDIANNESS_H
#if defined(__SVR4) && defined(__sun)
#include <sys/byteorder.h>
@@ -91,4 +86,4 @@
#error Unable to determine endian
#endif /* Check we found an endianness correctly. */
-#endif /* ENDIANNESS_H */
+#endif /* INT_ENDIANNESS_H */
diff --git a/lib/int_lib.h b/lib/int_lib.h
index e1fd6b7..a87426c 100644
--- a/lib/int_lib.h
+++ b/lib/int_lib.h
@@ -16,141 +16,31 @@
#ifndef INT_LIB_H
#define INT_LIB_H
-/* Assumption: signed integral is 2's complement */
-/* Assumption: right shift of signed negative is arithmetic shift */
+/* Assumption: Signed integral is 2's complement. */
+/* Assumption: Right shift of signed negative is arithmetic shift. */
+/* Assumption: Endianness is little or big (not mixed). */
-#include <limits.h>
-#include <stdint.h>
-#include "endianness.h"
-#include <math.h>
+/* ABI macro definitions */
-/* If compiling for kernel use, call panic() instead of abort(). */
-#ifdef KERNEL_USE
-extern void panic (const char *, ...);
-#define compilerrt_abort() \
- panic("%s:%d: abort in %s", __FILE__, __LINE__, __FUNCTION__)
+#if __ARM_EABI__
+# define ARM_EABI_FNALIAS(aeabi_name, name) \
+ void __aeabi_##aeabi_name() __attribute__((alias("__" #name)));
+# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
#else
-#define compilerrt_abort() abort()
+# define ARM_EABI_FNALIAS(aeabi_name, name)
+# define COMPILER_RT_ABI
#endif
-#if !defined(INFINITY) && defined(HUGE_VAL)
-#define INFINITY HUGE_VAL
-#endif /* INFINITY */
-
-typedef int si_int;
-typedef unsigned su_int;
-
-typedef long long di_int;
-typedef unsigned long long du_int;
-
-typedef union
-{
- di_int all;
- struct
- {
-#if _YUGA_LITTLE_ENDIAN
- su_int low;
- si_int high;
-#else
- si_int high;
- su_int low;
-#endif /* _YUGA_LITTLE_ENDIAN */
- }s;
-} dwords;
-
-typedef union
-{
- du_int all;
- struct
- {
-#if _YUGA_LITTLE_ENDIAN
- su_int low;
- su_int high;
-#else
- su_int high;
- su_int low;
-#endif /* _YUGA_LITTLE_ENDIAN */
- }s;
-} udwords;
-
-#if __x86_64
-
-typedef int ti_int __attribute__ ((mode (TI)));
-typedef unsigned tu_int __attribute__ ((mode (TI)));
-
-typedef union
-{
- ti_int all;
- struct
- {
-#if _YUGA_LITTLE_ENDIAN
- du_int low;
- di_int high;
-#else
- di_int high;
- du_int low;
-#endif /* _YUGA_LITTLE_ENDIAN */
- }s;
-} twords;
-
-typedef union
-{
- tu_int all;
- struct
- {
-#if _YUGA_LITTLE_ENDIAN
- du_int low;
- du_int high;
-#else
- du_int high;
- du_int low;
-#endif /* _YUGA_LITTLE_ENDIAN */
- }s;
-} utwords;
-
-static inline ti_int make_ti(di_int h, di_int l) {
- twords r;
- r.s.high = h;
- r.s.low = l;
- return r.all;
-}
-
-static inline tu_int make_tu(du_int h, du_int l) {
- utwords r;
- r.s.high = h;
- r.s.low = l;
- return r.all;
-}
-
-#endif /* __x86_64 */
-
-typedef union
-{
- su_int u;
- float f;
-} float_bits;
-
-typedef union
-{
- udwords u;
- double f;
-} double_bits;
+/* Include the standard compiler builtin headers we use functionality from. */
+#include <limits.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <float.h>
-typedef struct
-{
-#if _YUGA_LITTLE_ENDIAN
- udwords low;
- udwords high;
-#else
- udwords high;
- udwords low;
-#endif /* _YUGA_LITTLE_ENDIAN */
-} uqwords;
+/* Include the commonly used internal type definitions. */
+#include "int_types.h"
-typedef union
-{
- uqwords u;
- long double f;
-} long_double_bits;
+/* Include internal utility function declarations. */
+#include "int_util.h"
#endif /* INT_LIB_H */
diff --git a/lib/int_math.h b/lib/int_math.h
new file mode 100644
index 0000000..d6b4bda
--- /dev/null
+++ b/lib/int_math.h
@@ -0,0 +1,67 @@
+/* ===-- int_math.h - internal math inlines ---------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===-----------------------------------------------------------------------===
+ *
+ * This file is not part of the interface of this library.
+ *
+ * This file defines substitutes for the libm functions used in some of the
+ * compiler-rt implementations, defined in such a way that there is not a direct
+ * dependency on libm or math.h. Instead, we use the compiler builtin versions
+ * where available. This reduces our dependencies on the system SDK by foisting
+ * the responsibility onto the compiler.
+ *
+ * ===-----------------------------------------------------------------------===
+ */
+
+#ifndef INT_MATH_H
+#define INT_MATH_H
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+#define CRT_INFINITY __builtin_huge_valf()
+
+#define crt_isinf(x) __builtin_isinf((x))
+#define crt_isnan(x) __builtin_isnan((x))
+
+/* Define crt_isfinite in terms of the builtin if available, otherwise provide
+ * an alternate version in terms of our other functions. This supports some
+ * versions of GCC which didn't have __builtin_isfinite.
+ */
+#if __has_builtin(__builtin_isfinite)
+# define crt_isfinite(x) __builtin_isfinite((x))
+#else
+# define crt_isfinite(x) \
+ __extension__(({ \
+ __typeof((x)) x_ = (x); \
+ !crt_isinf(x_) && !crt_isnan(x_); \
+ }))
+#endif
+
+#define crt_copysign(x, y) __builtin_copysign((x), (y))
+#define crt_copysignf(x, y) __builtin_copysignf((x), (y))
+#define crt_copysignl(x, y) __builtin_copysignl((x), (y))
+
+#define crt_fabs(x) __builtin_fabs((x))
+#define crt_fabsf(x) __builtin_fabsf((x))
+#define crt_fabsl(x) __builtin_fabsl((x))
+
+#define crt_fmax(x, y) __builtin_fmax((x), (y))
+#define crt_fmaxf(x, y) __builtin_fmaxf((x), (y))
+#define crt_fmaxl(x, y) __builtin_fmaxl((x), (y))
+
+#define crt_logb(x) __builtin_logb((x))
+#define crt_logbf(x) __builtin_logbf((x))
+#define crt_logbl(x) __builtin_logbl((x))
+
+#define crt_scalbn(x, y) __builtin_scalbn((x), (y))
+#define crt_scalbnf(x, y) __builtin_scalbnf((x), (y))
+#define crt_scalbnl(x, y) __builtin_scalbnl((x), (y))
+
+#endif /* INT_MATH_H */
diff --git a/lib/int_types.h b/lib/int_types.h
new file mode 100644
index 0000000..fcce390
--- /dev/null
+++ b/lib/int_types.h
@@ -0,0 +1,140 @@
+/* ===-- int_lib.h - configuration header for compiler-rt -----------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file is not part of the interface of this library.
+ *
+ * This file defines various standard types, most importantly a number of unions
+ * used to access parts of larger types.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#ifndef INT_TYPES_H
+#define INT_TYPES_H
+
+#include "int_endianness.h"
+
+typedef int si_int;
+typedef unsigned su_int;
+
+typedef long long di_int;
+typedef unsigned long long du_int;
+
+typedef union
+{
+ di_int all;
+ struct
+ {
+#if _YUGA_LITTLE_ENDIAN
+ su_int low;
+ si_int high;
+#else
+ si_int high;
+ su_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+ }s;
+} dwords;
+
+typedef union
+{
+ du_int all;
+ struct
+ {
+#if _YUGA_LITTLE_ENDIAN
+ su_int low;
+ su_int high;
+#else
+ su_int high;
+ su_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+ }s;
+} udwords;
+
+#if __x86_64
+
+typedef int ti_int __attribute__ ((mode (TI)));
+typedef unsigned tu_int __attribute__ ((mode (TI)));
+
+typedef union
+{
+ ti_int all;
+ struct
+ {
+#if _YUGA_LITTLE_ENDIAN
+ du_int low;
+ di_int high;
+#else
+ di_int high;
+ du_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+ }s;
+} twords;
+
+typedef union
+{
+ tu_int all;
+ struct
+ {
+#if _YUGA_LITTLE_ENDIAN
+ du_int low;
+ du_int high;
+#else
+ du_int high;
+ du_int low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+ }s;
+} utwords;
+
+static inline ti_int make_ti(di_int h, di_int l) {
+ twords r;
+ r.s.high = h;
+ r.s.low = l;
+ return r.all;
+}
+
+static inline tu_int make_tu(du_int h, du_int l) {
+ utwords r;
+ r.s.high = h;
+ r.s.low = l;
+ return r.all;
+}
+
+#endif /* __x86_64 */
+
+typedef union
+{
+ su_int u;
+ float f;
+} float_bits;
+
+typedef union
+{
+ udwords u;
+ double f;
+} double_bits;
+
+typedef struct
+{
+#if _YUGA_LITTLE_ENDIAN
+ udwords low;
+ udwords high;
+#else
+ udwords high;
+ udwords low;
+#endif /* _YUGA_LITTLE_ENDIAN */
+} uqwords;
+
+typedef union
+{
+ uqwords u;
+ long double f;
+} long_double_bits;
+
+#endif /* INT_TYPES_H */
+
diff --git a/lib/int_util.c b/lib/int_util.c
new file mode 100644
index 0000000..f194768
--- /dev/null
+++ b/lib/int_util.c
@@ -0,0 +1,43 @@
+/* ===-- int_util.c - Implement internal utilities --------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_util.h"
+#include "int_lib.h"
+
+/* NOTE: The definitions in this file are declared weak because we clients to be
+ * able to arbitrarily package individual functions into separate .a files. If
+ * we did not declare these weak, some link situations might end up seeing
+ * duplicate strong definitions of the same symbol.
+ *
+ * We can't use this solution for kernel use (which may not support weak), but
+ * currently expect that when built for kernel use all the functionality is
+ * packaged into a single library.
+ */
+
+#ifdef KERNEL_USE
+
+extern void panic(const char *, ...) __attribute__((noreturn));
+__attribute__((visibility("hidden")))
+void compilerrt_abort_impl(const char *file, int line, const char *function) {
+ panic("%s:%d: abort in %s", file, line, function);
+}
+
+#else
+
+/* Get the system definition of abort() */
+#include <stdlib.h>
+
+__attribute__((weak))
+__attribute__((visibility("hidden")))
+void compilerrt_abort_impl(const char *file, int line, const char *function) {
+ abort();
+}
+
+#endif
diff --git a/lib/int_util.h b/lib/int_util.h
new file mode 100644
index 0000000..17d7722
--- /dev/null
+++ b/lib/int_util.h
@@ -0,0 +1,32 @@
+/* ===-- int_util.h - internal utility functions ----------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===-----------------------------------------------------------------------===
+ *
+ * This file is not part of the interface of this library.
+ *
+ * This file defines non-inline utilities which are available for use in the
+ * library. The function definitions themselves are all contained in int_util.c
+ * which will always be compiled into any compiler-rt library.
+ *
+ * ===-----------------------------------------------------------------------===
+ */
+
+#ifndef INT_UTIL_H
+#define INT_UTIL_H
+
+/** \brief Trigger a program abort (or panic for kernel code). */
+#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, \
+ __FUNCTION__)
+void compilerrt_abort_impl(const char *file, int line,
+ const char *function)
+#ifndef KERNEL_USE
+ __attribute__((weak))
+#endif
+ __attribute__((noreturn)) __attribute__((visibility("hidden")));
+
+#endif /* INT_UTIL_H */
diff --git a/lib/lshrdi3.c b/lib/lshrdi3.c
index 911edb1..8af3e0c 100644
--- a/lib/lshrdi3.c
+++ b/lib/lshrdi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/moddi3.c b/lib/moddi3.c
index af0a8083..2f3b9cc 100644
--- a/lib/moddi3.c
+++ b/lib/moddi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/modsi3.c b/lib/modsi3.c
index 05ce806..d16213c 100644
--- a/lib/modsi3.c
+++ b/lib/modsi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/muldc3.c b/lib/muldc3.c
index 9f9bd2a..5f4a6d1 100644
--- a/lib/muldc3.c
+++ b/lib/muldc3.c
@@ -13,8 +13,7 @@
*/
#include "int_lib.h"
-#include <math.h>
-#include <complex.h>
+#include "int_math.h"
/* Returns: the product of a + ib and c + id */
@@ -28,46 +27,46 @@ __muldc3(double __a, double __b, double __c, double __d)
double _Complex z;
__real__ z = __ac - __bd;
__imag__ z = __ad + __bc;
- if (isnan(__real__ z) && isnan(__imag__ z))
+ if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
{
int __recalc = 0;
- if (isinf(__a) || isinf(__b))
+ if (crt_isinf(__a) || crt_isinf(__b))
{
- __a = copysign(isinf(__a) ? 1 : 0, __a);
- __b = copysign(isinf(__b) ? 1 : 0, __b);
- if (isnan(__c))
- __c = copysign(0, __c);
- if (isnan(__d))
- __d = copysign(0, __d);
+ __a = crt_copysign(crt_isinf(__a) ? 1 : 0, __a);
+ __b = crt_copysign(crt_isinf(__b) ? 1 : 0, __b);
+ if (crt_isnan(__c))
+ __c = crt_copysign(0, __c);
+ if (crt_isnan(__d))
+ __d = crt_copysign(0, __d);
__recalc = 1;
}
- if (isinf(__c) || isinf(__d))
+ if (crt_isinf(__c) || crt_isinf(__d))
{
- __c = copysign(isinf(__c) ? 1 : 0, __c);
- __d = copysign(isinf(__d) ? 1 : 0, __d);
- if (isnan(__a))
- __a = copysign(0, __a);
- if (isnan(__b))
- __b = copysign(0, __b);
+ __c = crt_copysign(crt_isinf(__c) ? 1 : 0, __c);
+ __d = crt_copysign(crt_isinf(__d) ? 1 : 0, __d);
+ if (crt_isnan(__a))
+ __a = crt_copysign(0, __a);
+ if (crt_isnan(__b))
+ __b = crt_copysign(0, __b);
__recalc = 1;
}
- if (!__recalc && (isinf(__ac) || isinf(__bd) ||
- isinf(__ad) || isinf(__bc)))
+ if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) ||
+ crt_isinf(__ad) || crt_isinf(__bc)))
{
- if (isnan(__a))
- __a = copysign(0, __a);
- if (isnan(__b))
- __b = copysign(0, __b);
- if (isnan(__c))
- __c = copysign(0, __c);
- if (isnan(__d))
- __d = copysign(0, __d);
+ if (crt_isnan(__a))
+ __a = crt_copysign(0, __a);
+ if (crt_isnan(__b))
+ __b = crt_copysign(0, __b);
+ if (crt_isnan(__c))
+ __c = crt_copysign(0, __c);
+ if (crt_isnan(__d))
+ __d = crt_copysign(0, __d);
__recalc = 1;
}
if (__recalc)
{
- __real__ z = INFINITY * (__a * __c - __b * __d);
- __imag__ z = INFINITY * (__a * __d + __b * __c);
+ __real__ z = CRT_INFINITY * (__a * __c - __b * __d);
+ __imag__ z = CRT_INFINITY * (__a * __d + __b * __c);
}
}
return z;
diff --git a/lib/muldf3.c b/lib/muldf3.c
index f402cfb..86d72d8 100644
--- a/lib/muldf3.c
+++ b/lib/muldf3.c
@@ -11,7 +11,6 @@
// with the IEEE-754 default rounding (to nearest, ties to even).
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/muldi3.c b/lib/muldi3.c
index e6322bf..3e99630 100644
--- a/lib/muldi3.c
+++ b/lib/muldi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/mulodi4.c b/lib/mulodi4.c
new file mode 100644
index 0000000..0c1b5cd
--- /dev/null
+++ b/lib/mulodi4.c
@@ -0,0 +1,58 @@
+/*===-- mulodi4.c - Implement __mulodi4 -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __mulodi4 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+
+/* Returns: a * b */
+
+/* Effects: sets *overflow to 1 if a * b overflows */
+
+di_int
+__mulodi4(di_int a, di_int b, int* overflow)
+{
+ const int N = (int)(sizeof(di_int) * CHAR_BIT);
+ const di_int MIN = (di_int)1 << (N-1);
+ const di_int MAX = ~MIN;
+ *overflow = 0;
+ di_int result = a * b;
+ if (a == MIN)
+ {
+ if (b != 0 && b != 1)
+ *overflow = 1;
+ return result;
+ }
+ if (b == MIN)
+ {
+ if (a != 0 && a != 1)
+ *overflow = 1;
+ return result;
+ }
+ di_int sa = a >> (N - 1);
+ di_int abs_a = (a ^ sa) - sa;
+ di_int sb = b >> (N - 1);
+ di_int abs_b = (b ^ sb) - sb;
+ if (abs_a < 2 || abs_b < 2)
+ return result;
+ if (sa == sb)
+ {
+ if (abs_a > MAX / abs_b)
+ *overflow = 1;
+ }
+ else
+ {
+ if (abs_a > MIN / -abs_b)
+ *overflow = 1;
+ }
+ return result;
+}
diff --git a/lib/mulosi4.c b/lib/mulosi4.c
new file mode 100644
index 0000000..f3398d1
--- /dev/null
+++ b/lib/mulosi4.c
@@ -0,0 +1,58 @@
+/*===-- mulosi4.c - Implement __mulosi4 -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __mulosi4 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+
+/* Returns: a * b */
+
+/* Effects: sets *overflow to 1 if a * b overflows */
+
+si_int
+__mulosi4(si_int a, si_int b, int* overflow)
+{
+ const int N = (int)(sizeof(si_int) * CHAR_BIT);
+ const si_int MIN = (si_int)1 << (N-1);
+ const si_int MAX = ~MIN;
+ *overflow = 0;
+ si_int result = a * b;
+ if (a == MIN)
+ {
+ if (b != 0 && b != 1)
+ *overflow = 1;
+ return result;
+ }
+ if (b == MIN)
+ {
+ if (a != 0 && a != 1)
+ *overflow = 1;
+ return result;
+ }
+ si_int sa = a >> (N - 1);
+ si_int abs_a = (a ^ sa) - sa;
+ si_int sb = b >> (N - 1);
+ si_int abs_b = (b ^ sb) - sb;
+ if (abs_a < 2 || abs_b < 2)
+ return result;
+ if (sa == sb)
+ {
+ if (abs_a > MAX / abs_b)
+ *overflow = 1;
+ }
+ else
+ {
+ if (abs_a > MIN / -abs_b)
+ *overflow = 1;
+ }
+ return result;
+}
diff --git a/lib/muloti4.c b/lib/muloti4.c
new file mode 100644
index 0000000..1fcd0ba
--- /dev/null
+++ b/lib/muloti4.c
@@ -0,0 +1,62 @@
+/*===-- muloti4.c - Implement __muloti4 -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __muloti4 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#if __x86_64
+
+#include "int_lib.h"
+
+/* Returns: a * b */
+
+/* Effects: sets *overflow to 1 if a * b overflows */
+
+ti_int
+__muloti4(ti_int a, ti_int b, int* overflow)
+{
+ const int N = (int)(sizeof(ti_int) * CHAR_BIT);
+ const ti_int MIN = (ti_int)1 << (N-1);
+ const ti_int MAX = ~MIN;
+ *overflow = 0;
+ ti_int result = a * b;
+ if (a == MIN)
+ {
+ if (b != 0 && b != 1)
+ *overflow = 1;
+ return result;
+ }
+ if (b == MIN)
+ {
+ if (a != 0 && a != 1)
+ *overflow = 1;
+ return result;
+ }
+ ti_int sa = a >> (N - 1);
+ ti_int abs_a = (a ^ sa) - sa;
+ ti_int sb = b >> (N - 1);
+ ti_int abs_b = (b ^ sb) - sb;
+ if (abs_a < 2 || abs_b < 2)
+ return result;
+ if (sa == sb)
+ {
+ if (abs_a > MAX / abs_b)
+ *overflow = 1;
+ }
+ else
+ {
+ if (abs_a > MIN / -abs_b)
+ *overflow = 1;
+ }
+ return result;
+}
+
+#endif
diff --git a/lib/mulsc3.c b/lib/mulsc3.c
index a878ba1..6d433fb 100644
--- a/lib/mulsc3.c
+++ b/lib/mulsc3.c
@@ -13,8 +13,7 @@
*/
#include "int_lib.h"
-#include <math.h>
-#include <complex.h>
+#include "int_math.h"
/* Returns: the product of a + ib and c + id */
@@ -28,46 +27,46 @@ __mulsc3(float __a, float __b, float __c, float __d)
float _Complex z;
__real__ z = __ac - __bd;
__imag__ z = __ad + __bc;
- if (isnan(__real__ z) && isnan(__imag__ z))
+ if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
{
int __recalc = 0;
- if (isinf(__a) || isinf(__b))
+ if (crt_isinf(__a) || crt_isinf(__b))
{
- __a = copysignf(isinf(__a) ? 1 : 0, __a);
- __b = copysignf(isinf(__b) ? 1 : 0, __b);
- if (isnan(__c))
- __c = copysignf(0, __c);
- if (isnan(__d))
- __d = copysignf(0, __d);
+ __a = crt_copysignf(crt_isinf(__a) ? 1 : 0, __a);
+ __b = crt_copysignf(crt_isinf(__b) ? 1 : 0, __b);
+ if (crt_isnan(__c))
+ __c = crt_copysignf(0, __c);
+ if (crt_isnan(__d))
+ __d = crt_copysignf(0, __d);
__recalc = 1;
}
- if (isinf(__c) || isinf(__d))
+ if (crt_isinf(__c) || crt_isinf(__d))
{
- __c = copysignf(isinf(__c) ? 1 : 0, __c);
- __d = copysignf(isinf(__d) ? 1 : 0, __d);
- if (isnan(__a))
- __a = copysignf(0, __a);
- if (isnan(__b))
- __b = copysignf(0, __b);
+ __c = crt_copysignf(crt_isinf(__c) ? 1 : 0, __c);
+ __d = crt_copysignf(crt_isinf(__d) ? 1 : 0, __d);
+ if (crt_isnan(__a))
+ __a = crt_copysignf(0, __a);
+ if (crt_isnan(__b))
+ __b = crt_copysignf(0, __b);
__recalc = 1;
}
- if (!__recalc && (isinf(__ac) || isinf(__bd) ||
- isinf(__ad) || isinf(__bc)))
+ if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) ||
+ crt_isinf(__ad) || crt_isinf(__bc)))
{
- if (isnan(__a))
- __a = copysignf(0, __a);
- if (isnan(__b))
- __b = copysignf(0, __b);
- if (isnan(__c))
- __c = copysignf(0, __c);
- if (isnan(__d))
- __d = copysignf(0, __d);
+ if (crt_isnan(__a))
+ __a = crt_copysignf(0, __a);
+ if (crt_isnan(__b))
+ __b = crt_copysignf(0, __b);
+ if (crt_isnan(__c))
+ __c = crt_copysignf(0, __c);
+ if (crt_isnan(__d))
+ __d = crt_copysignf(0, __d);
__recalc = 1;
}
if (__recalc)
{
- __real__ z = INFINITY * (__a * __c - __b * __d);
- __imag__ z = INFINITY * (__a * __d + __b * __c);
+ __real__ z = CRT_INFINITY * (__a * __c - __b * __d);
+ __imag__ z = CRT_INFINITY * (__a * __d + __b * __c);
}
}
return z;
diff --git a/lib/mulsf3.c b/lib/mulsf3.c
index bf46e14..fce2fd4 100644
--- a/lib/mulsf3.c
+++ b/lib/mulsf3.c
@@ -11,7 +11,6 @@
// with the IEEE-754 default rounding (to nearest, ties to even).
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/mulvdi3.c b/lib/mulvdi3.c
index fcbb5b3..bcc8e65 100644
--- a/lib/mulvdi3.c
+++ b/lib/mulvdi3.c
@@ -13,7 +13,6 @@
*/
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a * b */
diff --git a/lib/mulvsi3.c b/lib/mulvsi3.c
index 6271cd4..d372b201 100644
--- a/lib/mulvsi3.c
+++ b/lib/mulvsi3.c
@@ -13,7 +13,6 @@
*/
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a * b */
diff --git a/lib/mulvti3.c b/lib/mulvti3.c
index 7da9187..ae65cf8 100644
--- a/lib/mulvti3.c
+++ b/lib/mulvti3.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a * b */
diff --git a/lib/mulxc3.c b/lib/mulxc3.c
index b5ae865..cec0573 100644
--- a/lib/mulxc3.c
+++ b/lib/mulxc3.c
@@ -15,8 +15,7 @@
#if !_ARCH_PPC
#include "int_lib.h"
-#include <math.h>
-#include <complex.h>
+#include "int_math.h"
/* Returns: the product of a + ib and c + id */
@@ -30,46 +29,46 @@ __mulxc3(long double __a, long double __b, long double __c, long double __d)
long double _Complex z;
__real__ z = __ac - __bd;
__imag__ z = __ad + __bc;
- if (isnan(__real__ z) && isnan(__imag__ z))
+ if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
{
int __recalc = 0;
- if (isinf(__a) || isinf(__b))
+ if (crt_isinf(__a) || crt_isinf(__b))
{
- __a = copysignl(isinf(__a) ? 1 : 0, __a);
- __b = copysignl(isinf(__b) ? 1 : 0, __b);
- if (isnan(__c))
- __c = copysignl(0, __c);
- if (isnan(__d))
- __d = copysignl(0, __d);
+ __a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a);
+ __b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b);
+ if (crt_isnan(__c))
+ __c = crt_copysignl(0, __c);
+ if (crt_isnan(__d))
+ __d = crt_copysignl(0, __d);
__recalc = 1;
}
- if (isinf(__c) || isinf(__d))
+ if (crt_isinf(__c) || crt_isinf(__d))
{
- __c = copysignl(isinf(__c) ? 1 : 0, __c);
- __d = copysignl(isinf(__d) ? 1 : 0, __d);
- if (isnan(__a))
- __a = copysignl(0, __a);
- if (isnan(__b))
- __b = copysignl(0, __b);
+ __c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c);
+ __d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d);
+ if (crt_isnan(__a))
+ __a = crt_copysignl(0, __a);
+ if (crt_isnan(__b))
+ __b = crt_copysignl(0, __b);
__recalc = 1;
}
- if (!__recalc && (isinf(__ac) || isinf(__bd) ||
- isinf(__ad) || isinf(__bc)))
+ if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) ||
+ crt_isinf(__ad) || crt_isinf(__bc)))
{
- if (isnan(__a))
- __a = copysignl(0, __a);
- if (isnan(__b))
- __b = copysignl(0, __b);
- if (isnan(__c))
- __c = copysignl(0, __c);
- if (isnan(__d))
- __d = copysignl(0, __d);
+ if (crt_isnan(__a))
+ __a = crt_copysignl(0, __a);
+ if (crt_isnan(__b))
+ __b = crt_copysignl(0, __b);
+ if (crt_isnan(__c))
+ __c = crt_copysignl(0, __c);
+ if (crt_isnan(__d))
+ __d = crt_copysignl(0, __d);
__recalc = 1;
}
if (__recalc)
{
- __real__ z = INFINITY * (__a * __c - __b * __d);
- __imag__ z = INFINITY * (__a * __d + __b * __c);
+ __real__ z = CRT_INFINITY * (__a * __c - __b * __d);
+ __imag__ z = CRT_INFINITY * (__a * __d + __b * __c);
}
}
return z;
diff --git a/lib/negdf2.c b/lib/negdf2.c
index b47f397..b11b480 100644
--- a/lib/negdf2.c
+++ b/lib/negdf2.c
@@ -10,7 +10,6 @@
// This file implements double-precision soft-float negation.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/negsf2.c b/lib/negsf2.c
index 98f9fc0..f8ef2d1 100644
--- a/lib/negsf2.c
+++ b/lib/negsf2.c
@@ -10,7 +10,6 @@
// This file implements single-precision soft-float negation.
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/negvdi2.c b/lib/negvdi2.c
index aafaa9d..e336ecf 100644
--- a/lib/negvdi2.c
+++ b/lib/negvdi2.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: -a */
diff --git a/lib/negvsi2.c b/lib/negvsi2.c
index 559ea18..b9e93fe 100644
--- a/lib/negvsi2.c
+++ b/lib/negvsi2.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: -a */
diff --git a/lib/negvti2.c b/lib/negvti2.c
index d931305..ef766bb 100644
--- a/lib/negvti2.c
+++ b/lib/negvti2.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: -a */
diff --git a/lib/paritydi2.c b/lib/paritydi2.c
index e7bebf6..2ded54c 100644
--- a/lib/paritydi2.c
+++ b/lib/paritydi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/paritysi2.c b/lib/paritysi2.c
index 64d509f..5999846 100644
--- a/lib/paritysi2.c
+++ b/lib/paritysi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/popcountdi2.c b/lib/popcountdi2.c
index 136fc04..5e8a62f 100644
--- a/lib/popcountdi2.c
+++ b/lib/popcountdi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/popcountsi2.c b/lib/popcountsi2.c
index bfaa3ff..44544ff 100644
--- a/lib/popcountsi2.c
+++ b/lib/popcountsi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/powidf2.c b/lib/powidf2.c
index 2e211eb..ac13b17 100644
--- a/lib/powidf2.c
+++ b/lib/powidf2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/powisf2.c b/lib/powisf2.c
index e6b43b3..0c400ec 100644
--- a/lib/powisf2.c
+++ b/lib/powisf2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/ppc/DD.h b/lib/ppc/DD.h
index 9ecd1f5..13862dc 100644
--- a/lib/ppc/DD.h
+++ b/lib/ppc/DD.h
@@ -1,7 +1,7 @@
#ifndef __DD_HEADER
#define __DD_HEADER
-#include <stdint.h>
+#include "../int_lib.h"
typedef union {
long double ld;
diff --git a/lib/ppc/Makefile.mk b/lib/ppc/Makefile.mk
index 519d654..b78d386 100644
--- a/lib/ppc/Makefile.mk
+++ b/lib/ppc/Makefile.mk
@@ -7,6 +7,7 @@
#
#===------------------------------------------------------------------------===#
+ModuleName := builtins
SubDirs :=
OnlyArchs := ppc
diff --git a/lib/ppc/divtc3.c b/lib/ppc/divtc3.c
index d41f621..2991281 100644
--- a/lib/ppc/divtc3.c
+++ b/lib/ppc/divtc3.c
@@ -3,16 +3,16 @@
*/
#include "DD.h"
-#include <math.h>
+#include "../int_math.h"
-#if !defined(INFINITY) && defined(HUGE_VAL)
-#define INFINITY HUGE_VAL
-#endif /* INFINITY */
+#if !defined(CRT_INFINITY) && defined(HUGE_VAL)
+#define CRT_INFINITY HUGE_VAL
+#endif /* CRT_INFINITY */
-#define makeFinite(x) { \
- (x).s.hi = __builtin_copysign(isinf((x).s.hi) ? 1.0 : 0.0, (x).s.hi); \
- (x).s.lo = 0.0; \
- }
+#define makeFinite(x) { \
+ (x).s.hi = crt_copysign(crt_isinf((x).s.hi) ? 1.0 : 0.0, (x).s.hi); \
+ (x).s.lo = 0.0; \
+ }
long double __gcc_qadd(long double, long double);
long double __gcc_qsub(long double, long double);
@@ -26,16 +26,16 @@ __divtc3(long double a, long double b, long double c, long double d)
DD dDD = { .ld = d };
int ilogbw = 0;
- const double logbw = logb(__builtin_fmax( __builtin_fabs(cDD.s.hi), __builtin_fabs(dDD.s.hi) ));
+ const double logbw = crt_logb(crt_fmax(crt_fabs(cDD.s.hi), crt_fabs(dDD.s.hi) ));
- if (isfinite(logbw))
+ if (crt_isfinite(logbw))
{
ilogbw = (int)logbw;
- cDD.s.hi = scalbn(cDD.s.hi, -ilogbw);
- cDD.s.lo = scalbn(cDD.s.lo, -ilogbw);
- dDD.s.hi = scalbn(dDD.s.hi, -ilogbw);
- dDD.s.lo = scalbn(dDD.s.lo, -ilogbw);
+ cDD.s.hi = crt_scalbn(cDD.s.hi, -ilogbw);
+ cDD.s.lo = crt_scalbn(cDD.s.lo, -ilogbw);
+ dDD.s.hi = crt_scalbn(dDD.s.hi, -ilogbw);
+ dDD.s.lo = crt_scalbn(dDD.s.lo, -ilogbw);
}
const long double denom = __gcc_qadd(__gcc_qmul(cDD.ld, cDD.ld), __gcc_qmul(dDD.ld, dDD.ld));
@@ -45,42 +45,45 @@ __divtc3(long double a, long double b, long double c, long double d)
DD real = { .ld = __gcc_qdiv(realNumerator, denom) };
DD imag = { .ld = __gcc_qdiv(imagNumerator, denom) };
- real.s.hi = scalbn(real.s.hi, -ilogbw);
- real.s.lo = scalbn(real.s.lo, -ilogbw);
- imag.s.hi = scalbn(imag.s.hi, -ilogbw);
- imag.s.lo = scalbn(imag.s.lo, -ilogbw);
+ real.s.hi = crt_scalbn(real.s.hi, -ilogbw);
+ real.s.lo = crt_scalbn(real.s.lo, -ilogbw);
+ imag.s.hi = crt_scalbn(imag.s.hi, -ilogbw);
+ imag.s.lo = crt_scalbn(imag.s.lo, -ilogbw);
- if (isnan(real.s.hi) && isnan(imag.s.hi))
+ if (crt_isnan(real.s.hi) && crt_isnan(imag.s.hi))
{
DD aDD = { .ld = a };
DD bDD = { .ld = b };
DD rDD = { .ld = denom };
- if ((rDD.s.hi == 0.0) && (!isnan(aDD.s.hi) || !isnan(bDD.s.hi)))
+ if ((rDD.s.hi == 0.0) && (!crt_isnan(aDD.s.hi) ||
+ !crt_isnan(bDD.s.hi)))
{
- real.s.hi = __builtin_copysign(INFINITY,cDD.s.hi) * aDD.s.hi;
+ real.s.hi = crt_copysign(CRT_INFINITY,cDD.s.hi) * aDD.s.hi;
real.s.lo = 0.0;
- imag.s.hi = __builtin_copysign(INFINITY,cDD.s.hi) * bDD.s.hi;
+ imag.s.hi = crt_copysign(CRT_INFINITY,cDD.s.hi) * bDD.s.hi;
imag.s.lo = 0.0;
}
- else if ((isinf(aDD.s.hi) || isinf(bDD.s.hi)) && isfinite(cDD.s.hi) && isfinite(dDD.s.hi))
+ else if ((crt_isinf(aDD.s.hi) || crt_isinf(bDD.s.hi)) &&
+ crt_isfinite(cDD.s.hi) && crt_isfinite(dDD.s.hi))
{
makeFinite(aDD);
makeFinite(bDD);
- real.s.hi = INFINITY * (aDD.s.hi*cDD.s.hi + bDD.s.hi*dDD.s.hi);
+ real.s.hi = CRT_INFINITY * (aDD.s.hi*cDD.s.hi + bDD.s.hi*dDD.s.hi);
real.s.lo = 0.0;
- imag.s.hi = INFINITY * (bDD.s.hi*cDD.s.hi - aDD.s.hi*dDD.s.hi);
+ imag.s.hi = CRT_INFINITY * (bDD.s.hi*cDD.s.hi - aDD.s.hi*dDD.s.hi);
imag.s.lo = 0.0;
}
- else if ((isinf(cDD.s.hi) || isinf(dDD.s.hi)) && isfinite(aDD.s.hi) && isfinite(bDD.s.hi))
+ else if ((crt_isinf(cDD.s.hi) || crt_isinf(dDD.s.hi)) &&
+ crt_isfinite(aDD.s.hi) && crt_isfinite(bDD.s.hi))
{
makeFinite(cDD);
makeFinite(dDD);
- real.s.hi = __builtin_copysign(0.0,(aDD.s.hi*cDD.s.hi + bDD.s.hi*dDD.s.hi));
+ real.s.hi = crt_copysign(0.0,(aDD.s.hi*cDD.s.hi + bDD.s.hi*dDD.s.hi));
real.s.lo = 0.0;
- imag.s.hi = __builtin_copysign(0.0,(bDD.s.hi*cDD.s.hi - aDD.s.hi*dDD.s.hi));
+ imag.s.hi = crt_copysign(0.0,(bDD.s.hi*cDD.s.hi - aDD.s.hi*dDD.s.hi));
imag.s.lo = 0.0;
}
}
diff --git a/lib/ppc/fixtfdi.c b/lib/ppc/fixtfdi.c
index fa113a0..56e7b3f 100644
--- a/lib/ppc/fixtfdi.c
+++ b/lib/ppc/fixtfdi.c
@@ -7,7 +7,7 @@
*/
#include "DD.h"
-#include <stdint.h>
+#include "../int_math.h"
uint64_t __fixtfdi(long double input)
{
@@ -65,7 +65,7 @@ uint64_t __fixtfdi(long double input)
/* Edge cases handled here: */
/* |x| < 1, result is zero. */
- if (1.0 > __builtin_fabs(x.s.hi))
+ if (1.0 > crt_fabs(x.s.hi))
return INT64_C(0);
/* x very close to INT64_MIN, care must be taken to see which side we are on. */
diff --git a/lib/ppc/fixunstfdi.c b/lib/ppc/fixunstfdi.c
index 1fb5248..5e6e2ce 100644
--- a/lib/ppc/fixunstfdi.c
+++ b/lib/ppc/fixunstfdi.c
@@ -6,7 +6,6 @@
/* This file implements the PowerPC 128-bit double-double -> uint64_t conversion */
#include "DD.h"
-#include <stdint.h>
uint64_t __fixunstfdi(long double input)
{
diff --git a/lib/ppc/floatditf.c b/lib/ppc/floatditf.c
index ed23dc8..beabdd0 100644
--- a/lib/ppc/floatditf.c
+++ b/lib/ppc/floatditf.c
@@ -6,7 +6,6 @@
/* This file implements the PowerPC long long -> long double conversion */
#include "DD.h"
-#include <stdint.h>
long double __floatditf(int64_t a) {
diff --git a/lib/ppc/floatunditf.c b/lib/ppc/floatunditf.c
index 20a3b71..b12e1e7 100644
--- a/lib/ppc/floatunditf.c
+++ b/lib/ppc/floatunditf.c
@@ -6,7 +6,6 @@
/* This file implements the PowerPC unsigned long long -> long double conversion */
#include "DD.h"
-#include <stdint.h>
long double __floatunditf(uint64_t a) {
diff --git a/lib/ppc/multc3.c b/lib/ppc/multc3.c
index 9d17a2c..738b65a 100644
--- a/lib/ppc/multc3.c
+++ b/lib/ppc/multc3.c
@@ -3,23 +3,19 @@
*/
#include "DD.h"
-#include <math.h>
+#include "../int_math.h"
-#if !defined(INFINITY) && defined(HUGE_VAL)
-#define INFINITY HUGE_VAL
-#endif /* INFINITY */
+#define makeFinite(x) { \
+ (x).s.hi = crt_copysign(crt_isinf((x).s.hi) ? 1.0 : 0.0, (x).s.hi); \
+ (x).s.lo = 0.0; \
+ }
-#define makeFinite(x) { \
- (x).s.hi = __builtin_copysign(isinf((x).s.hi) ? 1.0 : 0.0, (x).s.hi); \
- (x).s.lo = 0.0; \
- }
-
-#define zeroNaN(x) { \
- if (isnan((x).s.hi)) { \
- (x).s.hi = __builtin_copysign(0.0, (x).s.hi); \
- (x).s.lo = 0.0; \
- } \
- }
+#define zeroNaN(x) { \
+ if (crt_isnan((x).s.hi)) { \
+ (x).s.hi = crt_copysign(0.0, (x).s.hi); \
+ (x).s.lo = 0.0; \
+ } \
+ }
long double __gcc_qadd(long double, long double);
long double __gcc_qsub(long double, long double);
@@ -36,7 +32,7 @@ __multc3(long double a, long double b, long double c, long double d)
DD real = { .ld = __gcc_qsub(ac,bd) };
DD imag = { .ld = __gcc_qadd(ad,bc) };
- if (isnan(real.s.hi) && isnan(imag.s.hi))
+ if (crt_isnan(real.s.hi) && crt_isnan(imag.s.hi))
{
int recalc = 0;
@@ -45,7 +41,7 @@ __multc3(long double a, long double b, long double c, long double d)
DD cDD = { .ld = c };
DD dDD = { .ld = d };
- if (isinf(aDD.s.hi) || isinf(bDD.s.hi))
+ if (crt_isinf(aDD.s.hi) || crt_isinf(bDD.s.hi))
{
makeFinite(aDD);
makeFinite(bDD);
@@ -54,7 +50,7 @@ __multc3(long double a, long double b, long double c, long double d)
recalc = 1;
}
- if (isinf(cDD.s.hi) || isinf(dDD.s.hi))
+ if (crt_isinf(cDD.s.hi) || crt_isinf(dDD.s.hi))
{
makeFinite(cDD);
makeFinite(dDD);
@@ -70,7 +66,8 @@ __multc3(long double a, long double b, long double c, long double d)
DD adDD = { .ld = ad };
DD bcDD = { .ld = bc };
- if (isinf(acDD.s.hi) || isinf(bdDD.s.hi) || isinf(adDD.s.hi) || isinf(bcDD.s.hi))
+ if (crt_isinf(acDD.s.hi) || crt_isinf(bdDD.s.hi) ||
+ crt_isinf(adDD.s.hi) || crt_isinf(bcDD.s.hi))
{
zeroNaN(aDD);
zeroNaN(bDD);
@@ -82,9 +79,9 @@ __multc3(long double a, long double b, long double c, long double d)
if (recalc)
{
- real.s.hi = INFINITY * (aDD.s.hi*cDD.s.hi - bDD.s.hi*dDD.s.hi);
+ real.s.hi = CRT_INFINITY * (aDD.s.hi*cDD.s.hi - bDD.s.hi*dDD.s.hi);
real.s.lo = 0.0;
- imag.s.hi = INFINITY * (aDD.s.hi*dDD.s.hi + bDD.s.hi*cDD.s.hi);
+ imag.s.hi = CRT_INFINITY * (aDD.s.hi*dDD.s.hi + bDD.s.hi*cDD.s.hi);
imag.s.lo = 0.0;
}
}
diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
new file mode 100644
index 0000000..fd506e9
--- /dev/null
+++ b/lib/profile/GCDAProfiling.c
@@ -0,0 +1,200 @@
+/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\
+|*
+|* The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+|*===----------------------------------------------------------------------===*|
+|*
+|* This file implements the call back routines for the gcov profiling
+|* instrumentation pass. Link against this library when running code through
+|* the -insert-gcov-profiling LLVM pass.
+|*
+|* We emit files in a corrupt version of GCOV's "gcda" file format. These files
+|* are only close enough that LCOV will happily parse them. Anything that lcov
+|* ignores is missing.
+|*
+|* TODO: gcov is multi-process safe by having each exit open the existing file
+|* and append to it. We'd like to achieve that and be thread-safe too.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include <direct.h>
+#endif
+
+#ifndef _MSC_VER
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned int uint64_t;
+#endif
+
+/* #define DEBUG_GCDAPROFILING */
+
+/*
+ * --- GCOV file format I/O primitives ---
+ */
+
+static FILE *output_file = NULL;
+
+static void write_int32(uint32_t i) {
+ fwrite(&i, 4, 1, output_file);
+}
+
+static void write_int64(uint64_t i) {
+ uint32_t lo, hi;
+ lo = i >> 0;
+ hi = i >> 32;
+
+ write_int32(lo);
+ write_int32(hi);
+}
+
+static uint32_t length_of_string(const char *s) {
+ return (strlen(s) / 4) + 1;
+}
+
+static void write_string(const char *s) {
+ uint32_t len = length_of_string(s);
+ write_int32(len);
+ fwrite(s, strlen(s), 1, output_file);
+ fwrite("\0\0\0\0", 4 - (strlen(s) % 4), 1, output_file);
+}
+
+static char *mangle_filename(const char *orig_filename) {
+ /* TODO: handle GCOV_PREFIX_STRIP */
+ const char *prefix;
+ char *filename = 0;
+
+ prefix = getenv("GCOV_PREFIX");
+
+ if (!prefix)
+ return strdup(orig_filename);
+
+ filename = malloc(strlen(prefix) + 1 + strlen(orig_filename) + 1);
+ strcpy(filename, prefix);
+ strcat(filename, "/");
+ strcat(filename, orig_filename);
+
+ return filename;
+}
+
+static void recursive_mkdir(const char *filename) {
+ char *pathname;
+ int i, e;
+
+ for (i = 1, e = strlen(filename); i != e; ++i) {
+ if (filename[i] == '/') {
+ pathname = malloc(i + 1);
+ strncpy(pathname, filename, i);
+ pathname[i] = '\0';
+#ifdef _WIN32
+ _mkdir(pathname);
+#else
+ mkdir(pathname, 0750); /* some of these will fail, ignore it. */
+#endif
+ free(pathname);
+ }
+ }
+}
+
+/*
+ * --- LLVM line counter API ---
+ */
+
+/* A file in this case is a translation unit. Each .o file built with line
+ * profiling enabled will emit to a different file. Only one file may be
+ * started at a time.
+ */
+void llvm_gcda_start_file(const char *orig_filename) {
+ char *filename;
+ filename = mangle_filename(orig_filename);
+ recursive_mkdir(filename);
+ output_file = fopen(filename, "wb");
+
+ /* gcda file, version 404*, stamp LLVM. */
+#ifdef __APPLE__
+ fwrite("adcg*204MVLL", 12, 1, output_file);
+#else
+ fwrite("adcg*404MVLL", 12, 1, output_file);
+#endif
+
+#ifdef DEBUG_GCDAPROFILING
+ printf("llvmgcda: [%s]\n", orig_filename);
+#endif
+
+ free(filename);
+}
+
+/* Given an array of pointers to counters (counters), increment the n-th one,
+ * where we're also given a pointer to n (predecessor).
+ */
+void llvm_gcda_increment_indirect_counter(uint32_t *predecessor,
+ uint64_t **counters) {
+ uint64_t *counter;
+ uint32_t pred;
+
+ pred = *predecessor;
+ if (pred == 0xffffffff)
+ return;
+ counter = counters[pred];
+
+ /* Don't crash if the pred# is out of sync. This can happen due to threads,
+ or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */
+ if (counter)
+ ++*counter;
+#ifdef DEBUG_GCDAPROFILING
+ else
+ printf("llvmgcda: increment_indirect_counter counters=%x, pred=%u\n",
+ state_table_row, *predecessor);
+#endif
+}
+
+void llvm_gcda_emit_function(uint32_t ident, const char *function_name) {
+#ifdef DEBUG_GCDAPROFILING
+ printf("llvmgcda: function id=%x\n", ident);
+#endif
+
+ /* function tag */
+ fwrite("\0\0\0\1", 4, 1, output_file);
+ write_int32(3 + 1 + length_of_string(function_name));
+ write_int32(ident);
+ write_int32(0);
+ write_int32(0);
+ write_string(function_name);
+}
+
+void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) {
+ uint32_t i;
+ /* counter #1 (arcs) tag */
+ fwrite("\0\0\xa1\1", 4, 1, output_file);
+ write_int32(num_counters * 2);
+ for (i = 0; i < num_counters; ++i) {
+ write_int64(counters[i]);
+ }
+
+#ifdef DEBUG_GCDAPROFILING
+ printf("llvmgcda: %u arcs\n", num_counters);
+ for (i = 0; i < num_counters; ++i) {
+ printf("llvmgcda: %llu\n", (unsigned long long)counters[i]);
+ }
+#endif
+}
+
+void llvm_gcda_end_file() {
+ /* Write out EOF record. */
+ fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file);
+ fclose(output_file);
+ output_file = NULL;
+
+#ifdef DEBUG_GCDAPROFILING
+ printf("llvmgcda: -----\n");
+#endif
+}
diff --git a/lib/profile/Makefile.mk b/lib/profile/Makefile.mk
new file mode 100644
index 0000000..7689c9a
--- /dev/null
+++ b/lib/profile/Makefile.mk
@@ -0,0 +1,18 @@
+#===- lib/profile/Makefile.mk ------------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := profile
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
+ObjNames := $(Sources:%.c=%.o)
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
diff --git a/lib/subdf3.c b/lib/subdf3.c
index 825e3c6..5eb1853 100644
--- a/lib/subdf3.c
+++ b/lib/subdf3.c
@@ -11,7 +11,6 @@
// IEEE-754 default rounding (to nearest, ties to even).
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/subsf3.c b/lib/subsf3.c
index 625376a..351be0ef 100644
--- a/lib/subsf3.c
+++ b/lib/subsf3.c
@@ -11,7 +11,6 @@
// IEEE-754 default rounding (to nearest, ties to even).
//
//===----------------------------------------------------------------------===//
-#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"
diff --git a/lib/subvdi3.c b/lib/subvdi3.c
index 36b51ad..0f1f924 100644
--- a/lib/subvdi3.c
+++ b/lib/subvdi3.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a - b */
diff --git a/lib/subvsi3.c b/lib/subvsi3.c
index 03983f7..ec4594c 100644
--- a/lib/subvsi3.c
+++ b/lib/subvsi3.c
@@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a - b */
diff --git a/lib/subvti3.c b/lib/subvti3.c
index 5d693dc..44127b7 100644
--- a/lib/subvti3.c
+++ b/lib/subvti3.c
@@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
-#include <stdlib.h>
/* Returns: a - b */
diff --git a/lib/trampoline_setup.c b/lib/trampoline_setup.c
index ced119f..1b13976 100644
--- a/lib/trampoline_setup.c
+++ b/lib/trampoline_setup.c
@@ -8,9 +8,6 @@
* ===----------------------------------------------------------------------===
*/
-#include <stdint.h>
-#include <stdlib.h>
-
#include "int_lib.h"
extern void __clear_cache(void* start, void* end);
diff --git a/lib/truncdfsf2.c b/lib/truncdfsf2.c
index 1dbf02f..f57af7e 100644
--- a/lib/truncdfsf2.c
+++ b/lib/truncdfsf2.c
@@ -37,11 +37,7 @@
//
//===----------------------------------------------------------------------===//
-#include <stdint.h>
-#include <limits.h>
-#include <stdbool.h>
-
-#include "abi.h"
+#include "int_lib.h"
typedef double src_t;
typedef uint64_t src_rep_t;
diff --git a/lib/ucmpdi2.c b/lib/ucmpdi2.c
index f2d3f99..3242bbf 100644
--- a/lib/ucmpdi2.c
+++ b/lib/ucmpdi2.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/udivdi3.c b/lib/udivdi3.c
index bbd551a..6c0303d 100644
--- a/lib/udivdi3.c
+++ b/lib/udivdi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/udivmoddi4.c b/lib/udivmoddi4.c
index c5db21c..73043d4 100644
--- a/lib/udivmoddi4.c
+++ b/lib/udivmoddi4.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
@@ -133,7 +132,7 @@ __udivmoddi4(du_int a, du_int b, du_int* rem)
*rem = n.s.low & (d.s.low - 1);
if (d.s.low == 1)
return n.all;
- unsigned sr = __builtin_ctz(d.s.low);
+ sr = __builtin_ctz(d.s.low);
q.s.high = n.s.high >> sr;
q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
return q.all;
diff --git a/lib/udivmodsi4.c b/lib/udivmodsi4.c
index 2a3ee27..5b49089 100644
--- a/lib/udivmodsi4.c
+++ b/lib/udivmodsi4.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/udivmodti4.c b/lib/udivmodti4.c
index d1e19ed..427861b 100644
--- a/lib/udivmodti4.c
+++ b/lib/udivmodti4.c
@@ -132,7 +132,7 @@ __udivmodti4(tu_int a, tu_int b, tu_int* rem)
*rem = n.s.low & (d.s.low - 1);
if (d.s.low == 1)
return n.all;
- unsigned sr = __builtin_ctzll(d.s.low);
+ sr = __builtin_ctzll(d.s.low);
q.s.high = n.s.high >> sr;
q.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr);
return q.all;
diff --git a/lib/udivsi3.c b/lib/udivsi3.c
index 721ae89..39ef48b 100644
--- a/lib/udivsi3.c
+++ b/lib/udivsi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/umoddi3.c b/lib/umoddi3.c
index 9de1a64..3541ab6 100644
--- a/lib/umoddi3.c
+++ b/lib/umoddi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/umodsi3.c b/lib/umodsi3.c
index 569b7fc..aae741d 100644
--- a/lib/umodsi3.c
+++ b/lib/umodsi3.c
@@ -11,7 +11,6 @@
*
* ===----------------------------------------------------------------------===
*/
-#include "abi.h"
#include "int_lib.h"
diff --git a/lib/x86_64/Makefile.mk b/lib/x86_64/Makefile.mk
index 09037b9..ee3f9ce 100644
--- a/lib/x86_64/Makefile.mk
+++ b/lib/x86_64/Makefile.mk
@@ -7,6 +7,7 @@
#
#===------------------------------------------------------------------------===#
+ModuleName := builtins
SubDirs :=
OnlyArchs := x86_64
diff --git a/lib/x86_64/floatdidf.c b/lib/x86_64/floatdidf.c
index cce3cd5..388404e 100644
--- a/lib/x86_64/floatdidf.c
+++ b/lib/x86_64/floatdidf.c
@@ -6,7 +6,7 @@
#ifdef __x86_64__
-#include <stdint.h>
+#include "../int_lib.h"
double __floatdidf(int64_t a)
{
diff --git a/lib/x86_64/floatdisf.c b/lib/x86_64/floatdisf.c
index 753ba90..96c3728 100644
--- a/lib/x86_64/floatdisf.c
+++ b/lib/x86_64/floatdisf.c
@@ -4,7 +4,7 @@
#ifdef __x86_64__
-#include <stdint.h>
+#include "../int_lib.h"
float __floatdisf(int64_t a)
{
diff --git a/lib/x86_64/floatdixf.c b/lib/x86_64/floatdixf.c
index 569f727..c01193a 100644
--- a/lib/x86_64/floatdixf.c
+++ b/lib/x86_64/floatdixf.c
@@ -6,7 +6,7 @@
#ifdef __x86_64__
-#include <stdint.h>
+#include "../int_lib.h"
long double __floatdixf(int64_t a)
{
OpenPOWER on IntegriCloud