Import compiler-rt r147390.
This commit is contained in:
parent
b0a04aaa59
commit
219fb04889
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
*~
|
||||
Debug
|
||||
Release
|
||||
Profile
|
||||
darwin_fat
|
||||
clang_darwin
|
||||
multi_arch
|
||||
|
22
LICENSE.TXT
22
LICENSE.TXT
@ -74,3 +74,25 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
Copyrights and Licenses for Third Party Software Distributed with LLVM:
|
||||
==============================================================================
|
||||
The LLVM software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
||||
other licenses gives permission to use the names of the LLVM Team or the
|
||||
University of Illinois to endorse or promote products derived from this
|
||||
Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
sysinfo lib/asan/sysinfo
|
||||
mach_override lib/asan/mach_override
|
||||
|
6
Makefile
6
Makefile
@ -164,6 +164,7 @@ define PerPlatformConfigArch_template
|
||||
$(call Set,Tmp.Arch,$(1))
|
||||
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch))
|
||||
$(call Set,Tmp.Functions,$(strip \
|
||||
$(AlwaysRequiredModules) \
|
||||
$(call GetCNAVar,FUNCTIONS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.Optimized,$(strip \
|
||||
$(call GetCNAVar,OPTIMIZED,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
@ -226,7 +227,10 @@ $(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.S $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$<
|
||||
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.c $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
|
||||
$(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$<
|
||||
$(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c $(COMMON_CFLAGS) -o $$@ $$<
|
||||
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.cc $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
|
||||
$(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c $(COMMON_CXXFLAGS) -o $$@ $$<
|
||||
.PRECIOUS: $(Tmp.ObjPath)/.dir
|
||||
|
||||
endef
|
||||
|
@ -106,6 +106,15 @@ si_int __mulvsi3(si_int a, si_int b); // a * b
|
||||
di_int __mulvdi3(di_int a, di_int b); // a * b
|
||||
ti_int __mulvti3(ti_int a, ti_int b); // a * b
|
||||
|
||||
|
||||
// Integral arithmetic which returns if overflow
|
||||
|
||||
si_int __mulosi4(si_int a, si_int b, int* overflow); // a * b, overflow set to one if result not in signed range
|
||||
di_int __mulodi4(di_int a, di_int b, int* overflow); // a * b, overflow set to one if result not in signed range
|
||||
ti_int __muloti4(ti_int a, ti_int b, int* overflow); // a * b, overflow set to
|
||||
one if result not in signed range
|
||||
|
||||
|
||||
// Integral comparison: a < b -> 0
|
||||
// a == b -> 1
|
||||
// a > b -> 2
|
||||
|
9
SDKs/README.txt
Normal file
9
SDKs/README.txt
Normal 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
3
SDKs/darwin/README.txt
Normal 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.
|
23
SDKs/darwin/usr/include/limits.h
Normal file
23
SDKs/darwin/usr/include/limits.h
Normal 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__ */
|
61
SDKs/darwin/usr/include/stdio.h
Normal file
61
SDKs/darwin/usr/include/stdio.h
Normal 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__ */
|
29
SDKs/darwin/usr/include/stdlib.h
Normal file
29
SDKs/darwin/usr/include/stdlib.h
Normal 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__ */
|
28
SDKs/darwin/usr/include/string.h
Normal file
28
SDKs/darwin/usr/include/string.h
Normal 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__ */
|
25
SDKs/darwin/usr/include/sys/stat.h
Normal file
25
SDKs/darwin/usr/include/sys/stat.h
Normal 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__ */
|
20
SDKs/darwin/usr/include/sys/types.h
Normal file
20
SDKs/darwin/usr/include/sys/types.h
Normal 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
2
SDKs/linux/README.txt
Normal 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.
|
29
SDKs/linux/usr/include/endian.h
Normal file
29
SDKs/linux/usr/include/endian.h
Normal 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__ */
|
23
SDKs/linux/usr/include/limits.h
Normal file
23
SDKs/linux/usr/include/limits.h
Normal 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__ */
|
35
SDKs/linux/usr/include/stdio.h
Normal file
35
SDKs/linux/usr/include/stdio.h
Normal 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__ */
|
32
SDKs/linux/usr/include/stdlib.h
Normal file
32
SDKs/linux/usr/include/stdlib.h
Normal 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__ */
|
28
SDKs/linux/usr/include/string.h
Normal file
28
SDKs/linux/usr/include/string.h
Normal 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__ */
|
29
SDKs/linux/usr/include/sys/mman.h
Normal file
29
SDKs/linux/usr/include/sys/mman.h
Normal 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__ */
|
24
SDKs/linux/usr/include/sys/stat.h
Normal file
24
SDKs/linux/usr/include/sys/stat.h
Normal 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__ */
|
20
SDKs/linux/usr/include/sys/types.h
Normal file
20
SDKs/linux/usr/include/sys/types.h
Normal 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__ */
|
26
SDKs/linux/usr/include/unistd.h
Normal file
26
SDKs/linux/usr/include/unistd.h
Normal 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__ */
|
@ -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
|
||||
|
23
lib/abi.h
23
lib/abi.h
@ -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
|
@ -11,10 +11,8 @@
|
||||
*
|
||||
*===----------------------------------------------------------------------===
|
||||
*/
|
||||
#include "abi.h"
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Returns: absolute value */
|
||||
|
||||
|
@ -11,10 +11,8 @@
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
#include "abi.h"
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Returns: absolute value */
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#if __x86_64
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Returns: absolute value */
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "abi.h"
|
||||
|
||||
#define DOUBLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "abi.h"
|
||||
|
||||
#define SINGLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
|
||||
|
@ -11,10 +11,8 @@
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
#include "abi.h"
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Returns: a + b */
|
||||
|
||||
|
@ -11,10 +11,8 @@
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
#include "abi.h"
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Returns: a + b */
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#if __x86_64
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Returns: a + b */
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := armv5 armv6 armv7
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
22
lib/asan/Makefile.mk
Normal 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
352
lib/asan/Makefile.old
Normal 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
26
lib/asan/README.txt
Normal 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
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
157
lib/asan/asan_allocator.h
Normal 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
171
lib/asan/asan_globals.cc
Normal 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]);
|
||||
}
|
||||
}
|
391
lib/asan/asan_interceptors.cc
Normal file
391
lib/asan/asan_interceptors.cc
Normal 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
|
134
lib/asan/asan_interceptors.h
Normal file
134
lib/asan/asan_interceptors.h
Normal 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
135
lib/asan/asan_interface.h
Normal 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
239
lib/asan/asan_internal.h
Normal 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
101
lib/asan/asan_linux.cc
Normal 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
100
lib/asan/asan_lock.h
Normal 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
311
lib/asan/asan_mac.cc
Normal 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
87
lib/asan/asan_mac.h
Normal 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__
|
142
lib/asan/asan_malloc_linux.cc
Normal file
142
lib/asan/asan_malloc_linux.cc
Normal 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
390
lib/asan/asan_malloc_mac.cc
Normal 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
100
lib/asan/asan_mapping.h
Normal 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
159
lib/asan/asan_poisoning.cc
Normal 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
181
lib/asan/asan_printf.cc
Normal 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
810
lib/asan/asan_rtl.cc
Normal 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
280
lib/asan/asan_stack.cc
Normal 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
94
lib/asan/asan_stack.h
Normal 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
88
lib/asan/asan_stats.cc
Normal 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
59
lib/asan/asan_stats.h
Normal 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
178
lib/asan/asan_thread.cc
Normal 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
108
lib/asan/asan_thread.h
Normal 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
|
227
lib/asan/asan_thread_registry.cc
Normal file
227
lib/asan/asan_thread_registry.cc
Normal 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
|
88
lib/asan/asan_thread_registry.h
Normal file
88
lib/asan/asan_thread_registry.h
Normal 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
|
3
lib/asan/mach_override/LICENSE.TXT
Normal file
3
lib/asan/mach_override/LICENSE.TXT
Normal 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>
|
||||
|
22
lib/asan/mach_override/Makefile.mk
Normal file
22
lib/asan/mach_override/Makefile.mk
Normal 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=%)
|
9
lib/asan/mach_override/README.txt
Normal file
9
lib/asan/mach_override/README.txt
Normal 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.
|
||||
|
862
lib/asan/mach_override/mach_override.c
Normal file
862
lib/asan/mach_override/mach_override.c
Normal 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__
|
127
lib/asan/mach_override/mach_override.h
Normal file
127
lib/asan/mach_override/mach_override.h
Normal 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
Loading…
x
Reference in New Issue
Block a user