diff options
author | ed <ed@FreeBSD.org> | 2011-12-31 14:55:23 +0000 |
---|---|---|
committer | ed <ed@FreeBSD.org> | 2011-12-31 14:55:23 +0000 |
commit | 6a5f9bc9ae3205be8bb5570b86a509fcd01a9db6 (patch) | |
tree | d59ced7e19afaab87432b7610faefff3080e8d2a | |
parent | ee2dbb0f7e84d908ac62aecc178992c9be1ca636 (diff) | |
download | FreeBSD-src-6a5f9bc9ae3205be8bb5570b86a509fcd01a9db6.zip FreeBSD-src-6a5f9bc9ae3205be8bb5570b86a509fcd01a9db6.tar.gz |
Import compiler-rt r147390.
267 files changed, 14520 insertions, 675 deletions
@@ -1,4 +1,4 @@ *~ -Debug -Release -Profile +darwin_fat +clang_darwin +multi_arch diff --git a/LICENSE.TXT b/LICENSE.TXT index 2552e8c..488baf5 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -74,3 +74,25 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +============================================================================== +Copyrights and Licenses for Third Party Software Distributed with LLVM: +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- +sysinfo lib/asan/sysinfo +mach_override lib/asan/mach_override @@ -164,6 +164,7 @@ define PerPlatformConfigArch_template $(call Set,Tmp.Arch,$(1)) $(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch)) $(call Set,Tmp.Functions,$(strip \ + $(AlwaysRequiredModules) \ $(call GetCNAVar,FUNCTIONS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) $(call Set,Tmp.Optimized,$(strip \ $(call GetCNAVar,OPTIMIZED,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) @@ -226,7 +227,10 @@ $(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.S $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$< $(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.c $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir $(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" - $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$< + $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c $(COMMON_CFLAGS) -o $$@ $$< +$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.cc $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir + $(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" + $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c $(COMMON_CXXFLAGS) -o $$@ $$< .PRECIOUS: $(Tmp.ObjPath)/.dir endef @@ -106,6 +106,15 @@ si_int __mulvsi3(si_int a, si_int b); // a * b di_int __mulvdi3(di_int a, di_int b); // a * b ti_int __mulvti3(ti_int a, ti_int b); // a * b + +// Integral arithmetic which returns if overflow + +si_int __mulosi4(si_int a, si_int b, int* overflow); // a * b, overflow set to one if result not in signed range +di_int __mulodi4(di_int a, di_int b, int* overflow); // a * b, overflow set to one if result not in signed range +ti_int __muloti4(ti_int a, ti_int b, int* overflow); // a * b, overflow set to + one if result not in signed range + + // Integral comparison: a < b -> 0 // a == b -> 1 // a > b -> 2 diff --git a/SDKs/README.txt b/SDKs/README.txt new file mode 100644 index 0000000..b95575e --- /dev/null +++ b/SDKs/README.txt @@ -0,0 +1,9 @@ +It is often convenient to be able to build compiler-rt libraries for a certain +platform without having a full SDK or development environment installed. + +This makes it easy for users to build a compiler which can target a number of +different platforms, without having to actively maintain full development +environments for those platforms. + +Since compiler-rt's libraries typically have minimal interaction with the +system, we achieve this by stubbing out the SDKs of certain platforms. diff --git a/SDKs/darwin/README.txt b/SDKs/darwin/README.txt new file mode 100644 index 0000000..ea30af3 --- /dev/null +++ b/SDKs/darwin/README.txt @@ -0,0 +1,3 @@ +The Darwin platforms are all similar enough we roll them into one SDK, and use +preprocessor tricks to get the right definitions for the few things which +diverge between OS X and iOS. diff --git a/SDKs/darwin/usr/include/limits.h b/SDKs/darwin/usr/include/limits.h new file mode 100644 index 0000000..5495a78 --- /dev/null +++ b/SDKs/darwin/usr/include/limits.h @@ -0,0 +1,23 @@ +/* ===-- limits.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __LIMITS_H__ +#define __LIMITS_H__ + +/* This is only here as a landing pad for the include_next from the compiler's + built-in limits.h. */ + +#endif /* __LIMITS_H__ */ diff --git a/SDKs/darwin/usr/include/stdio.h b/SDKs/darwin/usr/include/stdio.h new file mode 100644 index 0000000..3b56036 --- /dev/null +++ b/SDKs/darwin/usr/include/stdio.h @@ -0,0 +1,61 @@ +/* ===-- stdio.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDIO_H__ +#define __STDIO_H__ + +typedef struct __sFILE FILE; +typedef __SIZE_TYPE__ size_t; + +/* Determine the appropriate fopen() and fwrite() functions. */ +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if defined(__i386) +# define __FOPEN_NAME "_fopen$UNIX2003" +# define __FWRITE_NAME "_fwrite$UNIX2003" +# elif defined(__x86_64__) +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# elif defined(__arm) +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# else +# error "unrecognized architecture for targetting OS X" +# endif +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if defined(__i386) || defined (__x86_64) +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# elif defined(__arm) +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# else +# error "unrecognized architecture for targetting iOS" +# endif +#else +# error "unrecognized architecture for targetting Darwin" +#endif + +# define stderr __stderrp +extern FILE *__stderrp; + +int fclose(FILE *); +int fflush(FILE *); +FILE *fopen(const char * restrict, const char * restrict) __asm(__FOPEN_NAME); +int fprintf(FILE * restrict, const char * restrict, ...); +size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict) + __asm(__FWRITE_NAME); + +#endif /* __STDIO_H__ */ diff --git a/SDKs/darwin/usr/include/stdlib.h b/SDKs/darwin/usr/include/stdlib.h new file mode 100644 index 0000000..cf65df4 --- /dev/null +++ b/SDKs/darwin/usr/include/stdlib.h @@ -0,0 +1,29 @@ +/* ===-- stdlib.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + +#define NULL ((void *)0) + +typedef __SIZE_TYPE__ size_t; + +void abort(void) __attribute__((__noreturn__)); +void free(void *); +char *getenv(const char *); +void *malloc(size_t); + +#endif /* __STDLIB_H__ */ diff --git a/SDKs/darwin/usr/include/string.h b/SDKs/darwin/usr/include/string.h new file mode 100644 index 0000000..5e91109 --- /dev/null +++ b/SDKs/darwin/usr/include/string.h @@ -0,0 +1,28 @@ +/* ===-- string.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STRING_H__ +#define __STRING_H__ + +typedef __SIZE_TYPE__ size_t; + +char *strcat(char *, const char *); +char *strcpy(char *, const char *); +char *strdup(const char *); +size_t strlen(const char *); +char *strncpy(char *, const char *, size_t); + +#endif /* __STRING_H__ */ diff --git a/SDKs/darwin/usr/include/sys/stat.h b/SDKs/darwin/usr/include/sys/stat.h new file mode 100644 index 0000000..6225f90 --- /dev/null +++ b/SDKs/darwin/usr/include/sys/stat.h @@ -0,0 +1,25 @@ +/* ===-- stat.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_STAT_H__ +#define __SYS_STAT_H__ + +typedef unsigned short uint16_t; +typedef uint16_t mode_t; + +int mkdir(const char *, mode_t); + +#endif /* __SYS_STAT_H__ */ diff --git a/SDKs/darwin/usr/include/sys/types.h b/SDKs/darwin/usr/include/sys/types.h new file mode 100644 index 0000000..b425767 --- /dev/null +++ b/SDKs/darwin/usr/include/sys/types.h @@ -0,0 +1,20 @@ +/* ===-- types.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_TYPES_H__ +#define __SYS_TYPES_H__ + +#endif /* __SYS_TYPES_H__ */ diff --git a/SDKs/linux/README.txt b/SDKs/linux/README.txt new file mode 100644 index 0000000..aa0604a --- /dev/null +++ b/SDKs/linux/README.txt @@ -0,0 +1,2 @@ +This is a stub SDK for Linux. Currently, this has only been tested on i386 and +x86_64 using the Clang compiler. diff --git a/SDKs/linux/usr/include/endian.h b/SDKs/linux/usr/include/endian.h new file mode 100644 index 0000000..95528db --- /dev/null +++ b/SDKs/linux/usr/include/endian.h @@ -0,0 +1,29 @@ +/* ===-- endian.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __ENDIAN_H__ +#define __ENDIAN_H__ + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 + +#if defined(__LITTLE_ENDIAN__) || defined(__ORDER_LITTLE_ENDIAN__) +#define __BYTE_ORDER __LITTLE_ENDIAN +#else +#define __BYTE_ORDER __BIG_ENDIAN +#endif + +#endif /* __ENDIAN_H__ */ diff --git a/SDKs/linux/usr/include/limits.h b/SDKs/linux/usr/include/limits.h new file mode 100644 index 0000000..5495a78 --- /dev/null +++ b/SDKs/linux/usr/include/limits.h @@ -0,0 +1,23 @@ +/* ===-- limits.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __LIMITS_H__ +#define __LIMITS_H__ + +/* This is only here as a landing pad for the include_next from the compiler's + built-in limits.h. */ + +#endif /* __LIMITS_H__ */ diff --git a/SDKs/linux/usr/include/stdio.h b/SDKs/linux/usr/include/stdio.h new file mode 100644 index 0000000..ddfe755 --- /dev/null +++ b/SDKs/linux/usr/include/stdio.h @@ -0,0 +1,35 @@ +/* ===-- stdio.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDIO_H__ +#define __STDIO_H__ + +typedef __SIZE_TYPE__ size_t; + +struct _IO_FILE; +typedef struct _IO_FILE FILE; + +extern struct _IO_FILE *stdin; +extern struct _IO_FILE *stdout; +extern struct _IO_FILE *stderr; + +extern int fclose(FILE *); +extern int fflush(FILE *); +extern FILE *fopen(const char * restrict, const char * restrict); +extern int fprintf(FILE * restrict, const char * restrict, ...); +extern size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict); + +#endif /* __STDIO_H__ */ diff --git a/SDKs/linux/usr/include/stdlib.h b/SDKs/linux/usr/include/stdlib.h new file mode 100644 index 0000000..b3755df --- /dev/null +++ b/SDKs/linux/usr/include/stdlib.h @@ -0,0 +1,32 @@ +/* ===-- stdlib.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + +#define NULL ((void *)0) + +typedef __SIZE_TYPE__ size_t; + +void abort(void) __attribute__((__nothrow__)) __attribute__((__noreturn__)); +void free(void *) __attribute__((__nothrow__)); +char *getenv(const char *) __attribute__((__nothrow__)) + __attribute__((__nonnull__(1))); + __attribute__((__warn_unused_result__)); +void *malloc(size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) + __attribute__((__warn_unused_result__)); + +#endif /* __STDLIB_H__ */ diff --git a/SDKs/linux/usr/include/string.h b/SDKs/linux/usr/include/string.h new file mode 100644 index 0000000..5e91109 --- /dev/null +++ b/SDKs/linux/usr/include/string.h @@ -0,0 +1,28 @@ +/* ===-- string.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STRING_H__ +#define __STRING_H__ + +typedef __SIZE_TYPE__ size_t; + +char *strcat(char *, const char *); +char *strcpy(char *, const char *); +char *strdup(const char *); +size_t strlen(const char *); +char *strncpy(char *, const char *, size_t); + +#endif /* __STRING_H__ */ diff --git a/SDKs/linux/usr/include/sys/mman.h b/SDKs/linux/usr/include/sys/mman.h new file mode 100644 index 0000000..7c4d051 --- /dev/null +++ b/SDKs/linux/usr/include/sys/mman.h @@ -0,0 +1,29 @@ +/* ===-- limits.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_MMAN_H__ +#define __SYS_MMAN_H__ + +typedef __SIZE_TYPE__ size_t; + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 + +extern int mprotect (void *__addr, size_t __len, int __prot) + __attribute__((__nothrow__)); + +#endif /* __SYS_MMAN_H__ */ diff --git a/SDKs/linux/usr/include/sys/stat.h b/SDKs/linux/usr/include/sys/stat.h new file mode 100644 index 0000000..0449fdd --- /dev/null +++ b/SDKs/linux/usr/include/sys/stat.h @@ -0,0 +1,24 @@ +/* ===-- stat.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_STAT_H__ +#define __SYS_STAT_H__ + +typedef unsigned int mode_t; + +int mkdir(const char *, mode_t); + +#endif /* __SYS_STAT_H__ */ diff --git a/SDKs/linux/usr/include/sys/types.h b/SDKs/linux/usr/include/sys/types.h new file mode 100644 index 0000000..10e74bb --- /dev/null +++ b/SDKs/linux/usr/include/sys/types.h @@ -0,0 +1,20 @@ +/* ===-- stat.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_TYPES_H__ +#define __SYS_TYPES_H__ + +#endif /* __SYS_TYPES_H__ */ diff --git a/SDKs/linux/usr/include/unistd.h b/SDKs/linux/usr/include/unistd.h new file mode 100644 index 0000000..773b081 --- /dev/null +++ b/SDKs/linux/usr/include/unistd.h @@ -0,0 +1,26 @@ +/* ===-- unistd.h - stub SDK 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 is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __UNISTD_H__ +#define __UNISTD_H__ + +enum { + _SC_PAGESIZE = 30 +}; + +extern long int sysconf (int __name) __attribute__ ((__nothrow__)); + +#endif /* __UNISTD_H__ */ 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) { diff --git a/make/AppleBI.mk b/make/AppleBI.mk index 0e3473d..96f8222 100644 --- a/make/AppleBI.mk +++ b/make/AppleBI.mk @@ -14,17 +14,12 @@ endif ifeq (,$(SDKROOT)) INSTALL_TARGET = install-MacOSX - LD_OTHER_FLAGS = else INSTALL_TARGET = install-iOS - CFLAGS.Release.armv6 := $(CFLAGS) -Wall -Os -fomit-frame-pointer -g -isysroot $(SDKROOT) - CFLAGS.Release.armv7 := $(CFLAGS) -Wall -Os -fomit-frame-pointer -g -isysroot $(SDKROOT) - CFLAGS.Static.armv6 := $(CFLAGS) -Wall -Os -fomit-frame-pointer -g -isysroot $(SDKROOT) -static - CFLAGS.Static.armv7 := $(CFLAGS) -Wall -Os -fomit-frame-pointer -g -isysroot $(SDKROOT) -static - LD_OTHER_FLAGS = -Wl,-alias_list,$(SRCROOT)/lib/arm/softfloat-alias.list -isysroot $(SDKROOT) endif + # Log full compile lines in B&I logs and omit summary lines. Verb := Summary := @true @@ -49,7 +44,7 @@ install-MacOSX : $(SYMROOT)/libcompiler_rt.dylib \ cp $(SYMROOT)/libcompiler_rt-dyld.a \ $(DSTROOT)/usr/local/lib/dyld/libcompiler_rt.a mkdir -p $(DSTROOT)/usr/lib/system - strip -S $(SYMROOT)/libcompiler_rt.dylib \ + $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt.dylib \ -o $(DSTROOT)/usr/lib/system/libcompiler_rt.dylib cd $(DSTROOT)/usr/lib/system; \ ln -s libcompiler_rt.dylib libcompiler_rt_profile.dylib; \ @@ -58,16 +53,17 @@ install-MacOSX : $(SYMROOT)/libcompiler_rt.dylib \ # Rule to make each dylib slice $(OBJROOT)/libcompiler_rt-%.dylib : $(OBJROOT)/darwin_bni/Release/%/libcompiler_rt.a echo "const char vers[] = \"@(#) $(RC_ProjectName)-$(RC_ProjectSourceVersion)\"; " > $(OBJROOT)/version.c - $(CC.Release) $(OBJROOT)/version.c -arch $* -dynamiclib \ + $(call GetCNAVar,CC,Platform.darwin_bni,Release,$*) \ + $(OBJROOT)/version.c -arch $* -dynamiclib \ -install_name /usr/lib/system/libcompiler_rt.dylib \ -compatibility_version 1 -current_version $(RC_ProjectSourceVersion) \ -nodefaultlibs -lSystem -umbrella System -dead_strip \ - $(LD_OTHER_FLAGS) -Wl,-force_load,$^ -o $@ + $(DYLIB_FLAGS) -Wl,-force_load,$^ -o $@ # Rule to make fat dylib -$(SYMROOT)/libcompiler_rt.dylib: $(foreach arch,$(RC_ARCHS), \ +$(SYMROOT)/libcompiler_rt.dylib: $(foreach arch,$(filter-out armv4t,$(RC_ARCHS)), \ $(OBJROOT)/libcompiler_rt-$(arch).dylib) - lipo -create $^ -o $@ + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ @@ -83,25 +79,27 @@ install-iOS: $(SYMROOT)/libcompiler_rt-static.a \ cp $(SYMROOT)/libcompiler_rt-dyld.a \ $(DSTROOT)/usr/local/lib/dyld/libcompiler_rt.a mkdir -p $(DSTROOT)/usr/lib/system - strip -S $(SYMROOT)/libcompiler_rt.dylib \ + $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt.dylib \ -o $(DSTROOT)/usr/lib/system/libcompiler_rt.dylib # Rule to make fat archive $(SYMROOT)/libcompiler_rt-static.a : $(foreach arch,$(RC_ARCHS), \ $(OBJROOT)/darwin_bni/Static/$(arch)/libcompiler_rt.a) - lipo -create $^ -o $@ + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ -# rule to make each archive slice for dyld +# rule to make each archive slice for dyld (which removes a few archive members) $(OBJROOT)/libcompiler_rt-dyld-%.a : $(OBJROOT)/darwin_bni/Release/%/libcompiler_rt.a cp $^ $@ - ar -d $@ apple_versioning.o - ar -d $@ gcc_personality_v0.o - ar -d $@ eprintf.o - ranlib $@ + DEL_LIST=`$(AR) -t $@ | egrep 'apple_versioning|gcc_personality_v0|eprintf' | xargs echo` ; \ + if [ -n "$${DEL_LIST}" ] ; \ + then \ + $(call GetCNAVar,AR,Platform.darwin_bni,Release,) -d $@ $${DEL_LIST}; \ + $(call GetCNAVar,RANLIB,Platform.darwin_bni,Release,) $@ ; \ + fi # rule to make make archive for dyld $(SYMROOT)/libcompiler_rt-dyld.a : $(foreach arch,$(RC_ARCHS), \ $(OBJROOT)/libcompiler_rt-dyld-$(arch).a) - lipo -create $^ -o $@ + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ diff --git a/make/config.mk b/make/config.mk index d96b1b4..a826173 100644 --- a/make/config.mk +++ b/make/config.mk @@ -8,6 +8,11 @@ OS := $(shell uname) ProjSrcRoot := $(shell pwd) ProjObjRoot := $(ProjSrcRoot) +# The list of modules which are required to be built into every library. This +# should only be used for internal utilities which could be used in any other +# module. Any other cases the platform should be allowed to opt-in to. +AlwaysRequiredModules := int_util + ### # Tool configuration variables. @@ -34,3 +39,8 @@ Echo := @echo ifndef Summary Summary = $(Echo) endif + +### +# Common compiler options +COMMON_CXXFLAGS=-fno-exceptions -fPIC -funwind-tables +COMMON_CFLAGS=-fPIC diff --git a/make/lib_info.mk b/make/lib_info.mk index 72bc4a1..2e85f64 100644 --- a/make/lib_info.mk +++ b/make/lib_info.mk @@ -14,6 +14,14 @@ # AvailableIn.<function> - The list of subdir keys where 'function' is # defined. +# Determine the set of available modules. +AvailableModules := $(sort $(foreach key,$(SubDirKeys),\ + $($(key).ModuleName))) + +# Build a per-module map of subdir keys. +$(foreach key,$(SubDirKeys),\ + $(call Append,ModuleSubDirKeys.$($(key).ModuleName),$(key))) + AvailableArchs := $(sort $(foreach key,$(SubDirKeys),\ $($(key).OnlyArchs))) @@ -21,12 +29,12 @@ AvailableFunctions := $(sort $(foreach key,$(SubDirKeys),\ $(basename $($(key).ObjNames)))) CommonFunctions := $(sort\ - $(foreach key,$(SubDirKeys),\ + $(foreach key,$(ModuleSubDirKeys.builtins),\ $(if $(call strneq,,$(strip $($(key).OnlyArchs) $($(key).OnlyConfigs))),,\ $(basename $($(key).ObjNames))))) # Compute common arch functions. -$(foreach key,$(SubDirKeys),\ +$(foreach key,$(ModuleSubDirKeys.builtins),\ $(if $(call strneq,,$($(key).OnlyConfigs)),,\ $(foreach arch,$($(key).OnlyArchs),\ $(call Append,ArchFunctions.$(arch),$(sort \ @@ -48,4 +56,5 @@ AvailableOptions := AR ARFLAGS \ CC CFLAGS FUNCTIONS OPTIMIZED \ RANLIB RANLIBFLAGS \ VISIBILITY_HIDDEN \ - KERNEL_USE + KERNEL_USE \ + STRIP LIPO diff --git a/make/options.mk b/make/options.mk index f6a331b..f695fc8 100644 --- a/make/options.mk +++ b/make/options.mk @@ -31,3 +31,6 @@ ARFLAGS := cru 2> /dev/null RANLIB := ranlib # FIXME: Remove these pipes once ranlib errors are fixed. RANLIBFLAGS := 2> /dev/null + +STRIP := strip +LIPO := lipo diff --git a/make/platform/clang_darwin.mk b/make/platform/clang_darwin.mk index 6eb10c8..bb8bf78 100644 --- a/make/platform/clang_darwin.mk +++ b/make/platform/clang_darwin.mk @@ -12,7 +12,9 @@ CheckArches = \ $(shell \ result=""; \ for arch in $(1); do \ - if $(CC) -arch $$arch -dumpversion > /dev/null; then \ + if $(CC) -arch $$arch -c \ + $(ProjSrcRoot)/make/platform/clang_darwin_test_input.c \ + -o /dev/null > /dev/null 2> /dev/null; then \ result="$$result$$arch "; \ fi; \ done; \ @@ -20,6 +22,8 @@ CheckArches = \ ### +CC := clang + Configs := UniversalArchs := @@ -41,13 +45,41 @@ UniversalArchs.10.4 := $(call CheckArches,i386 x86_64) Configs += ios UniversalArchs.ios := $(call CheckArches,i386 x86_64 armv6 armv7) +# Configuration for targetting OSX. These functions may not be in libSystem +# so we should provide our own. +Configs += osx +UniversalArchs.osx := $(call CheckArches,i386 x86_64) + # Configuration for use with kernel/kexts. Configs += cc_kext UniversalArchs.cc_kext := $(call CheckArches,armv6 armv7 i386 x86_64) -### +# Configurations which define the profiling support functions. +Configs += profile_osx +UniversalArchs.profile_osx := $(call CheckArches,i386 x86_64) +Configs += profile_ios +UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64 armv6 armv7) + +# Configurations which define the ASAN support functions. +# +# Note that ASAN doesn't appear to currently support i386. +Configs += asan_osx +UniversalArchs.asan_osx := $(call CheckArches,i386 x86_64) -CC := gcc +# If RC_SUPPORTED_ARCHS is defined, treat it as a list of the architectures we +# are intended to support and limit what we try to build to that. +# +# We make sure to remove empty configs if we end up dropping all the requested +# archs for a particular config. +ifneq ($(RC_SUPPORTED_ARCHS),) +$(foreach config,$(Configs),\ + $(call Set,UniversalArchs.$(config),\ + $(filter $(RC_SUPPORTED_ARCHS),$(UniversalArchs.$(config))))\ + $(if $(UniversalArchs.$(config)),,\ + $(call Set,Configs,$(filter-out $(config),$(Configs))))) +endif + +### # Forcibly strip off any -arch, as that totally breaks our universal support. override CC := $(subst -arch ,-arch_,$(CC)) @@ -59,29 +91,41 @@ CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer # never depend on the environmental overrides. We simply set them to minimum # supported deployment target -- nothing in the compiler-rt libraries should # actually depend on the deployment target. -X86_DEPLOYMENT_ARGS := -mmacosx-version-min=10.4 -ARM_DEPLOYMENT_ARGS := -miphoneos-version-min=1.0 +OSX_DEPLOYMENT_ARGS := -mmacosx-version-min=10.4 +IOS_DEPLOYMENT_ARGS := -miphoneos-version-min=1.0 +IOSSIM_DEPLOYMENT_ARGS := -miphoneos-version-min=1.0 -# If an explicit ARM_SDK build variable is set, use that as the isysroot. -ifneq ($(ARM_SDK),) -ARM_DEPLOYMENT_ARGS += -isysroot $(ARM_SDK) -endif +# Use our stub SDK as the sysroot to support more portable building. +OSX_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin +IOS_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin +IOSSIM_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin + +CFLAGS.eprintf := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.10.4 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +# FIXME: We can't build ASAN with our stub SDK yet. +CFLAGS.asan_osx := $(CFLAGS) -mmacosx-version-min=10.5 -CFLAGS.eprintf := $(CFLAGS) $(X86_DEPLOYMENT_ARGS) -CFLAGS.10.4 := $(CFLAGS) $(X86_DEPLOYMENT_ARGS) -CFLAGS.ios.i386 := $(CFLAGS) $(X86_DEPLOYMENT_ARGS) -CFLAGS.ios.x86_64 := $(CFLAGS) $(X86_DEPLOYMENT_ARGS) -CFLAGS.ios.armv6 := $(CFLAGS) $(ARM_DEPLOYMENT_ARGS) -CFLAGS.ios.armv7 := $(CFLAGS) $(ARM_DEPLOYMENT_ARGS) -CFLAGS.cc_kext.i386 := $(CFLAGS) $(X86_DEPLOYMENT_ARGS) -CFLAGS.cc_kext.x86_64 := $(CFLAGS) $(X86_DEPLOYMENT_ARGS) -CFLAGS.cc_kext.armv6 := $(CFLAGS) $(ARM_DEPLOYMENT_ARGS) -mthumb -CFLAGS.cc_kext.armv7 := $(CFLAGS) $(ARM_DEPLOYMENT_ARGS) -mthumb +CFLAGS.ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.ios.armv6 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.osx.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.osx.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.armv6 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) -mthumb +CFLAGS.cc_kext.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.profile_osx.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.profile_osx.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.armv6 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) FUNCTIONS.eprintf := eprintf FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf -FUNCTIONS.ios := divmodsi4 udivmodsi4 +FUNCTIONS.ios := divmodsi4 udivmodsi4 mulosi4 mulodi4 muloti4 # On x86, the divmod functions reference divsi. FUNCTIONS.ios.i386 := $(FUNCTIONS.ios) \ divsi3 udivsi3 @@ -92,6 +136,13 @@ FUNCTIONS.ios.armv6 := $(FUNCTIONS.ios) \ switch16 switch32 switch8 switchu8 \ save_vfp_d8_d15_regs restore_vfp_d8_d15_regs +FUNCTIONS.osx := mulosi4 mulodi4 muloti4 + +FUNCTIONS.profile_osx := GCDAProfiling +FUNCTIONS.profile_ios := GCDAProfiling + +FUNCTIONS.asan_osx := $(AsanFunctions) + CCKEXT_COMMON_FUNCTIONS := \ absvdi2 \ absvsi2 \ @@ -203,8 +254,42 @@ CCKEXT_ARM_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \ unorddf2 \ unordsf2 -FUNCTIONS.cc_kext.armv6 := $(CCKEXT_ARM_FUNCTIONS) -FUNCTIONS.cc_kext.armv7 := $(CCKEXT_ARM_FUNCTIONS) +CCKEXT_ARMVFP_FUNCTIONS := $(CCKEXT_ARM_FUNCTIONS) \ + adddf3vfp \ + addsf3vfp \ + divdf3vfp \ + divsf3vfp \ + eqdf2vfp \ + eqsf2vfp \ + extendsfdf2vfp \ + fixdfsivfp \ + fixsfsivfp \ + fixunsdfsivfp \ + fixunssfsivfp \ + floatsidfvfp \ + floatsisfvfp \ + floatunssidfvfp \ + floatunssisfvfp \ + gedf2vfp \ + gesf2vfp \ + gtdf2vfp \ + gtsf2vfp \ + ledf2vfp \ + lesf2vfp \ + ltdf2vfp \ + ltsf2vfp \ + muldf3vfp \ + mulsf3vfp \ + nedf2vfp \ + nesf2vfp \ + subdf3vfp \ + subsf3vfp \ + truncdfsf2vfp \ + unorddf2vfp \ + unordsf2vfp + +FUNCTIONS.cc_kext.armv6 := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS) CCKEXT_X86_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \ divxc3 \ diff --git a/make/platform/clang_darwin_test_input.c b/make/platform/clang_darwin_test_input.c new file mode 100644 index 0000000..b7074b8 --- /dev/null +++ b/make/platform/clang_darwin_test_input.c @@ -0,0 +1,6 @@ +/* Include the headers we use in int_lib.h, to verify that they work. */ + +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> diff --git a/make/platform/clang_linux.mk b/make/platform/clang_linux.mk new file mode 100644 index 0000000..b3a7759 --- /dev/null +++ b/make/platform/clang_linux.mk @@ -0,0 +1,82 @@ +Description := Static runtime libraries for clang/Linux. + +### + +CC := clang +Arch := unknown +Configs := + +# We don't currently have any general purpose way to target architectures other +# than the compiler defaults (because there is no generalized way to invoke +# cross compilers). For now, we just find the target archicture of the compiler +# and only define configurations we know that compiler can generate. +CompilerTargetTriple := $(shell \ + $(CC) -v 2>&1 | grep 'Target:' | cut -d' ' -f2) +ifneq ($(DEBUGMAKE),) +ifeq ($(CompilerTargetTriple),) +$(error "unable to infer compiler target triple for $(CC)") +endif +endif + +CompilerTargetArch := $(firstword $(subst -, ,$(CompilerTargetTriple))) + +# Only define configs if we detected a linux target. +ifneq ($(findstring -linux-,$(CompilerTargetTriple)),) + +# Configurations which just include all the runtime functions. +ifeq ($(call contains,i386 x86_64,$(CompilerTargetArch)),true) +Configs += full-i386 full-x86_64 +Arch.full-i386 := i386 +Arch.full-x86_64 := x86_64 +endif + +# Configuration for profile runtime. +ifeq ($(call contains,i386 x86_64,$(CompilerTargetArch)),true) +Configs += profile-i386 profile-x86_64 +Arch.profile-i386 := i386 +Arch.profile-x86_64 := x86_64 +endif + +# Configuration for ASAN runtime. +ifeq ($(CompilerTargetArch),i386) +Configs += asan-i386 +Arch.asan-i386 := i386 +endif +ifeq ($(CompilerTargetArch),x86_64) +Configs += asan-x86_64 +Arch.asan-x86_64 := x86_64 +endif + +endif + +### + +CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer + +CFLAGS.full-i386 := $(CFLAGS) -m32 +CFLAGS.full-x86_64 := $(CFLAGS) -m64 +CFLAGS.profile-i386 := $(CFLAGS) -m32 +CFLAGS.profile-x86_64 := $(CFLAGS) -m64 +CFLAGS.asan-i386 := $(CFLAGS) -m32 +CFLAGS.asan-x86_64 := $(CFLAGS) -m64 + +# Use our stub SDK as the sysroot to support more portable building. For now we +# just do this for the non-ASAN modules, because the stub SDK doesn't have +# enough support to build ASAN. +CFLAGS.full-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux +CFLAGS.full-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux +CFLAGS.profile-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux +CFLAGS.profile-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux + +FUNCTIONS.full-i386 := $(CommonFunctions) $(ArchFunctions.i386) +FUNCTIONS.full-x86_64 := $(CommonFunctions) $(ArchFunctions.x86_64) +FUNCTIONS.profile-i386 := GCDAProfiling +FUNCTIONS.profile-x86_64 := GCDAProfiling +FUNCTIONS.asan-i386 := $(AsanFunctions) +FUNCTIONS.asan-x86_64 := $(AsanFunctions) + +# Always use optimized variants. +OPTIMIZED := 1 + +# We don't need to use visibility hidden on Linux. +VISIBILITY_HIDDEN := 0 diff --git a/make/platform/darwin_bni.mk b/make/platform/darwin_bni.mk index 14a1c19..477e072 100644 --- a/make/platform/darwin_bni.mk +++ b/make/platform/darwin_bni.mk @@ -1,24 +1,37 @@ Description := Target for Darwin using an Apple-style build. -Configs := Debug Release Profile Static +Configs := Debug Release Profile Static # We override this with RC_ARCHS because B&I may want to build on an ARCH we # haven't explicitly defined support for. If all goes well, this will just work # and the resulting lib will just have generic versions for anything unknown. UniversalArchs := $(RC_ARCHS) -ifeq (,$(SDKROOT)) - CC.Release := $(CC) - CC.Static := $(CC) +ifneq (,$(SDKROOT)) + override CC := $(shell xcrun -sdk $(SDKROOT) -find clang) + AR := $(shell xcrun -sdk $(SDKROOT) -find ar) + RANLIB := $(shell xcrun -sdk $(SDKROOT) -find ranlib) + STRIP := $(shell xcrun -sdk $(SDKROOT) -find strip) + LIPO := $(shell xcrun -sdk $(SDKROOT) -find lipo) +endif + +ifneq ($(IPHONEOS_DEPLOYMENT_TARGET),) + DEPLOYMENT_FLAGS := -miphoneos-version-min=$(IPHONEOS_DEPLOYMENT_TARGET) else - CC.Release := /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/cc - CC.Static := /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/cc + ifneq ($(MACOSX_DEPLOYMENT_TARGET),) + DEPLOYMENT_FLAGS := -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) + endif endif +ifneq (,$(SDKROOT)) + DEPLOYMENT_FLAGS += -isysroot $(SDKROOT) +endif -CFLAGS := -Wall -Os -fomit-frame-pointer -g -CFLAGS.Static := $(CFLAGS) -static +CFLAGS := -Wall -Os -fomit-frame-pointer -g $(DEPLOYMENT_FLAGS) +CFLAGS.Static := $(CFLAGS) -static +DYLIB_FLAGS := $(DEPLOYMENT_FLAGS) \ + -Xarch_arm -Wl,-alias_list,$(SRCROOT)/lib/arm/softfloat-alias.list VISIBILITY_HIDDEN := 0 VISIBILITY_HIDDEN.Static := 1 @@ -29,8 +42,8 @@ FUNCTIONS := absvdi2 absvsi2 addvdi3 addvsi3 ashldi3 ashrdi3 \ divdc3 divdi3 divsc3 ffsdi2 \ fixdfdi fixsfdi fixunsdfdi fixunsdfsi fixunssfdi \ fixunssfsi floatdidf floatdisf floatundidf floatundisf \ - gcc_personality_v0 lshrdi3 moddi3 muldc3 muldi3 \ - mulsc3 mulvdi3 mulvsi3 negdi2 negvdi2 negvsi2 \ + gcc_personality_v0 lshrdi3 moddi3 muldc3 muldi3 mulosi4 \ + mulodi4 muloti4 mulsc3 mulvdi3 mulvsi3 negdi2 negvdi2 negvsi2 \ paritydi2 paritysi2 popcountdi2 popcountsi2 powidf2 \ powisf2 subvdi3 subvsi3 ucmpdi2 udivdi3 \ udivmoddi4 umoddi3 apple_versioning eprintf @@ -51,9 +64,12 @@ FUNCTIONS.x86_64 := $(FUNCTIONS) \ fixunsxfti fixxfdi fixxfti floatdixf floattidf \ floattisf floattixf floatundixf floatuntidf \ floatuntisf floatuntixf lshrti3 modti3 multi3 \ - mulvti3 mulxc3 negti2 negvti2 parityti2 \ + muloti4 mulvti3 mulxc3 negti2 negvti2 parityti2 \ popcountti2 powixf2 subvti3 ucmpti2 udivmodti4 \ udivti3 umodti3 clear_cache enable_execute_stack + +FUNCTIONS.armv4t := $(FUNCTIONS) + FUNCTIONS.armv5 := $(FUNCTIONS) \ adddf3 addsf3 bswapdi2 bswapsi2 \ comparedf2 comparesf2 extendsfdf2 \ @@ -65,7 +81,7 @@ FUNCTIONS.armv5 := $(FUNCTIONS) \ truncdfsf2 \ modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 \ switch8 switchu8 switch16 switch32 \ - sync_synchronize + sync_synchronize FUNCTIONS.armv6 := $(FUNCTIONS) \ comparedf2 comparesf2 \ @@ -81,7 +97,7 @@ FUNCTIONS.armv6 := $(FUNCTIONS) \ modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 \ switch8 switchu8 switch16 switch32 \ restore_vfp_d8_d15_regs save_vfp_d8_d15_regs \ - sync_synchronize + sync_synchronize FUNCTIONS.armv7 := $(FUNCTIONS) \ comparedf2 comparesf2 \ @@ -95,4 +111,3 @@ FUNCTIONS.armv7 := $(FUNCTIONS) \ nedf2vfp nesf2vfp \ subdf3vfp subsf3vfp truncdfsf2vfp unorddf2vfp unordsf2vfp \ modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 - diff --git a/make/platform/darwin_fat.mk b/make/platform/darwin_fat.mk index 30e57a1..54936a3 100644 --- a/make/platform/darwin_fat.mk +++ b/make/platform/darwin_fat.mk @@ -24,7 +24,7 @@ Description := Target for building universal libraries for Darwin. Configs := Debug Release Profile -UniversalArchs := i386 ppc x86_64 +UniversalArchs := i386 x86_64 # Platform Options # @@ -37,7 +37,7 @@ UniversalArchs := i386 ppc x86_64 # <Option Name>.<Arch Name> := ... # <Option Name>.<Config Name>.<Arch Name> := ... -CC := gcc +CC := clang CFLAGS := -Wall -Werror CFLAGS.Debug := $(CFLAGS) -g diff --git a/make/platform/multi_arch.mk b/make/platform/multi_arch.mk index eebc7b2..fe6ac4b 100644 --- a/make/platform/multi_arch.mk +++ b/make/platform/multi_arch.mk @@ -5,8 +5,7 @@ Configs := m32 m64 Arch := i386 Arch.m64 := x86_64 -CC := gcc -CC.m32 := clang +CC := clang CFLAGS := -Wall -Werror CFLAGS.m32 := $(CFLAGS) -m32 -O3 diff --git a/make/subdir.mk b/make/subdir.mk index 900f7e6..5b3c1f0 100644 --- a/make/subdir.mk +++ b/make/subdir.mk @@ -20,7 +20,8 @@ endif # The list of variables which are intended to be overridden in a subdirectory # makefile. -RequiredSubdirVariables := SubDirs ObjNames Implementation Dependencies +RequiredSubdirVariables := \ + ModuleName SubDirs ObjNames Implementation Dependencies OptionalSubdirVariables := OnlyArchs OnlyConfigs # Template: subdir_traverse_template subdir diff --git a/test/Unit/mulodi4_test.c b/test/Unit/mulodi4_test.c new file mode 100644 index 0000000..10a0eaa --- /dev/null +++ b/test/Unit/mulodi4_test.c @@ -0,0 +1,178 @@ +//===-- mulodi4_test.c - Test __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 tests __mulodi4 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" +#include <stdio.h> + +extern di_int __mulodi4(di_int a, di_int b, int* overflow); + +int test__mulodi4(di_int a, di_int b, di_int expected, int expected_overflow) +{ + int ov; + di_int x = __mulodi4(a, b, &ov); + if (ov != expected_overflow) + printf("error in __mulodi4: overflow=%d expected=%d\n", + ov, expected_overflow); + else if (!expected_overflow && x != expected) { + printf("error in __mulodi4: 0x%llX * 0x%llX = 0x%llX (overflow=%d), " + "expected 0x%llX (overflow=%d)\n", + a, b, x, ov, expected, expected_overflow); + return 1; + } + return 0; +} + +int main() +{ + if (test__mulodi4(0, 0, 0, 0)) + return 1; + if (test__mulodi4(0, 1, 0, 0)) + return 1; + if (test__mulodi4(1, 0, 0, 0)) + return 1; + if (test__mulodi4(0, 10, 0, 0)) + return 1; + if (test__mulodi4(10, 0, 0, 0)) + return 1; + if (test__mulodi4(0, 81985529216486895LL, 0, 0)) + return 1; + if (test__mulodi4(81985529216486895LL, 0, 0, 0)) + return 1; + + if (test__mulodi4(0, -1, 0, 0)) + return 1; + if (test__mulodi4(-1, 0, 0, 0)) + return 1; + if (test__mulodi4(0, -10, 0, 0)) + return 1; + if (test__mulodi4(-10, 0, 0, 0)) + return 1; + if (test__mulodi4(0, -81985529216486895LL, 0, 0)) + return 1; + if (test__mulodi4(-81985529216486895LL, 0, 0, 0)) + return 1; + + if (test__mulodi4(1, 1, 1, 0)) + return 1; + if (test__mulodi4(1, 10, 10, 0)) + return 1; + if (test__mulodi4(10, 1, 10, 0)) + return 1; + if (test__mulodi4(1, 81985529216486895LL, 81985529216486895LL, 0)) + return 1; + if (test__mulodi4(81985529216486895LL, 1, 81985529216486895LL, 0)) + return 1; + + if (test__mulodi4(1, -1, -1, 0)) + return 1; + if (test__mulodi4(1, -10, -10, 0)) + return 1; + if (test__mulodi4(-10, 1, -10, 0)) + return 1; + if (test__mulodi4(1, -81985529216486895LL, -81985529216486895LL, 0)) + return 1; + if (test__mulodi4(-81985529216486895LL, 1, -81985529216486895LL, 0)) + return 1; + + if (test__mulodi4(3037000499LL, 3037000499LL, 9223372030926249001LL, 0)) + return 1; + if (test__mulodi4(-3037000499LL, 3037000499LL, -9223372030926249001LL, 0)) + return 1; + if (test__mulodi4(3037000499LL, -3037000499LL, -9223372030926249001LL, 0)) + return 1; + if (test__mulodi4(-3037000499LL, -3037000499LL, 9223372030926249001LL, 0)) + return 1; + + if (test__mulodi4(4398046511103LL, 2097152LL, 9223372036852678656LL, 0)) + return 1; + if (test__mulodi4(-4398046511103LL, 2097152LL, -9223372036852678656LL, 0)) + return 1; + if (test__mulodi4(4398046511103LL, -2097152LL, -9223372036852678656LL, 0)) + return 1; + if (test__mulodi4(-4398046511103LL, -2097152LL, 9223372036852678656LL, 0)) + return 1; + + if (test__mulodi4(2097152LL, 4398046511103LL, 9223372036852678656LL, 0)) + return 1; + if (test__mulodi4(-2097152LL, 4398046511103LL, -9223372036852678656LL, 0)) + return 1; + if (test__mulodi4(2097152LL, -4398046511103LL, -9223372036852678656LL, 0)) + return 1; + if (test__mulodi4(-2097152LL, -4398046511103LL, 9223372036852678656LL, 0)) + return 1; + + if (test__mulodi4(0x7FFFFFFFFFFFFFFFLL, -2, 2, 1)) + return 1; + if (test__mulodi4(-2, 0x7FFFFFFFFFFFFFFFLL, 2, 1)) + return 1; + if (test__mulodi4(0x7FFFFFFFFFFFFFFFLL, -1, 0x8000000000000001LL, 0)) + return 1; + if (test__mulodi4(-1, 0x7FFFFFFFFFFFFFFFLL, 0x8000000000000001LL, 0)) + return 1; + if (test__mulodi4(0x7FFFFFFFFFFFFFFFLL, 0, 0, 0)) + return 1; + if (test__mulodi4(0, 0x7FFFFFFFFFFFFFFFLL, 0, 0)) + return 1; + if (test__mulodi4(0x7FFFFFFFFFFFFFFFLL, 1, 0x7FFFFFFFFFFFFFFFLL, 0)) + return 1; + if (test__mulodi4(1, 0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL, 0)) + return 1; + if (test__mulodi4(0x7FFFFFFFFFFFFFFFLL, 2, 0x8000000000000001LL, 1)) + return 1; + if (test__mulodi4(2, 0x7FFFFFFFFFFFFFFFLL, 0x8000000000000001LL, 1)) + return 1; + + if (test__mulodi4(0x8000000000000000LL, -2, 0x8000000000000000LL, 1)) + return 1; + if (test__mulodi4(-2, 0x8000000000000000LL, 0x8000000000000000LL, 1)) + return 1; + if (test__mulodi4(0x8000000000000000LL, -1, 0x8000000000000000LL, 1)) + return 1; + if (test__mulodi4(-1, 0x8000000000000000LL, 0x8000000000000000LL, 1)) + return 1; + if (test__mulodi4(0x8000000000000000LL, 0, 0, 0)) + return 1; + if (test__mulodi4(0, 0x8000000000000000LL, 0, 0)) + return 1; + if (test__mulodi4(0x8000000000000000LL, 1, 0x8000000000000000LL, 0)) + return 1; + if (test__mulodi4(1, 0x8000000000000000LL, 0x8000000000000000LL, 0)) + return 1; + if (test__mulodi4(0x8000000000000000LL, 2, 0x8000000000000000LL, 1)) + return 1; + if (test__mulodi4(2, 0x8000000000000000LL, 0x8000000000000000LL, 1)) + return 1; + + if (test__mulodi4(0x8000000000000001LL, -2, 0x8000000000000001LL, 1)) + return 1; + if (test__mulodi4(-2, 0x8000000000000001LL, 0x8000000000000001LL, 1)) + return 1; + if (test__mulodi4(0x8000000000000001LL, -1, 0x7FFFFFFFFFFFFFFFLL, 0)) + return 1; + if (test__mulodi4(-1, 0x8000000000000001LL, 0x7FFFFFFFFFFFFFFFLL, 0)) + return 1; + if (test__mulodi4(0x8000000000000001LL, 0, 0, 0)) + return 1; + if (test__mulodi4(0, 0x8000000000000001LL, 0, 0)) + return 1; + if (test__mulodi4(0x8000000000000001LL, 1, 0x8000000000000001LL, 0)) + return 1; + if (test__mulodi4(1, 0x8000000000000001LL, 0x8000000000000001LL, 0)) + return 1; + if (test__mulodi4(0x8000000000000001LL, 2, 0x8000000000000000LL, 1)) + return 1; + if (test__mulodi4(2, 0x8000000000000001LL, 0x8000000000000000LL, 1)) + return 1; + + return 0; +} diff --git a/test/Unit/mulosi4_test.c b/test/Unit/mulosi4_test.c new file mode 100644 index 0000000..fc509db --- /dev/null +++ b/test/Unit/mulosi4_test.c @@ -0,0 +1,156 @@ +//===-- mulosi4_test.c - Test __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 tests __mulosi4 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" +#include <stdio.h> + +// Returns: a * b + +// Effects: aborts if a * b overflows + +si_int __mulosi4(si_int a, si_int b, int *overflow); + +int test__mulosi4(si_int a, si_int b, si_int expected, int expected_overflow) +{ + int ov; + si_int x = __mulosi4(a, b, &ov); + if (ov != expected_overflow) + printf("error in __mulosi4: overflow=%d expected=%d\n", + ov, expected_overflow); + else if (!expected_overflow && x != expected) { + printf("error in __mulosi4: 0x%X * 0x%X = 0x%X (overflow=%d), " + "expected 0x%X (overflow=%d)\n", + a, b, x, ov, expected, expected_overflow); + return 1; + } + return 0; +} + + +int main() +{ + if (test__mulosi4(0, 0, 0, 0)) + return 1; + if (test__mulosi4(0, 1, 0, 0)) + return 1; + if (test__mulosi4(1, 0, 0, 0)) + return 1; + if (test__mulosi4(0, 10, 0, 0)) + return 1; + if (test__mulosi4(10, 0, 0, 0)) + return 1; + if (test__mulosi4(0, 0x1234567, 0, 0)) + return 1; + if (test__mulosi4(0x1234567, 0, 0, 0)) + return 1; + + if (test__mulosi4(0, -1, 0, 0)) + return 1; + if (test__mulosi4(-1, 0, 0, 0)) + return 1; + if (test__mulosi4(0, -10, 0, 0)) + return 1; + if (test__mulosi4(-10, 0, 0, 0)) + return 1; + if (test__mulosi4(0, -0x1234567, 0, 0)) + return 1; + if (test__mulosi4(-0x1234567, 0, 0, 0)) + return 1; + + if (test__mulosi4(1, 1, 1, 0)) + return 1; + if (test__mulosi4(1, 10, 10, 0)) + return 1; + if (test__mulosi4(10, 1, 10, 0)) + return 1; + if (test__mulosi4(1, 0x1234567, 0x1234567, 0)) + return 1; + if (test__mulosi4(0x1234567, 1, 0x1234567, 0)) + return 1; + + if (test__mulosi4(1, -1, -1, 0)) + return 1; + if (test__mulosi4(1, -10, -10, 0)) + return 1; + if (test__mulosi4(-10, 1, -10, 0)) + return 1; + if (test__mulosi4(1, -0x1234567, -0x1234567, 0)) + return 1; + if (test__mulosi4(-0x1234567, 1, -0x1234567, 0)) + return 1; + + if (test__mulosi4(0x7FFFFFFF, -2, 0x80000001, 1)) + return 1; + if (test__mulosi4(-2, 0x7FFFFFFF, 0x80000001, 1)) + return 1; + if (test__mulosi4(0x7FFFFFFF, -1, 0x80000001, 0)) + return 1; + if (test__mulosi4(-1, 0x7FFFFFFF, 0x80000001, 0)) + return 1; + if (test__mulosi4(0x7FFFFFFF, 0, 0, 0)) + return 1; + if (test__mulosi4(0, 0x7FFFFFFF, 0, 0)) + return 1; + if (test__mulosi4(0x7FFFFFFF, 1, 0x7FFFFFFF, 0)) + return 1; + if (test__mulosi4(1, 0x7FFFFFFF, 0x7FFFFFFF, 0)) + return 1; + if (test__mulosi4(0x7FFFFFFF, 2, 0x80000001, 1)) + return 1; + if (test__mulosi4(2, 0x7FFFFFFF, 0x80000001, 1)) + return 1; + + if (test__mulosi4(0x80000000, -2, 0x80000000, 1)) + return 1; + if (test__mulosi4(-2, 0x80000000, 0x80000000, 1)) + return 1; + if (test__mulosi4(0x80000000, -1, 0x80000000, 1)) + return 1; + if (test__mulosi4(-1, 0x80000000, 0x80000000, 1)) + return 1; + if (test__mulosi4(0x80000000, 0, 0, 0)) + return 1; + if (test__mulosi4(0, 0x80000000, 0, 0)) + return 1; + if (test__mulosi4(0x80000000, 1, 0x80000000, 0)) + return 1; + if (test__mulosi4(1, 0x80000000, 0x80000000, 0)) + return 1; + if (test__mulosi4(0x80000000, 2, 0x80000000, 1)) + return 1; + if (test__mulosi4(2, 0x80000000, 0x80000000, 1)) + return 1; + + if (test__mulosi4(0x80000001, -2, 0x80000001, 1)) + return 1; + if (test__mulosi4(-2, 0x80000001, 0x80000001, 1)) + return 1; + if (test__mulosi4(0x80000001, -1, 0x7FFFFFFF, 0)) + return 1; + if (test__mulosi4(-1, 0x80000001, 0x7FFFFFFF, 0)) + return 1; + if (test__mulosi4(0x80000001, 0, 0, 0)) + return 1; + if (test__mulosi4(0, 0x80000001, 0, 0)) + return 1; + if (test__mulosi4(0x80000001, 1, 0x80000001, 0)) + return 1; + if (test__mulosi4(1, 0x80000001, 0x80000001, 0)) + return 1; + if (test__mulosi4(0x80000001, 2, 0x80000000, 1)) + return 1; + if (test__mulosi4(2, 0x80000001, 0x80000000, 1)) + return 1; + + return 0; +} diff --git a/test/Unit/muloti4_test.c b/test/Unit/muloti4_test.c new file mode 100644 index 0000000..44abddf --- /dev/null +++ b/test/Unit/muloti4_test.c @@ -0,0 +1,280 @@ +//===-- muloti4_test.c - Test __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 tests __muloti3 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#if __x86_64 + +#include "int_lib.h" +#include <stdio.h> + +// Returns: a * b + +// Effects: sets overflow if a * b overflows + +ti_int __muloti4(ti_int a, ti_int b, int *overflow); + +int test__muloti4(ti_int a, ti_int b, ti_int expected, int expected_overflow) +{ + int ov; + ti_int x = __muloti4(a, b, &ov); + if (ov != expected_overflow) { + twords at; + at.all = a; + twords bt; + bt.all = b; + twords xt; + xt.all = x; + twords expectedt; + expectedt.all = expected; + + printf("error in __muloti4: overflow=%d expected=%d\n", + ov, expected_overflow); + printf("error in __muloti4: 0x%.16llX%.16llX * 0x%.16llX%.16llX = " + "0x%.16llX%.16llX, expected 0x%.16llX%.16llX\n", + at.s.high, at.s.low, bt.s.high, bt.s.low, xt.s.high, xt.s.low, + expectedt.s.high, expectedt.s.low); + return 1; + } + else if (!expected_overflow && x != expected) + { + twords at; + at.all = a; + twords bt; + bt.all = b; + twords xt; + xt.all = x; + twords expectedt; + expectedt.all = expected; + printf("error in __muloti4: 0x%.16llX%.16llX * 0x%.16llX%.16llX = " + "0x%.16llX%.16llX, expected 0x%.16llX%.16llX\n", + at.s.high, at.s.low, bt.s.high, bt.s.low, xt.s.high, xt.s.low, + expectedt.s.high, expectedt.s.low); + return 1; + } + return 0; +} + +#endif + +int main() +{ +#if __x86_64 + if (test__muloti4(0, 0, 0, 0)) + return 1; + if (test__muloti4(0, 1, 0, 0)) + return 1; + if (test__muloti4(1, 0, 0, 0)) + return 1; + if (test__muloti4(0, 10, 0, 0)) + return 1; + if (test__muloti4(10, 0, 0, 0)) + return 1; + if (test__muloti4(0, 81985529216486895LL, 0, 0)) + return 1; + if (test__muloti4(81985529216486895LL, 0, 0, 0)) + return 1; + + if (test__muloti4(0, -1, 0, 0)) + return 1; + if (test__muloti4(-1, 0, 0, 0)) + return 1; + if (test__muloti4(0, -10, 0, 0)) + return 1; + if (test__muloti4(-10, 0, 0, 0)) + return 1; + if (test__muloti4(0, -81985529216486895LL, 0, 0)) + return 1; + if (test__muloti4(-81985529216486895LL, 0, 0, 0)) + return 1; + + if (test__muloti4(1, 1, 1, 0)) + return 1; + if (test__muloti4(1, 10, 10, 0)) + return 1; + if (test__muloti4(10, 1, 10, 0)) + return 1; + if (test__muloti4(1, 81985529216486895LL, 81985529216486895LL, 0)) + return 1; + if (test__muloti4(81985529216486895LL, 1, 81985529216486895LL, 0)) + return 1; + + if (test__muloti4(1, -1, -1, 0)) + return 1; + if (test__muloti4(1, -10, -10, 0)) + return 1; + if (test__muloti4(-10, 1, -10, 0)) + return 1; + if (test__muloti4(1, -81985529216486895LL, -81985529216486895LL, 0)) + return 1; + if (test__muloti4(-81985529216486895LL, 1, -81985529216486895LL, 0)) + return 1; + + if (test__muloti4(3037000499LL, 3037000499LL, 9223372030926249001LL, 0)) + return 1; + if (test__muloti4(-3037000499LL, 3037000499LL, -9223372030926249001LL, 0)) + return 1; + if (test__muloti4(3037000499LL, -3037000499LL, -9223372030926249001LL, 0)) + return 1; + if (test__muloti4(-3037000499LL, -3037000499LL, 9223372030926249001LL, 0)) + return 1; + + if (test__muloti4(4398046511103LL, 2097152LL, 9223372036852678656LL, 0)) + return 1; + if (test__muloti4(-4398046511103LL, 2097152LL, -9223372036852678656LL, 0)) + return 1; + if (test__muloti4(4398046511103LL, -2097152LL, -9223372036852678656LL, 0)) + return 1; + if (test__muloti4(-4398046511103LL, -2097152LL, 9223372036852678656LL, 0)) + return 1; + + if (test__muloti4(2097152LL, 4398046511103LL, 9223372036852678656LL, 0)) + return 1; + if (test__muloti4(-2097152LL, 4398046511103LL, -9223372036852678656LL, 0)) + return 1; + if (test__muloti4(2097152LL, -4398046511103LL, -9223372036852678656LL, 0)) + return 1; + if (test__muloti4(-2097152LL, -4398046511103LL, 9223372036852678656LL, 0)) + return 1; + + if (test__muloti4(make_ti(0x00000000000000B5LL, 0x04F333F9DE5BE000LL), + make_ti(0x0000000000000000LL, 0x00B504F333F9DE5BLL), + make_ti(0x7FFFFFFFFFFFF328LL, 0xDF915DA296E8A000LL), 0)) + return 1; + + if (test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + -2, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 1)) + return 1; + if (test__muloti4(-2, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 1)) + return 1; + if (test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + -1, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 0)) + return 1; + if (test__muloti4(-1, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 0)) + return 1; + if (test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + 0, + 0, 0)) + return 1; + if (test__muloti4(0, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + 0, 0)) + return 1; + if (test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + 1, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), 0)) + return 1; + if (test__muloti4(1, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), 0)) + return 1; + if (test__muloti4(make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + 2, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 1)) + return 1; + if (test__muloti4(2, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 1)) + return 1; + + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000000LL), + -2, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + if (test__muloti4(-2, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000000LL), + -1, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + if (test__muloti4(-1, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000000LL), + 0, + 0, 0)) + return 1; + if (test__muloti4(0, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), + 0, 0)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000000LL), + 1, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 0)) + return 1; + if (test__muloti4(1, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 0)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000000LL), + 2, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + if (test__muloti4(2, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000001LL), + -2, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 1)) + return 1; + if (test__muloti4(-2, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 1)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000001LL), + -1, + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), 0)) + return 1; + if (test__muloti4(-1, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), + make_ti(0x7FFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFFFFLL), 0)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000001LL), + 0, + 0, 0)) + return 1; + if (test__muloti4(0, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), + 0, 0)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000001LL), + 1, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 0)) + return 1; + if (test__muloti4(1, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), + make_ti(0x8000000000000000LL, 0x0000000000000001LL), 0)) + return 1; + if (test__muloti4(make_ti(0x8000000000000000LL, 0x0000000000000001LL), + 2, + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + if (test__muloti4(2, + make_ti(0x8000000000000000LL, 0x0000000000000001LL), + make_ti(0x8000000000000000LL, 0x0000000000000000LL), 1)) + return 1; + +#else + printf("skipped\n"); +#endif + return 0; +} diff --git a/www/index.html b/www/index.html index 7a7ef7c..85e5a83 100644 --- a/www/index.html +++ b/www/index.html @@ -26,6 +26,22 @@ <p>All of the code in the compiler-rt project is <a href="http://llvm.org/docs/DeveloperPolicy.html#license">dual licensed</a> under the MIT license and the UIUC License (a BSD-like license).</p> + + <!--=====================================================================--> + <h2 id="users">Clients</h2> + <!--=====================================================================--> + + <p>Currently compiler-rt is primarily used by + the <a href="http://clang.llvm.org">Clang</a> + and <a href="http://llvm.org">LLVM</a> projects as the implementation for + the runtime compiler support libraries. The library currently provides both + the low-level target-specific hooks required by code generation, as well as + additional modules for supporting the runtime requirements of features like + code coverage, profiling, or address sanitizer (ASAN) instrumentation.</p> + + <p>For more information on using compiler-rt with Clang, please see the Clang + <a href="http://clang.llvm.org/get_started.html">Getting Started</a> + page.</p> <!--=====================================================================--> <h2 id="goals">Goals</h2> |