Import compiler-rt r147390.

This commit is contained in:
Ed Schouten 2011-12-31 14:55:23 +00:00
parent b0a04aaa59
commit 219fb04889
267 changed files with 14521 additions and 676 deletions

6
.gitignore vendored
View File

@ -1,4 +1,4 @@
*~
Debug
Release
Profile
darwin_fat
clang_darwin
multi_arch

View File

@ -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

View File

@ -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

View File

@ -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

9
SDKs/README.txt Normal file
View File

@ -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.

3
SDKs/darwin/README.txt Normal file
View File

@ -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.

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

2
SDKs/linux/README.txt Normal file
View File

@ -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.

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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__ */

View File

@ -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

View File

@ -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

View File

@ -11,10 +11,8 @@
*
*===----------------------------------------------------------------------===
*/
#include "abi.h"
#include "int_lib.h"
#include <stdlib.h>
/* Returns: absolute value */

View File

@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
#include "abi.h"
#include "int_lib.h"
#include <stdlib.h>
/* Returns: absolute value */

View File

@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
#include <stdlib.h>
/* Returns: absolute value */

View File

@ -12,8 +12,6 @@
//
//===----------------------------------------------------------------------===//
#include "abi.h"
#define DOUBLE_PRECISION
#include "fp_lib.h"

View File

@ -12,8 +12,6 @@
//
//===----------------------------------------------------------------------===//
#include "abi.h"
#define SINGLE_PRECISION
#include "fp_lib.h"

View File

@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
#include "abi.h"
#include "int_lib.h"
#include <stdlib.h>
/* Returns: a + b */

View File

@ -11,10 +11,8 @@
*
* ===----------------------------------------------------------------------===
*/
#include "abi.h"
#include "int_lib.h"
#include <stdlib.h>
/* Returns: a + b */

View File

@ -15,7 +15,6 @@
#if __x86_64
#include "int_lib.h"
#include <stdlib.h>
/* Returns: a + b */

View File

@ -7,6 +7,7 @@
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := armv5 armv6 armv7

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

22
lib/asan/Makefile.mk Normal file
View File

@ -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=%)

352
lib/asan/Makefile.old Normal file
View File

@ -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-*

26
lib/asan/README.txt Normal file
View File

@ -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/

1020
lib/asan/asan_allocator.cc Normal file

File diff suppressed because it is too large Load Diff

157
lib/asan/asan_allocator.h Normal file
View File

@ -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

171
lib/asan/asan_globals.cc Normal file
View File

@ -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]);
}
}

View File

@ -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

View File

@ -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

135
lib/asan/asan_interface.h Normal file
View File

@ -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

239
lib/asan/asan_internal.h Normal file
View File

@ -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

101
lib/asan/asan_linux.cc Normal file
View File

@ -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__

100
lib/asan/asan_lock.h Normal file
View File

@ -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

311
lib/asan/asan_mac.cc Normal file
View File

@ -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__

87
lib/asan/asan_mac.h Normal file
View File

@ -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__

View File

@ -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__

390
lib/asan/asan_malloc_mac.cc Normal file
View File

@ -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__

100
lib/asan/asan_mapping.h Normal file
View File

@ -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

159
lib/asan/asan_poisoning.cc Normal file
View File

@ -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;
}

181
lib/asan/asan_printf.cc Normal file
View File

@ -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

810
lib/asan/asan_rtl.cc Normal file
View File

@ -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");
}
}

280
lib/asan/asan_stack.cc Normal file
View File

@ -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

94
lib/asan/asan_stack.h Normal file
View File

@ -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

88
lib/asan/asan_stats.cc Normal file
View File

@ -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();
}

59
lib/asan/asan_stats.h Normal file
View File

@ -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

178
lib/asan/asan_thread.cc Normal file
View File

@ -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

108
lib/asan/asan_thread.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
Some rights reserved: <http://opensource.org/licenses/mit-license.php>

View File

@ -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=%)

View File

@ -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.

View File

@ -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__

View File

@ -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__

Some files were not shown because too many files have changed in this diff Show More