diff options
Diffstat (limited to 'libsanitizer/sanitizer_common')
32 files changed, 5160 insertions, 0 deletions
diff --git a/libsanitizer/sanitizer_common/Makefile.am b/libsanitizer/sanitizer_common/Makefile.am new file mode 100644 index 00000000000..70df1d92709 --- /dev/null +++ b/libsanitizer/sanitizer_common/Makefile.am @@ -0,0 +1,71 @@ +AM_CPPFLAGS = -I $(top_srcdir)/include + +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions +ACLOCAL_AMFLAGS = -I m4 + +noinst_LTLIBRARIES = libsanitizer_common.la + +sanitizer_common_files = \ + sanitizer_allocator.cc \ + sanitizer_common.cc \ + sanitizer_flags.cc \ + sanitizer_libc.cc \ + sanitizer_linux.cc \ + sanitizer_mac.cc \ + sanitizer_posix.cc \ + sanitizer_printf.cc \ + sanitizer_stackdepot.cc \ + sanitizer_stacktrace.cc \ + sanitizer_symbolizer.cc \ + sanitizer_symbolizer_linux.cc \ + sanitizer_symbolizer_mac.cc \ + sanitizer_symbolizer_win.cc \ + sanitizer_win.cc + +libsanitizer_common_la_SOURCES = $(sanitizer_common_files) + +# Work around what appears to be a GNU make bug handling MAKEFLAGS +# values defined in terms of make variables, as is the case for CC and +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "CC=$(CC)" \ + "CXX=$(CXX)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES= + +## ################################################################ + diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in new file mode 100644 index 00000000000..75a83ab95b6 --- /dev/null +++ b/libsanitizer/sanitizer_common/Makefile.in @@ -0,0 +1,564 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = sanitizer_common +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libsanitizer_common_la_LIBADD = +am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \ + sanitizer_flags.lo sanitizer_libc.lo sanitizer_linux.lo \ + sanitizer_mac.lo sanitizer_posix.lo sanitizer_printf.lo \ + sanitizer_stackdepot.lo sanitizer_stacktrace.lo \ + sanitizer_symbolizer.lo sanitizer_symbolizer_linux.lo \ + sanitizer_symbolizer_mac.lo sanitizer_symbolizer_win.lo \ + sanitizer_win.lo +am_libsanitizer_common_la_OBJECTS = $(am__objects_1) +libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libsanitizer_common_la_SOURCES) +DIST_SOURCES = $(libsanitizer_common_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_shared = @enable_shared@ +enable_static = @enable_static@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +toolexecdir = @toolexecdir@ +toolexeclibdir = @toolexeclibdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I $(top_srcdir)/include +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions +ACLOCAL_AMFLAGS = -I m4 +noinst_LTLIBRARIES = libsanitizer_common.la +sanitizer_common_files = \ + sanitizer_allocator.cc \ + sanitizer_common.cc \ + sanitizer_flags.cc \ + sanitizer_libc.cc \ + sanitizer_linux.cc \ + sanitizer_mac.cc \ + sanitizer_posix.cc \ + sanitizer_printf.cc \ + sanitizer_stackdepot.cc \ + sanitizer_stacktrace.cc \ + sanitizer_symbolizer.cc \ + sanitizer_symbolizer_linux.cc \ + sanitizer_symbolizer_mac.cc \ + sanitizer_symbolizer_win.cc \ + sanitizer_win.cc + +libsanitizer_common_la_SOURCES = $(sanitizer_common_files) + +# Work around what appears to be a GNU make bug handling MAKEFLAGS +# values defined in terms of make variables, as is the case for CC and +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "CC=$(CC)" \ + "CXX=$(CXX)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES = +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign sanitizer_common/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign sanitizer_common/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libsanitizer_common.la: $(libsanitizer_common_la_OBJECTS) $(libsanitizer_common_la_DEPENDENCIES) $(EXTRA_libsanitizer_common_la_DEPENDENCIES) + $(CXXLINK) $(libsanitizer_common_la_OBJECTS) $(libsanitizer_common_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_linux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc new file mode 100644 index 00000000000..ff176a88d3a --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -0,0 +1,83 @@ +//===-- sanitizer_allocator.cc --------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// This allocator that is used inside run-times. +//===----------------------------------------------------------------------===// +#include "sanitizer_common.h" + +// FIXME: We should probably use more low-level allocator that would +// mmap some pages and split them into chunks to fulfill requests. +#if defined(__linux__) && !defined(__ANDROID__) +extern "C" void *__libc_malloc(__sanitizer::uptr size); +extern "C" void __libc_free(void *ptr); +# define LIBC_MALLOC __libc_malloc +# define LIBC_FREE __libc_free +#else // __linux__ && !ANDROID +# include <stdlib.h> +# define LIBC_MALLOC malloc +# define LIBC_FREE free +#endif // __linux__ && !ANDROID + +namespace __sanitizer { + +const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; + +void *InternalAlloc(uptr size) { + if (size + sizeof(u64) < size) + return 0; + void *p = LIBC_MALLOC(size + sizeof(u64)); + if (p == 0) + return 0; + ((u64*)p)[0] = kBlockMagic; + return (char*)p + sizeof(u64); +} + +void InternalFree(void *addr) { + if (addr == 0) + return; + addr = (char*)addr - sizeof(u64); + CHECK_EQ(((u64*)addr)[0], kBlockMagic); + ((u64*)addr)[0] = 0; + LIBC_FREE(addr); +} + +void *InternalAllocBlock(void *p) { + CHECK_NE(p, (void*)0); + u64 *pp = (u64*)((uptr)p & ~0x7); + for (; pp[0] != kBlockMagic; pp--) {} + return pp + 1; +} + +// LowLevelAllocator +static LowLevelAllocateCallback low_level_alloc_callback; + +void *LowLevelAllocator::Allocate(uptr size) { + // Align allocation size. + size = RoundUpTo(size, 8); + if (allocated_end_ - allocated_current_ < (sptr)size) { + uptr size_to_allocate = Max(size, kPageSize); + allocated_current_ = + (char*)MmapOrDie(size_to_allocate, __FUNCTION__); + allocated_end_ = allocated_current_ + size_to_allocate; + if (low_level_alloc_callback) { + low_level_alloc_callback((uptr)allocated_current_, + size_to_allocate); + } + } + CHECK(allocated_end_ - allocated_current_ >= (sptr)size); + void *res = allocated_current_; + allocated_current_ += size; + return res; +} + +void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { + low_level_alloc_callback = callback; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator64.h b/libsanitizer/sanitizer_common/sanitizer_allocator64.h new file mode 100644 index 00000000000..247719876aa --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_allocator64.h @@ -0,0 +1,573 @@ +//===-- sanitizer_allocator64.h ---------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Specialized allocator which works only in 64-bit address space. +// To be used by ThreadSanitizer, MemorySanitizer and possibly other tools. +// The main feature of this allocator is that the header is located far away +// from the user memory region, so that the tool does not use extra shadow +// for the header. +// +// Status: not yet ready. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_ALLOCATOR_H +#define SANITIZER_ALLOCATOR_H + +#include "sanitizer_internal_defs.h" +#if __WORDSIZE != 64 +# error "sanitizer_allocator64.h can only be used on 64-bit platforms" +#endif + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_list.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +// Maps size class id to size and back. +class DefaultSizeClassMap { + private: + // Here we use a spline composed of 5 polynomials of oder 1. + // The first size class is l0, then the classes go with step s0 + // untill they reach l1, after which they go with step s1 and so on. + // Steps should be powers of two for cheap division. + // The size of the last size class should be a power of two. + // There should be at most 256 size classes. + static const uptr l0 = 1 << 4; + static const uptr l1 = 1 << 9; + static const uptr l2 = 1 << 12; + static const uptr l3 = 1 << 15; + static const uptr l4 = 1 << 18; + static const uptr l5 = 1 << 21; + + static const uptr s0 = 1 << 4; + static const uptr s1 = 1 << 6; + static const uptr s2 = 1 << 9; + static const uptr s3 = 1 << 12; + static const uptr s4 = 1 << 15; + + static const uptr u0 = 0 + (l1 - l0) / s0; + static const uptr u1 = u0 + (l2 - l1) / s1; + static const uptr u2 = u1 + (l3 - l2) / s2; + static const uptr u3 = u2 + (l4 - l3) / s3; + static const uptr u4 = u3 + (l5 - l4) / s4; + + // Max cached in local cache blocks. + static const uptr c0 = 256; + static const uptr c1 = 64; + static const uptr c2 = 16; + static const uptr c3 = 4; + static const uptr c4 = 1; + + public: + static const uptr kNumClasses = u4 + 1; + static const uptr kMaxSize = l5; + static const uptr kMinSize = l0; + + COMPILER_CHECK(kNumClasses <= 256); + COMPILER_CHECK((kMaxSize & (kMaxSize - 1)) == 0); + + static uptr Size(uptr class_id) { + if (class_id <= u0) return l0 + s0 * (class_id - 0); + if (class_id <= u1) return l1 + s1 * (class_id - u0); + if (class_id <= u2) return l2 + s2 * (class_id - u1); + if (class_id <= u3) return l3 + s3 * (class_id - u2); + if (class_id <= u4) return l4 + s4 * (class_id - u3); + return 0; + } + static uptr ClassID(uptr size) { + if (size <= l1) return 0 + (size - l0 + s0 - 1) / s0; + if (size <= l2) return u0 + (size - l1 + s1 - 1) / s1; + if (size <= l3) return u1 + (size - l2 + s2 - 1) / s2; + if (size <= l4) return u2 + (size - l3 + s3 - 1) / s3; + if (size <= l5) return u3 + (size - l4 + s4 - 1) / s4; + return 0; + } + + static uptr MaxCached(uptr class_id) { + if (class_id <= u0) return c0; + if (class_id <= u1) return c1; + if (class_id <= u2) return c2; + if (class_id <= u3) return c3; + if (class_id <= u4) return c4; + return 0; + } +}; + +struct AllocatorListNode { + AllocatorListNode *next; +}; + +typedef IntrusiveList<AllocatorListNode> AllocatorFreeList; + + +// Space: a portion of address space of kSpaceSize bytes starting at +// a fixed address (kSpaceBeg). Both constants are powers of two and +// kSpaceBeg is kSpaceSize-aligned. +// +// Region: a part of Space dedicated to a single size class. +// There are kNumClasses Regions of equal size. +// +// UserChunk: a piece of memory returned to user. +// MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk. +// +// A Region looks like this: +// UserChunk1 ... UserChunkN <gap> MetaChunkN ... MetaChunk1 +template <const uptr kSpaceBeg, const uptr kSpaceSize, + const uptr kMetadataSize, class SizeClassMap> +class SizeClassAllocator64 { + public: + void Init() { + CHECK_EQ(AllocBeg(), reinterpret_cast<uptr>(MmapFixedNoReserve( + AllocBeg(), AllocSize()))); + } + + bool CanAllocate(uptr size, uptr alignment) { + return size <= SizeClassMap::kMaxSize && + alignment <= SizeClassMap::kMaxSize; + } + + void *Allocate(uptr size, uptr alignment) { + CHECK(CanAllocate(size, alignment)); + return AllocateBySizeClass(SizeClassMap::ClassID(size)); + } + + void Deallocate(void *p) { + CHECK(PointerIsMine(p)); + DeallocateBySizeClass(p, GetSizeClass(p)); + } + + // Allocate several chunks of the given class_id. + void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + if (region->free_list.empty()) { + PopulateFreeList(class_id, region); + } + CHECK(!region->free_list.empty()); + uptr count = SizeClassMap::MaxCached(class_id); + if (region->free_list.size() <= count) { + free_list->append_front(®ion->free_list); + } else { + for (uptr i = 0; i < count; i++) { + AllocatorListNode *node = region->free_list.front(); + region->free_list.pop_front(); + free_list->push_front(node); + } + } + CHECK(!free_list->empty()); + } + + // Swallow the entire free_list for the given class_id. + void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + region->free_list.append_front(free_list); + } + + static bool PointerIsMine(void *p) { + return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize; + } + + static uptr GetSizeClass(void *p) { + return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClasses; + } + + static void *GetBlockBegin(void *p) { + uptr class_id = GetSizeClass(p); + uptr size = SizeClassMap::Size(class_id); + uptr chunk_idx = GetChunkIdx((uptr)p, size); + uptr reg_beg = (uptr)p & ~(kRegionSize - 1); + uptr begin = reg_beg + chunk_idx * size; + return (void*)begin; + } + + static uptr GetActuallyAllocatedSize(void *p) { + CHECK(PointerIsMine(p)); + return SizeClassMap::Size(GetSizeClass(p)); + } + + uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } + + void *GetMetaData(void *p) { + uptr class_id = GetSizeClass(p); + uptr size = SizeClassMap::Size(class_id); + uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), size); + return reinterpret_cast<void*>(kSpaceBeg + (kRegionSize * (class_id + 1)) - + (1 + chunk_idx) * kMetadataSize); + } + + uptr TotalMemoryUsed() { + uptr res = 0; + for (uptr i = 0; i < kNumClasses; i++) + res += GetRegionInfo(i)->allocated_user; + return res; + } + + // Test-only. + void TestOnlyUnmap() { + UnmapOrDie(reinterpret_cast<void*>(AllocBeg()), AllocSize()); + } + + static uptr AllocBeg() { return kSpaceBeg; } + static uptr AllocEnd() { return kSpaceBeg + kSpaceSize + AdditionalSize(); } + static uptr AllocSize() { return kSpaceSize + AdditionalSize(); } + + static const uptr kNumClasses = 256; // Power of two <= 256 + typedef SizeClassMap SizeClassMapT; + + private: + COMPILER_CHECK(kSpaceBeg % kSpaceSize == 0); + COMPILER_CHECK(kNumClasses <= SizeClassMap::kNumClasses); + static const uptr kRegionSize = kSpaceSize / kNumClasses; + COMPILER_CHECK((kRegionSize >> 32) > 0); // kRegionSize must be >= 2^32. + // Populate the free list with at most this number of bytes at once + // or with one element if its size is greater. + static const uptr kPopulateSize = 1 << 18; + + struct RegionInfo { + SpinMutex mutex; + AllocatorFreeList free_list; + uptr allocated_user; // Bytes allocated for user memory. + uptr allocated_meta; // Bytes allocated for metadata. + char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(AllocatorFreeList)]; + }; + COMPILER_CHECK(sizeof(RegionInfo) == kCacheLineSize); + + static uptr AdditionalSize() { + uptr res = sizeof(RegionInfo) * kNumClasses; + CHECK_EQ(res % kPageSize, 0); + return res; + } + + RegionInfo *GetRegionInfo(uptr class_id) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg + kSpaceSize); + return ®ions[class_id]; + } + + static uptr GetChunkIdx(uptr chunk, uptr size) { + u32 offset = chunk % kRegionSize; + // Here we divide by a non-constant. This is costly. + // We require that kRegionSize is at least 2^32 so that offset is 32-bit. + // We save 2x by using 32-bit div, but may need to use a 256-way switch. + return offset / (u32)size; + } + + void PopulateFreeList(uptr class_id, RegionInfo *region) { + uptr size = SizeClassMap::Size(class_id); + uptr beg_idx = region->allocated_user; + uptr end_idx = beg_idx + kPopulateSize; + region->free_list.clear(); + uptr region_beg = kSpaceBeg + kRegionSize * class_id; + uptr idx = beg_idx; + uptr i = 0; + do { // do-while loop because we need to put at least one item. + uptr p = region_beg + idx; + region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p)); + idx += size; + i++; + } while (idx < end_idx); + region->allocated_user += idx - beg_idx; + region->allocated_meta += i * kMetadataSize; + CHECK_LT(region->allocated_user + region->allocated_meta, kRegionSize); + } + + void *AllocateBySizeClass(uptr class_id) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + if (region->free_list.empty()) { + PopulateFreeList(class_id, region); + } + CHECK(!region->free_list.empty()); + AllocatorListNode *node = region->free_list.front(); + region->free_list.pop_front(); + return reinterpret_cast<void*>(node); + } + + void DeallocateBySizeClass(void *p, uptr class_id) { + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p)); + } +}; + +// Objects of this type should be used as local caches for SizeClassAllocator64. +// Since the typical use of this class is to have one object per thread in TLS, +// is has to be POD. +template<const uptr kNumClasses, class SizeClassAllocator> +struct SizeClassAllocatorLocalCache { + // Don't need to call Init if the object is a global (i.e. zero-initialized). + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void *Allocate(SizeClassAllocator *allocator, uptr class_id) { + CHECK_LT(class_id, kNumClasses); + AllocatorFreeList *free_list = &free_lists_[class_id]; + if (free_list->empty()) + allocator->BulkAllocate(class_id, free_list); + CHECK(!free_list->empty()); + void *res = free_list->front(); + free_list->pop_front(); + return res; + } + + void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { + CHECK_LT(class_id, kNumClasses); + AllocatorFreeList *free_list = &free_lists_[class_id]; + free_list->push_front(reinterpret_cast<AllocatorListNode*>(p)); + if (free_list->size() >= 2 * SizeClassMap::MaxCached(class_id)) + DrainHalf(allocator, class_id); + } + + void Drain(SizeClassAllocator *allocator) { + for (uptr i = 0; i < kNumClasses; i++) { + allocator->BulkDeallocate(i, &free_lists_[i]); + CHECK(free_lists_[i].empty()); + } + } + + // private: + typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap; + AllocatorFreeList free_lists_[kNumClasses]; + + void DrainHalf(SizeClassAllocator *allocator, uptr class_id) { + AllocatorFreeList *free_list = &free_lists_[class_id]; + AllocatorFreeList half; + half.clear(); + const uptr count = free_list->size() / 2; + for (uptr i = 0; i < count; i++) { + AllocatorListNode *node = free_list->front(); + free_list->pop_front(); + half.push_front(node); + } + allocator->BulkDeallocate(class_id, &half); + } +}; + +// This class can (de)allocate only large chunks of memory using mmap/unmap. +// The main purpose of this allocator is to cover large and rare allocation +// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64). +class LargeMmapAllocator { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + void *Allocate(uptr size, uptr alignment) { + CHECK(IsPowerOfTwo(alignment)); + uptr map_size = RoundUpMapSize(size); + if (alignment > kPageSize) + map_size += alignment; + if (map_size < size) return 0; // Overflow. + uptr map_beg = reinterpret_cast<uptr>( + MmapOrDie(map_size, "LargeMmapAllocator")); + uptr map_end = map_beg + map_size; + uptr res = map_beg + kPageSize; + if (res & (alignment - 1)) // Align. + res += alignment - (res & (alignment - 1)); + CHECK_EQ(0, res & (alignment - 1)); + CHECK_LE(res + size, map_end); + Header *h = GetHeader(res); + h->size = size; + h->map_beg = map_beg; + h->map_size = map_size; + { + SpinMutexLock l(&mutex_); + h->next = list_; + h->prev = 0; + if (list_) + list_->prev = h; + list_ = h; + } + return reinterpret_cast<void*>(res); + } + + void Deallocate(void *p) { + Header *h = GetHeader(p); + { + SpinMutexLock l(&mutex_); + Header *prev = h->prev; + Header *next = h->next; + if (prev) + prev->next = next; + if (next) + next->prev = prev; + if (h == list_) + list_ = next; + } + UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size); + } + + uptr TotalMemoryUsed() { + SpinMutexLock l(&mutex_); + uptr res = 0; + for (Header *l = list_; l; l = l->next) { + res += RoundUpMapSize(l->size); + } + return res; + } + + bool PointerIsMine(void *p) { + // Fast check. + if ((reinterpret_cast<uptr>(p) % kPageSize) != 0) return false; + SpinMutexLock l(&mutex_); + for (Header *l = list_; l; l = l->next) { + if (GetUser(l) == p) return true; + } + return false; + } + + uptr GetActuallyAllocatedSize(void *p) { + return RoundUpMapSize(GetHeader(p)->size) - kPageSize; + } + + // At least kPageSize/2 metadata bytes is available. + void *GetMetaData(void *p) { + return GetHeader(p) + 1; + } + + void *GetBlockBegin(void *p) { + SpinMutexLock l(&mutex_); + for (Header *l = list_; l; l = l->next) { + void *b = GetUser(l); + if (p >= b && p < (u8*)b + l->size) + return b; + } + return 0; + } + + private: + struct Header { + uptr map_beg; + uptr map_size; + uptr size; + Header *next; + Header *prev; + }; + + Header *GetHeader(uptr p) { return reinterpret_cast<Header*>(p - kPageSize); } + Header *GetHeader(void *p) { return GetHeader(reinterpret_cast<uptr>(p)); } + + void *GetUser(Header *h) { + return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + kPageSize); + } + + uptr RoundUpMapSize(uptr size) { + return RoundUpTo(size, kPageSize) + kPageSize; + } + + Header *list_; + SpinMutex mutex_; +}; + +// This class implements a complete memory allocator by using two +// internal allocators: +// PrimaryAllocator is efficient, but may not allocate some sizes (alignments). +// When allocating 2^x bytes it should return 2^x aligned chunk. +// PrimaryAllocator is used via a local AllocatorCache. +// SecondaryAllocator can allocate anything, but is not efficient. +template <class PrimaryAllocator, class AllocatorCache, + class SecondaryAllocator> // NOLINT +class CombinedAllocator { + public: + void Init() { + primary_.Init(); + secondary_.Init(); + } + + void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, + bool cleared = false) { + // Returning 0 on malloc(0) may break a lot of code. + if (size == 0) + size = 1; + if (size + alignment < size) + return 0; + if (alignment > 8) + size = RoundUpTo(size, alignment); + void *res; + if (primary_.CanAllocate(size, alignment)) + res = cache->Allocate(&primary_, primary_.ClassID(size)); + else + res = secondary_.Allocate(size, alignment); + if (alignment > 8) + CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0); + if (cleared && res) + internal_memset(res, 0, size); + return res; + } + + void Deallocate(AllocatorCache *cache, void *p) { + if (!p) return; + if (primary_.PointerIsMine(p)) + cache->Deallocate(&primary_, primary_.GetSizeClass(p), p); + else + secondary_.Deallocate(p); + } + + void *Reallocate(AllocatorCache *cache, void *p, uptr new_size, + uptr alignment) { + if (!p) + return Allocate(cache, new_size, alignment); + if (!new_size) { + Deallocate(cache, p); + return 0; + } + CHECK(PointerIsMine(p)); + uptr old_size = GetActuallyAllocatedSize(p); + uptr memcpy_size = Min(new_size, old_size); + void *new_p = Allocate(cache, new_size, alignment); + if (new_p) + internal_memcpy(new_p, p, memcpy_size); + Deallocate(cache, p); + return new_p; + } + + bool PointerIsMine(void *p) { + if (primary_.PointerIsMine(p)) + return true; + return secondary_.PointerIsMine(p); + } + + void *GetMetaData(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetMetaData(p); + return secondary_.GetMetaData(p); + } + + void *GetBlockBegin(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetBlockBegin(p); + return secondary_.GetBlockBegin(p); + } + + uptr GetActuallyAllocatedSize(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetActuallyAllocatedSize(p); + return secondary_.GetActuallyAllocatedSize(p); + } + + uptr TotalMemoryUsed() { + return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed(); + } + + void TestOnlyUnmap() { primary_.TestOnlyUnmap(); } + + void SwallowCache(AllocatorCache *cache) { + cache->Drain(&primary_); + } + + private: + PrimaryAllocator primary_; + SecondaryAllocator secondary_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_ALLOCATOR_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic.h b/libsanitizer/sanitizer_common/sanitizer_atomic.h new file mode 100644 index 00000000000..f2bf23588a4 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_atomic.h @@ -0,0 +1,63 @@ +//===-- sanitizer_atomic.h --------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ATOMIC_H +#define SANITIZER_ATOMIC_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +enum memory_order { + memory_order_relaxed = 1 << 0, + memory_order_consume = 1 << 1, + memory_order_acquire = 1 << 2, + memory_order_release = 1 << 3, + memory_order_acq_rel = 1 << 4, + memory_order_seq_cst = 1 << 5 +}; + +struct atomic_uint8_t { + typedef u8 Type; + volatile Type val_dont_use; +}; + +struct atomic_uint16_t { + typedef u16 Type; + volatile Type val_dont_use; +}; + +struct atomic_uint32_t { + typedef u32 Type; + volatile Type val_dont_use; +}; + +struct atomic_uint64_t { + typedef u64 Type; + volatile Type val_dont_use; +}; + +struct atomic_uintptr_t { + typedef uptr Type; + volatile Type val_dont_use; +}; + +} // namespace __sanitizer + +#if defined(__GNUC__) +# include "sanitizer_atomic_clang.h" +#elif defined(_MSC_VER) +# include "sanitizer_atomic_msvc.h" +#else +# error "Unsupported compiler" +#endif + +#endif // SANITIZER_ATOMIC_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h new file mode 100644 index 00000000000..68e79f6a2f1 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h @@ -0,0 +1,120 @@ +//===-- sanitizer_atomic_clang.h --------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Not intended for direct inclusion. Include sanitizer_atomic.h. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ATOMIC_CLANG_H +#define SANITIZER_ATOMIC_CLANG_H + +namespace __sanitizer { + +INLINE void atomic_signal_fence(memory_order) { + __asm__ __volatile__("" ::: "memory"); +} + +INLINE void atomic_thread_fence(memory_order) { + __sync_synchronize(); +} + +INLINE void proc_yield(int cnt) { + __asm__ __volatile__("" ::: "memory"); +#if defined(__i386__) || defined(__x86_64__) + for (int i = 0; i < cnt; i++) + __asm__ __volatile__("pause"); +#endif + __asm__ __volatile__("" ::: "memory"); +} + +template<typename T> +INLINE typename T::Type atomic_load( + const volatile T *a, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_consume + | memory_order_acquire | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + typename T::Type v; + if (mo == memory_order_relaxed) { + v = a->val_dont_use; + } else { + atomic_signal_fence(memory_order_seq_cst); + v = a->val_dont_use; + atomic_signal_fence(memory_order_seq_cst); + } + return v; +} + +template<typename T> +INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_release + | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + if (mo == memory_order_relaxed) { + a->val_dont_use = v; + } else { + atomic_signal_fence(memory_order_seq_cst); + a->val_dont_use = v; + atomic_signal_fence(memory_order_seq_cst); + } + if (mo == memory_order_seq_cst) + atomic_thread_fence(memory_order_seq_cst); +} + +template<typename T> +INLINE typename T::Type atomic_fetch_add(volatile T *a, + typename T::Type v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return __sync_fetch_and_add(&a->val_dont_use, v); +} + +template<typename T> +INLINE typename T::Type atomic_fetch_sub(volatile T *a, + typename T::Type v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return __sync_fetch_and_add(&a->val_dont_use, -v); +} + +template<typename T> +INLINE typename T::Type atomic_exchange(volatile T *a, + typename T::Type v, memory_order mo) { + DCHECK(!((uptr)a % sizeof(*a))); + if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst)) + __sync_synchronize(); + v = __sync_lock_test_and_set(&a->val_dont_use, v); + if (mo == memory_order_seq_cst) + __sync_synchronize(); + return v; +} + +template<typename T> +INLINE bool atomic_compare_exchange_strong(volatile T *a, + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { + typedef typename T::Type Type; + Type cmpv = *cmp; + Type prev = __sync_val_compare_and_swap(&a->val_dont_use, cmpv, xchg); + if (prev == cmpv) + return true; + *cmp = prev; + return false; +} + +template<typename T> +INLINE bool atomic_compare_exchange_weak(volatile T *a, + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { + return atomic_compare_exchange_strong(a, cmp, xchg, mo); +} + +} // namespace __sanitizer + +#endif // SANITIZER_ATOMIC_CLANG_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h new file mode 100644 index 00000000000..2c02baa954a --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h @@ -0,0 +1,134 @@ +//===-- sanitizer_atomic_msvc.h ---------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Not intended for direct inclusion. Include sanitizer_atomic.h. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ATOMIC_MSVC_H +#define SANITIZER_ATOMIC_MSVC_H + +extern "C" void _ReadWriteBarrier(); +#pragma intrinsic(_ReadWriteBarrier) +extern "C" void _mm_mfence(); +#pragma intrinsic(_mm_mfence) +extern "C" void _mm_pause(); +#pragma intrinsic(_mm_pause) +extern "C" long _InterlockedExchangeAdd( // NOLINT + long volatile * Addend, long Value); // NOLINT +#pragma intrinsic(_InterlockedExchangeAdd) +extern "C" void *InterlockedCompareExchangePointer( + void *volatile *Destination, + void *Exchange, void *Comparand); + +namespace __sanitizer { + +INLINE void atomic_signal_fence(memory_order) { + _ReadWriteBarrier(); +} + +INLINE void atomic_thread_fence(memory_order) { + _mm_mfence(); +} + +INLINE void proc_yield(int cnt) { + for (int i = 0; i < cnt; i++) + _mm_pause(); +} + +template<typename T> +INLINE typename T::Type atomic_load( + const volatile T *a, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_consume + | memory_order_acquire | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + typename T::Type v; + if (mo == memory_order_relaxed) { + v = a->val_dont_use; + } else { + atomic_signal_fence(memory_order_seq_cst); + v = a->val_dont_use; + atomic_signal_fence(memory_order_seq_cst); + } + return v; +} + +template<typename T> +INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_release + | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + if (mo == memory_order_relaxed) { + a->val_dont_use = v; + } else { + atomic_signal_fence(memory_order_seq_cst); + a->val_dont_use = v; + atomic_signal_fence(memory_order_seq_cst); + } + if (mo == memory_order_seq_cst) + atomic_thread_fence(memory_order_seq_cst); +} + +INLINE u32 atomic_fetch_add(volatile atomic_uint32_t *a, + u32 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return (u32)_InterlockedExchangeAdd( + (volatile long*)&a->val_dont_use, (long)v); // NOLINT +} + +INLINE u8 atomic_exchange(volatile atomic_uint8_t *a, + u8 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + __asm { + mov eax, a + mov cl, v + xchg [eax], cl // NOLINT + mov v, cl + } + return v; +} + +INLINE u16 atomic_exchange(volatile atomic_uint16_t *a, + u16 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + __asm { + mov eax, a + mov cx, v + xchg [eax], cx // NOLINT + mov v, cx + } + return v; +} + +INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a, + uptr *cmp, + uptr xchg, + memory_order mo) { + uptr cmpv = *cmp; + uptr prev = (uptr)InterlockedCompareExchangePointer( + (void*volatile*)&a->val_dont_use, (void*)xchg, (void*)cmpv); + if (prev == cmpv) + return true; + *cmp = prev; + return false; +} + +template<typename T> +INLINE bool atomic_compare_exchange_weak(volatile T *a, + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { + return atomic_compare_exchange_strong(a, cmp, xchg, mo); +} + +} // namespace __sanitizer + +#endif // SANITIZER_ATOMIC_CLANG_H diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc new file mode 100644 index 00000000000..43ef980e846 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -0,0 +1,151 @@ +//===-- sanitizer_common.cc -----------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +static fd_t report_fd = 2; // By default, dump to stderr. +static char report_path[4096]; // Set via __sanitizer_set_report_path. + +static void (*DieCallback)(void); +void SetDieCallback(void (*callback)(void)) { + DieCallback = callback; +} + +void NORETURN Die() { + if (DieCallback) { + DieCallback(); + } + Exit(1); +} + +static CheckFailedCallbackType CheckFailedCallback; +void SetCheckFailedCallback(CheckFailedCallbackType callback) { + CheckFailedCallback = callback; +} + +void NORETURN CheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + if (CheckFailedCallback) { + CheckFailedCallback(file, line, cond, v1, v2); + } + Report("Sanitizer CHECK failed: %s:%d %s (%zd, %zd)\n", file, line, cond, + v1, v2); + Die(); +} + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = "RawWrite can't output requested buffer!"; + uptr length = (uptr)internal_strlen(buffer); + if (report_fd == kInvalidFd) { + fd_t fd = internal_open(report_path, true); + if (fd == kInvalidFd) { + report_fd = 2; + Report("ERROR: Can't open file: %s\n", report_path); + Die(); + } + report_fd = fd; + } + if (length != internal_write(report_fd, buffer, length)) { + internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError)); + Die(); + } +} + +uptr ReadFileToBuffer(const char *file_name, char **buff, + uptr *buff_size, uptr max_len) { + const uptr kMinFileLen = kPageSize; + uptr read_len = 0; + *buff = 0; + *buff_size = 0; + // The files we usually open are not seekable, so try different buffer sizes. + for (uptr size = kMinFileLen; size <= max_len; size *= 2) { + fd_t fd = internal_open(file_name, /*write*/ false); + if (fd == kInvalidFd) return 0; + UnmapOrDie(*buff, *buff_size); + *buff = (char*)MmapOrDie(size, __FUNCTION__); + *buff_size = size; + // Read up to one page at a time. + read_len = 0; + bool reached_eof = false; + while (read_len + kPageSize <= size) { + uptr just_read = internal_read(fd, *buff + read_len, kPageSize); + if (just_read == 0) { + reached_eof = true; + break; + } + read_len += just_read; + } + internal_close(fd); + if (reached_eof) // We've read the whole file. + break; + } + return read_len; +} + +// We don't want to use std::sort to avoid including <algorithm>, as +// we may end up with two implementation of std::sort - one in instrumented +// code, and the other in runtime. +// qsort() from stdlib won't work as it calls malloc(), which results +// in deadlock in ASan allocator. +// We re-implement in-place sorting w/o recursion as straightforward heapsort. +void SortArray(uptr *array, uptr size) { + if (size < 2) + return; + // Stage 1: insert elements to the heap. + for (uptr i = 1; i < size; i++) { + uptr j, p; + for (j = i; j > 0; j = p) { + p = (j - 1) / 2; + if (array[j] > array[p]) + Swap(array[j], array[p]); + else + break; + } + } + // Stage 2: swap largest element with the last one, + // and sink the new top. + for (uptr i = size - 1; i > 0; i--) { + Swap(array[0], array[i]); + uptr j, max_ind; + for (j = 0; j < i; j = max_ind) { + uptr left = 2 * j + 1; + uptr right = 2 * j + 2; + max_ind = j; + if (left < i && array[left] > array[max_ind]) + max_ind = left; + if (right < i && array[right] > array[max_ind]) + max_ind = right; + if (max_ind != j) + Swap(array[j], array[max_ind]); + else + break; + } + } +} + +} // namespace __sanitizer + +void __sanitizer_set_report_path(const char *path) { + if (!path) return; + uptr len = internal_strlen(path); + if (len > sizeof(__sanitizer::report_path) - 100) { + Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", + path[0], path[1], path[2], path[3], + path[4], path[5], path[6], path[7]); + Die(); + } + internal_snprintf(__sanitizer::report_path, + sizeof(__sanitizer::report_path), "%s.%d", path, GetPid()); + __sanitizer::report_fd = kInvalidFd; +} diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h new file mode 100644 index 00000000000..cddefd7ea09 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -0,0 +1,181 @@ +//===-- sanitizer_common.h --------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// It declares common functions and classes that are used in both runtimes. +// Implementation of some functions are provided in sanitizer_common, while +// others must be defined by run-time library itself. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_COMMON_H +#define SANITIZER_COMMON_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// Constants. +const uptr kWordSize = __WORDSIZE / 8; +const uptr kWordSizeInBits = 8 * kWordSize; +const uptr kPageSizeBits = 12; +const uptr kPageSize = 1UL << kPageSizeBits; +const uptr kCacheLineSize = 64; +#ifndef _WIN32 +const uptr kMmapGranularity = kPageSize; +#else +const uptr kMmapGranularity = 1UL << 16; +#endif + +// Threads +int GetPid(); +uptr GetTid(); +uptr GetThreadSelf(); +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom); + +// Memory management +void *MmapOrDie(uptr size, const char *mem_type); +void UnmapOrDie(void *addr, uptr size); +void *MmapFixedNoReserve(uptr fixed_addr, uptr size); +void *Mprotect(uptr fixed_addr, uptr size); +// Used to check if we can map shadow memory to a fixed location. +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); + +// Internal allocator +void *InternalAlloc(uptr size); +void InternalFree(void *p); +// Given the pointer p into a valid allocated block, +// returns a pointer to the beginning of the block. +void *InternalAllocBlock(void *p); + +// InternalScopedBuffer can be used instead of large stack arrays to +// keep frame size low. +// FIXME: use InternalAlloc instead of MmapOrDie once +// InternalAlloc is made libc-free. +template<typename T> +class InternalScopedBuffer { + public: + explicit InternalScopedBuffer(uptr cnt) { + cnt_ = cnt; + ptr_ = (T*)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer"); + } + ~InternalScopedBuffer() { + UnmapOrDie(ptr_, cnt_ * sizeof(T)); + } + T &operator[](uptr i) { return ptr_[i]; } + T *data() { return ptr_; } + uptr size() { return cnt_ * sizeof(T); } + + private: + T *ptr_; + uptr cnt_; + // Disallow evil constructors. + InternalScopedBuffer(const InternalScopedBuffer&); + void operator=(const InternalScopedBuffer&); +}; + +// Simple low-level (mmap-based) allocator for internal use. Doesn't have +// constructor, so all instances of LowLevelAllocator should be +// linker initialized. +class LowLevelAllocator { + public: + // Requires an external lock. + void *Allocate(uptr size); + private: + char *allocated_end_; + char *allocated_current_; +}; +typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size); +// Allows to register tool-specific callbacks for LowLevelAllocator. +// Passing NULL removes the callback. +void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); + +// IO +void RawWrite(const char *buffer); +void Printf(const char *format, ...); +void Report(const char *format, ...); +void SetPrintfAndReportCallback(void (*callback)(const char *)); + +// Opens the file 'file_name" and reads up to 'max_len' bytes. +// The resulting buffer is mmaped and stored in '*buff'. +// The size of the mmaped region is stored in '*buff_size', +// Returns the number of read bytes or 0 if file can not be opened. +uptr ReadFileToBuffer(const char *file_name, char **buff, + uptr *buff_size, uptr max_len); +// Maps given file to virtual memory, and returns pointer to it +// (or NULL if the mapping failes). Stores the size of mmaped region +// in '*buff_size'. +void *MapFileToMemory(const char *file_name, uptr *buff_size); + +// OS +void DisableCoreDumper(); +void DumpProcessMap(); +const char *GetEnv(const char *name); +const char *GetPwd(); +void ReExec(); +bool StackSizeIsUnlimited(); +void SetStackSizeLimitInBytes(uptr limit); + +// Other +void SleepForSeconds(int seconds); +void SleepForMillis(int millis); +int Atexit(void (*function)(void)); +void SortArray(uptr *array, uptr size); + +// Exit +void NORETURN Abort(); +void NORETURN Exit(int exitcode); +void NORETURN Die(); +void NORETURN SANITIZER_INTERFACE_ATTRIBUTE +CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); + +// Specific tools may override behavior of "Die" and "CheckFailed" functions +// to do tool-specific job. +void SetDieCallback(void (*callback)(void)); +typedef void (*CheckFailedCallbackType)(const char *, int, const char *, + u64, u64); +void SetCheckFailedCallback(CheckFailedCallbackType callback); + +// Math +INLINE bool IsPowerOfTwo(uptr x) { + return (x & (x - 1)) == 0; +} +INLINE uptr RoundUpTo(uptr size, uptr boundary) { + CHECK(IsPowerOfTwo(boundary)); + return (size + boundary - 1) & ~(boundary - 1); +} +// Don't use std::min, std::max or std::swap, 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; } +template<class T> void Swap(T& a, T& b) { + T tmp = a; + a = b; + b = tmp; +} + +// Char handling +INLINE bool IsSpace(int c) { + return (c == ' ') || (c == '\n') || (c == '\t') || + (c == '\f') || (c == '\r') || (c == '\v'); +} +INLINE bool IsDigit(int c) { + return (c >= '0') && (c <= '9'); +} +INLINE int ToLower(int c) { + return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; +} + +#if __WORDSIZE == 64 +# define FIRST_32_SECOND_64(a, b) (b) +#else +# define FIRST_32_SECOND_64(a, b) (a) +#endif + +} // namespace __sanitizer + +#endif // SANITIZER_COMMON_H diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.cc b/libsanitizer/sanitizer_common/sanitizer_flags.cc new file mode 100644 index 00000000000..837738ceb81 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_flags.cc @@ -0,0 +1,95 @@ +//===-- sanitizer_flags.cc ------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_flags.h" + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +static bool GetFlagValue(const char *env, const char *name, + const char **value, int *value_length) { + if (env == 0) + return false; + const char *pos = internal_strstr(env, name); + const char *end; + if (pos == 0) + return false; + pos += internal_strlen(name); + if (pos[0] != '=') { + end = pos; + } else { + pos += 1; + if (pos[0] == '"') { + pos += 1; + end = internal_strchr(pos, '"'); + } else if (pos[0] == '\'') { + pos += 1; + end = internal_strchr(pos, '\''); + } else { + end = internal_strchr(pos, ' '); + } + if (end == 0) + end = pos + internal_strlen(pos); + } + *value = pos; + *value_length = end - pos; + return true; +} + +static bool StartsWith(const char *flag, int flag_length, const char *value) { + if (!flag || !value) + return false; + int value_length = internal_strlen(value); + return (flag_length >= value_length) && + (0 == internal_strncmp(flag, value, value_length)); +} + +void ParseFlag(const char *env, bool *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + if (StartsWith(value, value_length, "0") || + StartsWith(value, value_length, "no") || + StartsWith(value, value_length, "false")) + *flag = false; + if (StartsWith(value, value_length, "1") || + StartsWith(value, value_length, "yes") || + StartsWith(value, value_length, "true")) + *flag = true; +} + +void ParseFlag(const char *env, int *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + *flag = internal_atoll(value); +} + +static LowLevelAllocator allocator_for_flags; + +void ParseFlag(const char *env, const char **flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + // Copy the flag value. Don't use locks here, as flags are parsed at + // tool startup. + char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1)); + internal_memcpy(value_copy, value, value_length); + value_copy[value_length] = '\0'; + *flag = value_copy; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.h b/libsanitizer/sanitizer_common/sanitizer_flags.h new file mode 100644 index 00000000000..1b9bf5bffdb --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_flags.h @@ -0,0 +1,25 @@ +//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_FLAGS_H +#define SANITIZER_FLAGS_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +void ParseFlag(const char *env, bool *flag, const char *name); +void ParseFlag(const char *env, int *flag, const char *name); +void ParseFlag(const char *env, const char **flag, const char *name); + +} // namespace __sanitizer + +#endif // SANITIZER_FLAGS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h new file mode 100644 index 00000000000..da4d049e2c6 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -0,0 +1,186 @@ +//===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// It contains macro used in run-time libraries code. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_DEFS_H +#define SANITIZER_DEFS_H + +#include "sanitizer/common_interface_defs.h" +using namespace __sanitizer; // NOLINT +// ----------- ATTENTION ------------- +// This header should NOT include any other headers to avoid portability issues. + +// Common defs. +#define INLINE static inline +#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#define WEAK SANITIZER_WEAK_ATTRIBUTE + +// Platform-specific defs. +#if defined(_WIN32) +typedef unsigned long DWORD; // NOLINT +# define ALWAYS_INLINE __declspec(forceinline) +// FIXME(timurrrr): do we need this on Windows? +# define ALIAS(x) +# define ALIGNED(x) __declspec(align(x)) +# define FORMAT(f, a) +# define NOINLINE __declspec(noinline) +# define NORETURN __declspec(noreturn) +# define THREADLOCAL __declspec(thread) +# define NOTHROW +#else // _WIN32 +# define ALWAYS_INLINE __attribute__((always_inline)) +# define ALIAS(x) __attribute__((alias(x))) +# define ALIGNED(x) __attribute__((aligned(x))) +# define FORMAT(f, a) __attribute__((format(printf, f, a))) +# define NOINLINE __attribute__((noinline)) +# define NORETURN __attribute__((noreturn)) +# define THREADLOCAL __thread +# ifdef __cplusplus +# define NOTHROW throw() +# else +# define NOTHROW __attribute__((__nothrow__)) +#endif +#endif // _WIN32 + +// We have no equivalent of these on Windows. +#ifndef _WIN32 +# define LIKELY(x) __builtin_expect(!!(x), 1) +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +# define UNUSED __attribute__((unused)) +# define USED __attribute__((used)) +#endif + +#if defined(_WIN32) +typedef DWORD thread_return_t; +# define THREAD_CALLING_CONV __stdcall +#else // _WIN32 +typedef void* thread_return_t; +# define THREAD_CALLING_CONV +#endif // _WIN32 +typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); + +// If __WORDSIZE was undefined by the platform, define it in terms of the +// compiler built-ins __LP64__ and _WIN64. +#ifndef __WORDSIZE +# if __LP64__ || defined(_WIN64) +# define __WORDSIZE 64 +# else +# define __WORDSIZE 32 +# endif +#endif // __WORDSIZE + +// NOTE: Functions below must be defined in each run-time. +namespace __sanitizer { +void NORETURN Die(); +void NORETURN CheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2); +} // namespace __sanitizer + +// Check macro +#define RAW_CHECK_MSG(expr, msg) do { \ + if (!(expr)) { \ + RawWrite(msg); \ + Die(); \ + } \ +} while (0) + +#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr) + +#define CHECK_IMPL(c1, op, c2) \ + do { \ + __sanitizer::u64 v1 = (u64)(c1); \ + __sanitizer::u64 v2 = (u64)(c2); \ + if (!(v1 op v2)) \ + __sanitizer::CheckFailed(__FILE__, __LINE__, \ + "(" #c1 ") " #op " (" #c2 ")", v1, v2); \ + } while (false) \ +/**/ + +#define CHECK(a) CHECK_IMPL((a), !=, 0) +#define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b)) +#define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b)) +#define CHECK_LT(a, b) CHECK_IMPL((a), <, (b)) +#define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b)) +#define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) +#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) + +#if TSAN_DEBUG +#define DCHECK(a) CHECK(a) +#define DCHECK_EQ(a, b) CHECK_EQ(a, b) +#define DCHECK_NE(a, b) CHECK_NE(a, b) +#define DCHECK_LT(a, b) CHECK_LT(a, b) +#define DCHECK_LE(a, b) CHECK_LE(a, b) +#define DCHECK_GT(a, b) CHECK_GT(a, b) +#define DCHECK_GE(a, b) CHECK_GE(a, b) +#else +#define DCHECK(a) +#define DCHECK_EQ(a, b) +#define DCHECK_NE(a, b) +#define DCHECK_LT(a, b) +#define DCHECK_LE(a, b) +#define DCHECK_GT(a, b) +#define DCHECK_GE(a, b) +#endif + +#define UNIMPLEMENTED() CHECK("unimplemented" && 0) + +#define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__) + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) + +#define IMPL_PASTE(a, b) a##b +#define IMPL_COMPILER_ASSERT(pred, line) \ + typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1] + +// Limits for integral types. We have to redefine it in case we don't +// have stdint.h (like in Visual Studio 9). +#undef __INT64_C +#undef __UINT64_C +#if __WORDSIZE == 64 +# define __INT64_C(c) c ## L +# define __UINT64_C(c) c ## UL +#else +# define __INT64_C(c) c ## LL +# define __UINT64_C(c) c ## ULL +#endif // __WORDSIZE == 64 +#undef INT32_MIN +#define INT32_MIN (-2147483647-1) +#undef INT32_MAX +#define INT32_MAX (2147483647) +#undef UINT32_MAX +#define UINT32_MAX (4294967295U) +#undef INT64_MIN +#define INT64_MIN (-__INT64_C(9223372036854775807)-1) +#undef INT64_MAX +#define INT64_MAX (__INT64_C(9223372036854775807)) +#undef UINT64_MAX +#define UINT64_MAX (__UINT64_C(18446744073709551615)) + +enum LinkerInitialized { LINKER_INITIALIZED = 0 }; + +#if !defined(_MSC_VER) || defined(__clang__) +# define GET_CALLER_PC() (uptr)__builtin_return_address(0) +# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0) +#else +extern "C" void* _ReturnAddress(void); +# pragma intrinsic(_ReturnAddress) +# define GET_CALLER_PC() (uptr)_ReturnAddress() +// CaptureStackBackTrace doesn't need to know BP on Windows. +// FIXME: This macro is still used when printing error reports though it's not +// clear if the BP value is needed in the ASan reports on Windows. +# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF +#endif + +#define HANDLE_EINTR(res, f) { \ + do { \ + res = (f); \ + } while (res == -1 && errno == EINTR); \ + } + +#endif // SANITIZER_DEFS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc new file mode 100644 index 00000000000..21869bc4846 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc @@ -0,0 +1,189 @@ +//===-- sanitizer_libc.cc -------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. See sanitizer_libc.h for details. +//===----------------------------------------------------------------------===// +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +s64 internal_atoll(const char *nptr) { + return internal_simple_strtoll(nptr, (char**)0, 10); +} + +void *internal_memchr(const void *s, int c, uptr n) { + const char* t = (char*)s; + for (uptr i = 0; i < n; ++i, ++t) + if (*t == c) + return (void*)t; + return 0; +} + +int internal_memcmp(const void* s1, const void* s2, uptr n) { + const char* t1 = (char*)s1; + const char* t2 = (char*)s2; + for (uptr i = 0; i < n; ++i, ++t1, ++t2) + if (*t1 != *t2) + return *t1 < *t2 ? -1 : 1; + return 0; +} + +void *internal_memcpy(void *dest, const void *src, uptr n) { + char *d = (char*)dest; + char *s = (char*)src; + for (uptr i = 0; i < n; ++i) + d[i] = s[i]; + return dest; +} + +void *internal_memset(void* s, int c, uptr n) { + // The next line prevents Clang from making a call to memset() instead of the + // loop below. + // FIXME: building the runtime with -ffreestanding is a better idea. However + // there currently are linktime problems due to PR12396. + char volatile *t = (char*)s; + for (uptr i = 0; i < n; ++i, ++t) { + *t = c; + } + return s; +} + +uptr internal_strcspn(const char *s, const char *reject) { + uptr i; + for (i = 0; s[i]; i++) { + if (internal_strchr(reject, s[i]) != 0) + return i; + } + return i; +} + +char* internal_strdup(const char *s) { + uptr len = internal_strlen(s); + char *s2 = (char*)InternalAlloc(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +int internal_strcmp(const char *s1, const char *s2) { + while (true) { + unsigned c1 = *s1; + unsigned c2 = *s2; + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (c1 == 0) break; + s1++; + s2++; + } + return 0; +} + +int internal_strncmp(const char *s1, const char *s2, uptr n) { + for (uptr i = 0; i < n; i++) { + unsigned c1 = *s1; + unsigned c2 = *s2; + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (c1 == 0) break; + s1++; + s2++; + } + return 0; +} + +char* internal_strchr(const char *s, int c) { + while (true) { + if (*s == (char)c) + return (char*)s; + if (*s == 0) + return 0; + s++; + } +} + +char *internal_strrchr(const char *s, int c) { + const char *res = 0; + for (uptr i = 0; s[i]; i++) { + if (s[i] == c) res = s + i; + } + return (char*)res; +} + +uptr internal_strlen(const char *s) { + uptr i = 0; + while (s[i]) i++; + return i; +} + +char *internal_strncat(char *dst, const char *src, uptr n) { + uptr len = internal_strlen(dst); + uptr i; + for (i = 0; i < n && src[i]; i++) + dst[len + i] = src[i]; + dst[len + i] = 0; + return dst; +} + +char *internal_strncpy(char *dst, const char *src, uptr n) { + uptr i; + for (i = 0; i < n && src[i]; i++) + dst[i] = src[i]; + for (; i < n; i++) + dst[i] = '\0'; + return dst; +} + +uptr internal_strnlen(const char *s, uptr maxlen) { + uptr i = 0; + while (i < maxlen && s[i]) i++; + return i; +} + +char *internal_strstr(const char *haystack, const char *needle) { + // This is O(N^2), but we are not using it in hot places. + uptr len1 = internal_strlen(haystack); + uptr len2 = internal_strlen(needle); + if (len1 < len2) return 0; + for (uptr pos = 0; pos <= len1 - len2; pos++) { + if (internal_memcmp(haystack + pos, needle, len2) == 0) + return (char*)haystack + pos; + } + return 0; +} + +s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { + CHECK_EQ(base, 10); + while (IsSpace(*nptr)) nptr++; + int sgn = 1; + u64 res = 0; + bool have_digits = false; + char *old_nptr = (char*)nptr; + if (*nptr == '+') { + sgn = 1; + nptr++; + } else if (*nptr == '-') { + sgn = -1; + nptr++; + } + while (IsDigit(*nptr)) { + res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX; + int digit = ((*nptr) - '0'); + res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX; + have_digits = true; + nptr++; + } + if (endptr != 0) { + *endptr = (have_digits) ? (char*)nptr : old_nptr; + } + if (sgn > 0) { + return (s64)(Min((u64)INT64_MAX, res)); + } else { + return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1); + } +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h new file mode 100644 index 00000000000..13528511190 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -0,0 +1,69 @@ +//===-- sanitizer_libc.h ----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// These tools can not use some of the libc functions directly because those +// functions are intercepted. Instead, we implement a tiny subset of libc here. +// NOTE: This file may be included into user code. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LIBC_H +#define SANITIZER_LIBC_H + +// ----------- ATTENTION ------------- +// This header should NOT include any other headers from sanitizer runtime. +#include "sanitizer/common_interface_defs.h" + +namespace __sanitizer { + +// internal_X() is a custom implementation of X() for use in RTL. + +// String functions +s64 internal_atoll(const char *nptr); +void *internal_memchr(const void *s, int c, uptr n); +int internal_memcmp(const void* s1, const void* s2, uptr n); +void *internal_memcpy(void *dest, const void *src, uptr n); +// Should not be used in performance-critical places. +void *internal_memset(void *s, int c, uptr n); +char* internal_strchr(const char *s, int c); +int internal_strcmp(const char *s1, const char *s2); +uptr internal_strcspn(const char *s, const char *reject); +char *internal_strdup(const char *s); +uptr internal_strlen(const char *s); +char *internal_strncat(char *dst, const char *src, uptr n); +int internal_strncmp(const char *s1, const char *s2, uptr n); +char *internal_strncpy(char *dst, const char *src, uptr n); +uptr internal_strnlen(const char *s, uptr maxlen); +char *internal_strrchr(const char *s, int c); +// This is O(N^2), but we are not using it in hot places. +char *internal_strstr(const char *haystack, const char *needle); +// Works only for base=10 and doesn't set errno. +s64 internal_simple_strtoll(const char *nptr, char **endptr, int base); + +// Memory +void *internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset); +int internal_munmap(void *addr, uptr length); + +// I/O +typedef int fd_t; +const fd_t kInvalidFd = -1; +int internal_close(fd_t fd); +fd_t internal_open(const char *filename, bool write); +uptr internal_read(fd_t fd, void *buf, uptr count); +uptr internal_write(fd_t fd, const void *buf, uptr count); +uptr internal_filesize(fd_t fd); // -1 on error. +int internal_dup2(int oldfd, int newfd); +uptr internal_readlink(const char *path, char *buf, uptr bufsize); +int internal_snprintf(char *buffer, uptr length, const char *format, ...); + +// Threading +int internal_sched_yield(); + +} // namespace __sanitizer + +#endif // SANITIZER_LIBC_H diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc new file mode 100644 index 00000000000..ab6c5a4b82c --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc @@ -0,0 +1,296 @@ +//===-- sanitizer_linux.cc ------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements linux-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// +#ifdef __linux__ + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" + +#include <fcntl.h> +#include <pthread.h> +#include <sched.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> + +namespace __sanitizer { + +// --------------- sanitizer_libc.h +void *internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 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 +} + +int internal_munmap(void *addr, uptr length) { + return syscall(__NR_munmap, addr, length); +} + +int internal_close(fd_t fd) { + return syscall(__NR_close, fd); +} + +fd_t internal_open(const char *filename, bool write) { + return syscall(__NR_open, filename, + write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + sptr res; + HANDLE_EINTR(res, (sptr)syscall(__NR_read, fd, buf, count)); + return res; +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + sptr res; + HANDLE_EINTR(res, (sptr)syscall(__NR_write, fd, buf, count)); + return res; +} + +uptr internal_filesize(fd_t fd) { +#if __WORDSIZE == 64 + struct stat st; + if (syscall(__NR_fstat, fd, &st)) + return -1; +#else + struct stat64 st; + if (syscall(__NR_fstat64, fd, &st)) + return -1; +#endif + return (uptr)st.st_size; +} + +int internal_dup2(int oldfd, int newfd) { + return syscall(__NR_dup2, oldfd, newfd); +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { + return (uptr)syscall(__NR_readlink, path, buf, bufsize); +} + +int internal_sched_yield() { + return syscall(__NR_sched_yield); +} + +// ----------------- sanitizer_common.h +uptr GetTid() { + return syscall(__NR_gettid); +} + +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + static const uptr kMaxThreadStackSize = 256 * (1 << 20); // 256M + CHECK(stack_top); + CHECK(stack_bottom); + if (at_initialization) { + // This is the main thread. Libpthread may not be initialized yet. + struct rlimit rl; + CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); + + // Find the mapping that contains a stack variable. + MemoryMappingLayout proc_maps; + uptr start, end, offset; + uptr prev_end = 0; + while (proc_maps.Next(&start, &end, &offset, 0, 0)) { + if ((uptr)&rl < end) + break; + prev_end = end; + } + CHECK((uptr)&rl >= start && (uptr)&rl < end); + + // Get stacksize from rlimit, but clip it so that it does not overlap + // with other mappings. + uptr stacksize = rl.rlim_cur; + if (stacksize > end - prev_end) + stacksize = end - prev_end; + // 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 subprocesses with unlimited stack. + if (stacksize > kMaxThreadStackSize) + stacksize = kMaxThreadStackSize; + *stack_top = end; + *stack_bottom = end - stacksize; + return; + } + pthread_attr_t attr; + CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); + uptr stacksize = 0; + void *stackaddr = 0; + pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); + pthread_attr_destroy(&attr); + + *stack_top = (uptr)stackaddr + stacksize; + *stack_bottom = (uptr)stackaddr; + CHECK(stacksize < kMaxThreadStackSize); // Sanity check. +} + +// Like getenv, but reads env directly from /proc and does not use libc. +// This function should be called first inside __asan_init. +const char *GetEnv(const char *name) { + static char *environ; + static uptr len; + static bool inited; + if (!inited) { + inited = true; + uptr environ_size; + len = ReadFileToBuffer("/proc/self/environ", + &environ, &environ_size, 1 << 26); + } + if (!environ || len == 0) return 0; + uptr 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 == 0) // this entry isn't NUL terminated + return 0; + else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. + return p + namelen + 1; // point after = + p = endp + 1; + } + return 0; // Not found. +} + +void ReExec() { + static const int kMaxArgv = 100; + InternalScopedBuffer<char*> argv(kMaxArgv + 1); + static char *buff; + uptr buff_size = 0; + ReadFileToBuffer("/proc/self/cmdline", &buff, &buff_size, 1024 * 1024); + argv[0] = buff; + int argc, i; + for (argc = 1, i = 1; ; i++) { + if (buff[i] == 0) { + if (buff[i+1] == 0) break; + argv[argc] = &buff[i+1]; + CHECK_LE(argc, kMaxArgv); // FIXME: make this more flexible. + argc++; + } + } + argv[argc] = 0; + execv(argv[0], argv.data()); +} + +// ----------------- sanitizer_procmaps.h +MemoryMappingLayout::MemoryMappingLayout() { + proc_self_maps_buff_len_ = + ReadFileToBuffer("/proc/self/maps", &proc_self_maps_buff_, + &proc_self_maps_buff_mmaped_size_, 1 << 26); + CHECK_GT(proc_self_maps_buff_len_, 0); + // internal_write(2, proc_self_maps_buff_, proc_self_maps_buff_len_); + Reset(); +} + +MemoryMappingLayout::~MemoryMappingLayout() { + UnmapOrDie(proc_self_maps_buff_, proc_self_maps_buff_mmaped_size_); +} + +void MemoryMappingLayout::Reset() { + current_ = proc_self_maps_buff_; +} + +// Parse a hex value in str and update str. +static uptr ParseHex(char **str) { + uptr x = 0; + char *s; + for (s = *str; ; s++) { + char c = *s; + uptr v = 0; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + v = c - 'A' + 10; + else + break; + x = x * 16 + v; + } + *str = s; + return x; +} + +static bool IsOnOf(char c, char c1, char c2) { + return c == c1 || c == c2; +} + +static bool IsDecimal(char c) { + return c >= '0' && c <= '9'; +} + +bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size) { + char *last = proc_self_maps_buff_ + proc_self_maps_buff_len_; + if (current_ >= last) return false; + uptr dummy; + if (!start) start = &dummy; + if (!end) end = &dummy; + if (!offset) offset = &dummy; + char *next_line = (char*)internal_memchr(current_, '\n', last - current_); + if (next_line == 0) + next_line = last; + // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar + *start = ParseHex(¤t_); + CHECK_EQ(*current_++, '-'); + *end = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + CHECK(IsOnOf(*current_++, '-', 'r')); + CHECK(IsOnOf(*current_++, '-', 'w')); + CHECK(IsOnOf(*current_++, '-', 'x')); + CHECK(IsOnOf(*current_++, 's', 'p')); + CHECK_EQ(*current_++, ' '); + *offset = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + ParseHex(¤t_); + CHECK_EQ(*current_++, ':'); + ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + while (IsDecimal(*current_)) + current_++; + CHECK_EQ(*current_++, ' '); + // Skip spaces. + while (current_ < next_line && *current_ == ' ') + current_++; + // Fill in the filename. + uptr i = 0; + while (current_ < next_line) { + if (filename && i < filename_size - 1) + filename[i++] = *current_; + current_++; + } + if (filename && i < filename_size) + filename[i] = 0; + current_ = next_line + 1; + return true; +} + +// Gets the object name and the offset by walking MemoryMappingLayout. +bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], + uptr filename_size) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); +} + +} // namespace __sanitizer + +#endif // __linux__ diff --git a/libsanitizer/sanitizer_common/sanitizer_list.h b/libsanitizer/sanitizer_common/sanitizer_list.h new file mode 100644 index 00000000000..3df12f550a0 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_list.h @@ -0,0 +1,118 @@ +//===-- sanitizer_list.h ----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains implementation of a list class to be used by +// ThreadSanitizer, etc run-times. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LIST_H +#define SANITIZER_LIST_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// Intrusive singly-linked list with size(), push_back(), push_front() +// pop_front(), append_front() and append_back(). +// This class should be a POD (so that it can be put into TLS) +// and an object with all zero fields should represent a valid empty list. +// This class does not have a CTOR, so clear() should be called on all +// non-zero-initialized objects before using. +template<class Item> +struct IntrusiveList { + void clear() { + first_ = last_ = 0; + size_ = 0; + } + + bool empty() const { return size_ == 0; } + uptr size() const { return size_; } + + void push_back(Item *x) { + if (empty()) { + x->next = 0; + first_ = last_ = x; + size_ = 1; + } else { + x->next = 0; + last_->next = x; + last_ = x; + size_++; + } + } + + void push_front(Item *x) { + if (empty()) { + x->next = 0; + first_ = last_ = x; + size_ = 1; + } else { + x->next = first_; + first_ = x; + size_++; + } + } + + void pop_front() { + CHECK(!empty()); + first_ = first_->next; + if (first_ == 0) + last_ = 0; + size_--; + } + + Item *front() { return first_; } + Item *back() { return last_; } + + void append_front(IntrusiveList<Item> *l) { + CHECK_NE(this, l); + if (empty()) { + *this = *l; + } else if (!l->empty()) { + l->last_->next = first_; + first_ = l->first_; + size_ += l->size(); + } + l->clear(); + } + + void append_back(IntrusiveList<Item> *l) { + CHECK_NE(this, l); + if (empty()) { + *this = *l; + } else { + last_->next = l->first_; + last_ = l->last_; + size_ += l->size(); + } + l->clear(); + } + + void CheckConsistency() { + if (size_ == 0) { + CHECK_EQ(first_, 0); + CHECK_EQ(last_, 0); + } else { + uptr count = 0; + for (Item *i = first_; ; i = i->next) { + count++; + if (i == last_) break; + } + CHECK_EQ(size(), count); + CHECK_EQ(last_->next, 0); + } + } + +// private, don't use directly. + uptr size_; + Item *first_; + Item *last_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_LIST_H diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc new file mode 100644 index 00000000000..400cd21842b --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc @@ -0,0 +1,249 @@ +//===-- sanitizer_mac.cc --------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements mac-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" + +#include <crt_externs.h> // for _NSGetEnviron +#include <fcntl.h> +#include <mach-o/dyld.h> +#include <mach-o/loader.h> +#include <pthread.h> +#include <sched.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace __sanitizer { + +// ---------------------- sanitizer_libc.h +void *internal_mmap(void *addr, size_t length, int prot, int flags, + int fd, u64 offset) { + return mmap(addr, length, prot, flags, fd, offset); +} + +int internal_munmap(void *addr, uptr length) { + return munmap(addr, length); +} + +int internal_close(fd_t fd) { + return close(fd); +} + +fd_t internal_open(const char *filename, bool write) { + return open(filename, + write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + return read(fd, buf, count); +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + return write(fd, buf, count); +} + +uptr internal_filesize(fd_t fd) { + struct stat st; + if (fstat(fd, &st)) + return -1; + return (uptr)st.st_size; +} + +int internal_dup2(int oldfd, int newfd) { + return dup2(oldfd, newfd); +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { + return readlink(path, buf, bufsize); +} + +int internal_sched_yield() { + return sched_yield(); +} + +// ----------------- sanitizer_common.h +uptr GetTid() { + return reinterpret_cast<uptr>(pthread_self()); +} + +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + CHECK(stack_top); + CHECK(stack_bottom); + uptr stacksize = pthread_get_stacksize_np(pthread_self()); + void *stackaddr = pthread_get_stackaddr_np(pthread_self()); + *stack_top = (uptr)stackaddr; + *stack_bottom = *stack_top - stacksize; +} + +const char *GetEnv(const char *name) { + char ***env_ptr = _NSGetEnviron(); + CHECK(env_ptr); + char **environ = *env_ptr; + CHECK(environ); + uptr name_len = internal_strlen(name); + while (*environ != 0) { + uptr len = internal_strlen(*environ); + if (len > name_len) { + const char *p = *environ; + if (!internal_memcmp(p, name, name_len) && + p[name_len] == '=') { // Match. + return *environ + name_len + 1; // String starting after =. + } + } + environ++; + } + return 0; +} + +void ReExec() { + UNIMPLEMENTED(); +} + +// ----------------- sanitizer_procmaps.h + +MemoryMappingLayout::MemoryMappingLayout() { + Reset(); +} + +MemoryMappingLayout::~MemoryMappingLayout() { +} + +// More information about Mach-O headers can be found in mach-o/loader.h +// Each Mach-O image has a header (mach_header or mach_header_64) starting with +// a magic number, and a list of linker load commands directly following the +// header. +// A load command is at least two 32-bit words: the command type and the +// command size in bytes. We're interested only in segment load commands +// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped +// into the task's address space. +// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or +// segment_command_64 correspond to the memory address, memory size and the +// file offset of the current memory segment. +// Because these fields are taken from the images as is, one needs to add +// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime. + +void MemoryMappingLayout::Reset() { + // Count down from the top. + // TODO(glider): as per man 3 dyld, iterating over the headers with + // _dyld_image_count is thread-unsafe. We need to register callbacks for + // adding and removing images which will invalidate the MemoryMappingLayout + // state. + current_image_ = _dyld_image_count(); + current_load_cmd_count_ = -1; + current_load_cmd_addr_ = 0; + current_magic_ = 0; + current_filetype_ = 0; +} + +// Next and NextSegmentLoad were inspired by base/sysinfo.cc in +// Google Perftools, http://code.google.com/p/google-perftools. + +// NextSegmentLoad scans the current image for the next segment load command +// and returns the start and end addresses and file offset of the corresponding +// segment. +// Note that the segment addresses are not necessarily sorted. +template<u32 kLCSegment, typename SegmentCommand> +bool MemoryMappingLayout::NextSegmentLoad( + uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size) { + const char* lc = current_load_cmd_addr_; + current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize; + if (((const load_command *)lc)->cmd == kLCSegment) { + const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_); + const SegmentCommand* sc = (const SegmentCommand *)lc; + if (start) *start = sc->vmaddr + dlloff; + if (end) *end = sc->vmaddr + sc->vmsize + dlloff; + if (offset) { + if (current_filetype_ == /*MH_EXECUTE*/ 0x2) { + *offset = sc->vmaddr; + } else { + *offset = sc->fileoff; + } + } + if (filename) { + internal_strncpy(filename, _dyld_get_image_name(current_image_), + filename_size); + } + return true; + } + return false; +} + +bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size) { + for (; current_image_ >= 0; current_image_--) { + const mach_header* hdr = _dyld_get_image_header(current_image_); + if (!hdr) continue; + if (current_load_cmd_count_ < 0) { + // Set up for this image; + current_load_cmd_count_ = hdr->ncmds; + current_magic_ = hdr->magic; + current_filetype_ = hdr->filetype; + switch (current_magic_) { +#ifdef MH_MAGIC_64 + case MH_MAGIC_64: { + current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64); + break; + } +#endif + case MH_MAGIC: { + current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header); + break; + } + default: { + continue; + } + } + } + + for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) { + switch (current_magic_) { + // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64. +#ifdef MH_MAGIC_64 + case MH_MAGIC_64: { + if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>( + start, end, offset, filename, filename_size)) + return true; + break; + } +#endif + case MH_MAGIC: { + if (NextSegmentLoad<LC_SEGMENT, struct segment_command>( + start, end, offset, filename, filename_size)) + return true; + break; + } + } + } + // If we get here, no more load_cmd's in this image talk about + // segments. Go on to the next image. + } + return false; +} + +bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], + uptr filename_size) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); +} + +} // namespace __sanitizer + +#endif // __APPLE__ diff --git a/libsanitizer/sanitizer_common/sanitizer_mutex.h b/libsanitizer/sanitizer_common/sanitizer_mutex.h new file mode 100644 index 00000000000..a38a49ae242 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_mutex.h @@ -0,0 +1,106 @@ +//===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_MUTEX_H +#define SANITIZER_MUTEX_H + +#include "sanitizer_atomic.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +class StaticSpinMutex { + public: + void Init() { + atomic_store(&state_, 0, memory_order_relaxed); + } + + void Lock() { + if (atomic_exchange(&state_, 1, memory_order_acquire) == 0) + return; + LockSlow(); + } + + void Unlock() { + atomic_store(&state_, 0, memory_order_release); + } + + private: + atomic_uint8_t state_; + + void NOINLINE LockSlow() { + for (int i = 0;; i++) { + if (i < 10) + proc_yield(10); + else + internal_sched_yield(); + if (atomic_load(&state_, memory_order_relaxed) == 0 + && atomic_exchange(&state_, 1, memory_order_acquire) == 0) + return; + } + } +}; + +class SpinMutex : public StaticSpinMutex { + public: + SpinMutex() { + Init(); + } + + private: + SpinMutex(const SpinMutex&); + void operator=(const SpinMutex&); +}; + +template<typename MutexType> +class GenericScopedLock { + public: + explicit GenericScopedLock(MutexType *mu) + : mu_(mu) { + mu_->Lock(); + } + + ~GenericScopedLock() { + mu_->Unlock(); + } + + private: + MutexType *mu_; + + GenericScopedLock(const GenericScopedLock&); + void operator=(const GenericScopedLock&); +}; + +template<typename MutexType> +class GenericScopedReadLock { + public: + explicit GenericScopedReadLock(MutexType *mu) + : mu_(mu) { + mu_->ReadLock(); + } + + ~GenericScopedReadLock() { + mu_->ReadUnlock(); + } + + private: + MutexType *mu_; + + GenericScopedReadLock(const GenericScopedReadLock&); + void operator=(const GenericScopedReadLock&); +}; + +typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock; + +} // namespace __sanitizer + +#endif // SANITIZER_MUTEX_H diff --git a/libsanitizer/sanitizer_common/sanitizer_placement_new.h b/libsanitizer/sanitizer_common/sanitizer_placement_new.h new file mode 100644 index 00000000000..d149683b43d --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_placement_new.h @@ -0,0 +1,31 @@ +//===-- sanitizer_placement_new.h -------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// +// The file provides 'placement new'. +// Do not include it into header files, only into source files. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_PLACEMENT_NEW_H +#define SANITIZER_PLACEMENT_NEW_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { +#if (__WORDSIZE == 64) || defined(__APPLE__) +typedef uptr operator_new_ptr_type; +#else +typedef u32 operator_new_ptr_type; +#endif +} // namespace __sanitizer + +inline void *operator new(__sanitizer::operator_new_ptr_type sz, void *p) { + return p; +} + +#endif // SANITIZER_PLACEMENT_NEW_H diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cc b/libsanitizer/sanitizer_common/sanitizer_posix.cc new file mode 100644 index 00000000000..8f71cfc049d --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cc @@ -0,0 +1,187 @@ +//===-- sanitizer_posix.cc ------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements POSIX-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// +#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" + +#include <errno.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +namespace __sanitizer { + +// ------------- sanitizer_common.h + +int GetPid() { + return getpid(); +} + +uptr GetThreadSelf() { + return (uptr)pthread_self(); +} + +void *MmapOrDie(uptr size, const char *mem_type) { + size = RoundUpTo(size, kPageSize); + void *res = internal_mmap(0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (res == (void*)-1) { + static int recursion_count; + if (recursion_count) { + // The Report() and CHECK calls below may call mmap recursively and fail. + // If we went into recursion, just die. + RawWrite("AddressSanitizer is unable to mmap\n"); + Die(); + } + recursion_count++; + Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s: %s\n", + size, size, mem_type, strerror(errno)); + DumpProcessMap(); + CHECK("unable to mmap" && 0); + } + return res; +} + +void UnmapOrDie(void *addr, uptr size) { + if (!addr || !size) return; + int res = internal_munmap(addr, size); + if (res != 0) { + Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n", + size, size, addr); + CHECK("unable to unmap" && 0); + } +} + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { + return internal_mmap((void*)fixed_addr, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, + -1, 0); +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return internal_mmap((void*)fixed_addr, size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, + -1, 0); +} + +void *MapFileToMemory(const char *file_name, uptr *buff_size) { + fd_t fd = internal_open(file_name, false); + CHECK_NE(fd, kInvalidFd); + uptr fsize = internal_filesize(fd); + CHECK_NE(fsize, (uptr)-1); + CHECK_GT(fsize, 0); + *buff_size = RoundUpTo(fsize, kPageSize); + void *map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); + return (map == MAP_FAILED) ? 0 : map; +} + + +static inline bool IntervalsAreSeparate(uptr start1, uptr end1, + uptr start2, uptr end2) { + CHECK(start1 <= end1); + CHECK(start2 <= end2); + return (end1 < start2) || (end2 < start1); +} + +// FIXME: this is thread-unsafe, but should not cause problems most of the time. +// When the shadow is mapped only a single thread usually exists (plus maybe +// several worker threads on Mac, which aren't expected to map big chunks of +// memory). +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { + MemoryMappingLayout procmaps; + uptr start, end; + while (procmaps.Next(&start, &end, + /*offset*/0, /*filename*/0, /*filename_size*/0)) { + if (!IntervalsAreSeparate(start, end, range_start, range_end)) + return false; + } + return true; +} + +void DumpProcessMap() { + MemoryMappingLayout proc_maps; + uptr start, end; + const sptr kBufSize = 4095; + char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__); + Report("Process memory map follows:\n"); + while (proc_maps.Next(&start, &end, /* file_offset */0, + filename, kBufSize)) { + Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); + } + Report("End of process memory map.\n"); + UnmapOrDie(filename, kBufSize); +} + +const char *GetPwd() { + return GetEnv("PWD"); +} + +void DisableCoreDumper() { + struct rlimit nocore; + nocore.rlim_cur = 0; + nocore.rlim_max = 0; + setrlimit(RLIMIT_CORE, &nocore); +} + +bool StackSizeIsUnlimited() { + struct rlimit rlim; + CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim)); + return (rlim.rlim_cur == (uptr)-1); +} + +void SetStackSizeLimitInBytes(uptr limit) { + struct rlimit rlim; + rlim.rlim_cur = limit; + rlim.rlim_max = limit; + CHECK_EQ(0, setrlimit(RLIMIT_STACK, &rlim)); + CHECK(!StackSizeIsUnlimited()); +} + +void SleepForSeconds(int seconds) { + sleep(seconds); +} + +void SleepForMillis(int millis) { + usleep(millis * 1000); +} + +void Exit(int exitcode) { + _exit(exitcode); +} + +void Abort() { + abort(); +} + +int Atexit(void (*function)(void)) { +#ifndef SANITIZER_GO + return atexit(function); +#else + return 0; +#endif +} + +} // namespace __sanitizer + +#endif // __linux__ || __APPLE_ diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc new file mode 100644 index 00000000000..da4dc7f53a1 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -0,0 +1,196 @@ +//===-- sanitizer_printf.cc -----------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// +// Internal printf function, used inside run-time libraries. +// We can't use libc printf because we intercept some of the functions used +// inside it. +//===----------------------------------------------------------------------===// + + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +#include <stdio.h> +#include <stdarg.h> + +namespace __sanitizer { + +static 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, u64 num, + u8 base, u8 minimal_num_length) { + uptr const kMaxLen = 30; + RAW_CHECK(base == 10 || base == 16); + RAW_CHECK(minimal_num_length < kMaxLen); + uptr num_buffer[kMaxLen]; + uptr 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) { + uptr digit = num_buffer[pos]; + result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit + : 'a' + digit - 10); + } + return result; +} + +static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num) { + int result = 0; + if (num < 0) { + result += AppendChar(buff, buff_end, '-'); + num = -num; + } + result += AppendUnsigned(buff, buff_end, (u64)num, 10, 0); + return result; +} + +static int AppendString(char **buff, const char *buff_end, const char *s) { + if (s == 0) + s = "<null>"; + int result = 0; + for (; *s; s++) { + result += AppendChar(buff, buff_end, *s); + } + return result; +} + +static int AppendPointer(char **buff, const char *buff_end, u64 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; +} + +int VSNPrintf(char *buff, int buff_length, + const char *format, va_list args) { + static const char *kPrintfFormatsHelp = "Supported Printf formats: " + "%%[z]{d,u,x}; %%p; %%s; %%c\n"; + 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 != '%') { + result += AppendChar(&buff, buff_end, *cur); + continue; + } + cur++; + bool have_z = (*cur == 'z'); + cur += have_z; + s64 dval; + u64 uval; + switch (*cur) { + case 'd': { + dval = have_z ? va_arg(args, sptr) + : va_arg(args, int); + result += AppendSignedDecimal(&buff, buff_end, dval); + break; + } + case 'u': + case 'x': { + uval = have_z ? va_arg(args, uptr) + : va_arg(args, unsigned); + result += AppendUnsigned(&buff, buff_end, uval, + (*cur == 'u') ? 10 : 16, 0); + break; + } + case 'p': { + RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp); + result += AppendPointer(&buff, buff_end, va_arg(args, uptr)); + break; + } + case 's': { + RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp); + result += AppendString(&buff, buff_end, va_arg(args, char*)); + break; + } + case 'c': { + RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp); + result += AppendChar(&buff, buff_end, va_arg(args, int)); + break; + } + case '%' : { + RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp); + result += AppendChar(&buff, buff_end, '%'); + break; + } + default: { + RAW_CHECK_MSG(false, kPrintfFormatsHelp); + } + } + } + RAW_CHECK(buff <= buff_end); + AppendChar(&buff, buff_end + 1, '\0'); + return result; +} + +static void (*PrintfAndReportCallback)(const char *); +void SetPrintfAndReportCallback(void (*callback)(const char *)) { + PrintfAndReportCallback = callback; +} + +void Printf(const char *format, ...) { + const int kLen = 1024 * 4; + InternalScopedBuffer<char> buffer(kLen); + va_list args; + va_start(args, format); + int needed_length = VSNPrintf(buffer.data(), kLen, format, args); + va_end(args); + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n"); + RawWrite(buffer.data()); + if (PrintfAndReportCallback) + PrintfAndReportCallback(buffer.data()); +} + +// 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 internal_snprintf(char *buffer, uptr 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; + InternalScopedBuffer<char> buffer(kLen); + int needed_length = internal_snprintf(buffer.data(), + 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.data() + 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.data()); + if (PrintfAndReportCallback) + PrintfAndReportCallback(buffer.data()); +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps.h b/libsanitizer/sanitizer_common/sanitizer_procmaps.h new file mode 100644 index 00000000000..5e5e5ce89be --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps.h @@ -0,0 +1,95 @@ +//===-- sanitizer_procmaps.h ------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// +// Information about the process mappings. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_PROCMAPS_H +#define SANITIZER_PROCMAPS_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +#ifdef _WIN32 +class MemoryMappingLayout { + public: + MemoryMappingLayout() {} + bool GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], uptr filename_size) { + UNIMPLEMENTED(); + return false; + } +}; + +#else // _WIN32 +class MemoryMappingLayout { + public: + MemoryMappingLayout(); + bool Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size); + void Reset(); + // Gets the object file name and the offset in that object for a given + // address 'addr'. Returns true on success. + bool GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], uptr filename_size); + ~MemoryMappingLayout(); + + private: + // Default implementation of GetObjectNameAndOffset. + // Quite slow, because it iterates through the whole process map for each + // lookup. + bool IterateForObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], uptr filename_size) { + Reset(); + uptr start, end, file_offset; + for (int i = 0; Next(&start, &end, &file_offset, filename, filename_size); + i++) { + if (addr >= start && addr < end) { + // Don't subtract 'start' for the first entry: + // * If a binary is compiled w/o -pie, then the first entry in + // process maps is likely the binary itself (all dynamic libs + // are mapped higher in address space). For such a binary, + // instruction offset in binary coincides with the actual + // instruction address in virtual memory (as code section + // is mapped to a fixed memory range). + // * If a binary is compiled with -pie, all the modules are + // mapped high at address space (in particular, higher than + // shadow memory of the tool), so the module can't be the + // first entry. + *offset = (addr - (i ? start : 0)) + file_offset; + return true; + } + } + if (filename_size) + filename[0] = '\0'; + return false; + } + +# if defined __linux__ + char *proc_self_maps_buff_; + uptr proc_self_maps_buff_mmaped_size_; + uptr proc_self_maps_buff_len_; + char *current_; +# elif defined __APPLE__ + template<u32 kLCSegment, typename SegmentCommand> + bool NextSegmentLoad(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size); + int current_image_; + u32 current_magic_; + u32 current_filetype_; + int current_load_cmd_count_; + char *current_load_cmd_addr_; +# endif +}; + +#endif // _WIN32 + +} // namespace __sanitizer + +#endif // SANITIZER_PROCMAPS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc new file mode 100644 index 00000000000..d9c5b69c7a2 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc @@ -0,0 +1,194 @@ +//===-- sanitizer_stackdepot.cc -------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_stackdepot.h" +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_mutex.h" +#include "sanitizer_atomic.h" + +namespace __sanitizer { + +const int kTabSize = 1024 * 1024; // Hash table size. +const int kPartBits = 8; +const int kPartShift = sizeof(u32) * 8 - kPartBits - 1; +const int kPartCount = 1 << kPartBits; // Number of subparts in the table. +const int kPartSize = kTabSize / kPartCount; +const int kMaxId = 1 << kPartShift; + +struct StackDesc { + StackDesc *link; + u32 id; + u32 hash; + uptr size; + uptr stack[1]; // [size] +}; + +static struct { + StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator. + atomic_uintptr_t region_pos; // Region allocator for StackDesc's. + atomic_uintptr_t region_end; + atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's. + atomic_uint32_t seq[kPartCount]; // Unique id generators. +} depot; + +static u32 hash(const uptr *stack, uptr size) { + // murmur2 + const u32 m = 0x5bd1e995; + const u32 seed = 0x9747b28c; + const u32 r = 24; + u32 h = seed ^ (size * sizeof(uptr)); + for (uptr i = 0; i < size; i++) { + u32 k = stack[i]; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + } + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; +} + +static StackDesc *tryallocDesc(uptr memsz) { + // Optimisic lock-free allocation, essentially try to bump the region ptr. + for (;;) { + uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire); + uptr end = atomic_load(&depot.region_end, memory_order_acquire); + if (cmp == 0 || cmp + memsz > end) + return 0; + if (atomic_compare_exchange_weak( + &depot.region_pos, &cmp, cmp + memsz, + memory_order_acquire)) + return (StackDesc*)cmp; + } +} + +static StackDesc *allocDesc(uptr size) { + // Frist, try to allocate optimisitically. + uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr); + StackDesc *s = tryallocDesc(memsz); + if (s) + return s; + // If failed, lock, retry and alloc new superblock. + SpinMutexLock l(&depot.mtx); + for (;;) { + s = tryallocDesc(memsz); + if (s) + return s; + atomic_store(&depot.region_pos, 0, memory_order_relaxed); + uptr allocsz = 64 * 1024; + if (allocsz < memsz) + allocsz = memsz; + uptr mem = (uptr)MmapOrDie(allocsz, "stack depot"); + atomic_store(&depot.region_end, mem + allocsz, memory_order_release); + atomic_store(&depot.region_pos, mem, memory_order_release); + } +} + +static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) { + // Searches linked list s for the stack, returns its id. + for (; s; s = s->link) { + if (s->hash == hash && s->size == size) { + uptr i = 0; + for (; i < size; i++) { + if (stack[i] != s->stack[i]) + break; + } + if (i == size) + return s->id; + } + } + return 0; +} + +static StackDesc *lock(atomic_uintptr_t *p) { + // Uses the pointer lsb as mutex. + for (int i = 0;; i++) { + uptr cmp = atomic_load(p, memory_order_relaxed); + if ((cmp & 1) == 0 + && atomic_compare_exchange_weak(p, &cmp, cmp | 1, + memory_order_acquire)) + return (StackDesc*)cmp; + if (i < 10) + proc_yield(10); + else + internal_sched_yield(); + } +} + +static void unlock(atomic_uintptr_t *p, StackDesc *s) { + DCHECK_EQ((uptr)s & 1, 0); + atomic_store(p, (uptr)s, memory_order_release); +} + +u32 StackDepotPut(const uptr *stack, uptr size) { + if (stack == 0 || size == 0) + return 0; + uptr h = hash(stack, size); + atomic_uintptr_t *p = &depot.tab[h % kTabSize]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + // First, try to find the existing stack. + u32 id = find(s, stack, size, h); + if (id) + return id; + // If failed, lock, retry and insert new. + StackDesc *s2 = lock(p); + if (s2 != s) { + id = find(s2, stack, size, h); + if (id) { + unlock(p, s2); + return id; + } + } + uptr part = (h % kTabSize) / kPartSize; + id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1; + CHECK_LT(id, kMaxId); + id |= part << kPartShift; + CHECK_NE(id, 0); + CHECK_EQ(id & (1u << 31), 0); + s = allocDesc(size); + s->id = id; + s->hash = h; + s->size = size; + internal_memcpy(s->stack, stack, size * sizeof(uptr)); + s->link = s2; + unlock(p, s); + return id; +} + +const uptr *StackDepotGet(u32 id, uptr *size) { + if (id == 0) + return 0; + CHECK_EQ(id & (1u << 31), 0); + // High kPartBits contain part id, so we need to scan at most kPartSize lists. + uptr part = id >> kPartShift; + for (int i = 0; i != kPartSize; i++) { + uptr idx = part * kPartSize + i; + CHECK_LT(idx, kTabSize); + atomic_uintptr_t *p = &depot.tab[idx]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + for (; s; s = s->link) { + if (s->id == id) { + *size = s->size; + return s->stack; + } + } + } + *size = 0; + return 0; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h new file mode 100644 index 00000000000..c4c388aa74d --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h @@ -0,0 +1,27 @@ +//===-- sanitizer_stackdepot.h ----------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STACKDEPOT_H +#define SANITIZER_STACKDEPOT_H + +#include "sanitizer/common_interface_defs.h" + +namespace __sanitizer { + +// StackDepot efficiently stores huge amounts of stack traces. + +// Maps stack trace to an unique id. +u32 StackDepotPut(const uptr *stack, uptr size); +// Retrieves a stored stack trace by the id. +const uptr *StackDepotGet(u32 id, uptr *size); + +} // namespace __sanitizer + +#endif // SANITIZER_STACKDEPOT_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc new file mode 100644 index 00000000000..f6d7a0966c2 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc @@ -0,0 +1,245 @@ +//===-- sanitizer_stacktrace.cc -------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { +static const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix) { + if (filepath == internal_strstr(filepath, strip_file_prefix)) + return filepath + internal_strlen(strip_file_prefix); + return filepath; +} + +// ----------------------- StackTrace ----------------------------- {{{1 +// PCs in stack traces are actually the return addresses, that is, +// addresses of the next instructions after the call. That's why we +// decrement them. +static uptr patch_pc(uptr pc) { +#ifdef __arm__ + // Cancel Thumb bit. + pc = pc & (~1); +#endif + return pc - 1; +} + +static void PrintStackFramePrefix(uptr frame_num, uptr pc) { + Printf(" #%zu 0x%zx", frame_num, pc); +} + +static void PrintSourceLocation(const char *file, int line, int column, + const char *strip_file_prefix) { + CHECK(file); + Printf(" %s", StripPathPrefix(file, strip_file_prefix)); + if (line > 0) { + Printf(":%d", line); + if (column > 0) + Printf(":%d", column); + } +} + +static void PrintModuleAndOffset(const char *module, uptr offset, + const char *strip_file_prefix) { + Printf(" (%s+0x%zx)", StripPathPrefix(module, strip_file_prefix), offset); +} + +void StackTrace::PrintStack(const uptr *addr, uptr size, + bool symbolize, const char *strip_file_prefix, + SymbolizeCallback symbolize_callback ) { + MemoryMappingLayout proc_maps; + InternalScopedBuffer<char> buff(kPageSize * 2); + InternalScopedBuffer<AddressInfo> addr_frames(64); + uptr frame_num = 0; + for (uptr i = 0; i < size && addr[i]; i++) { + uptr pc = patch_pc(addr[i]); + uptr addr_frames_num = 0; // The number of stack frames for current + // instruction address. + if (symbolize_callback) { + if (symbolize_callback((void*)pc, buff.data(), buff.size())) { + addr_frames_num = 1; + PrintStackFramePrefix(frame_num, pc); + // We can't know anything about the string returned by external + // symbolizer, but if it starts with filename, try to strip path prefix + // from it. + Printf(" %s\n", StripPathPrefix(buff.data(), strip_file_prefix)); + frame_num++; + } + } else if (symbolize) { + // Use our own (online) symbolizer, if necessary. + addr_frames_num = SymbolizeCode(pc, addr_frames.data(), + addr_frames.size()); + for (uptr j = 0; j < addr_frames_num; j++) { + AddressInfo &info = addr_frames[j]; + PrintStackFramePrefix(frame_num, pc); + if (info.function) { + Printf(" in %s", info.function); + } + if (info.file) { + PrintSourceLocation(info.file, info.line, info.column, + strip_file_prefix); + } else if (info.module) { + PrintModuleAndOffset(info.module, info.module_offset, + strip_file_prefix); + } + Printf("\n"); + info.Clear(); + frame_num++; + } + } + if (addr_frames_num == 0) { + // If online symbolization failed, try to output at least module and + // offset for instruction. + PrintStackFramePrefix(frame_num, pc); + uptr offset; + if (proc_maps.GetObjectNameAndOffset(pc, &offset, + buff.data(), buff.size())) { + PrintModuleAndOffset(buff.data(), offset, strip_file_prefix); + } + Printf("\n"); + frame_num++; + } + } +} + +uptr StackTrace::GetCurrentPc() { + return GET_CALLER_PC(); +} + +void StackTrace::FastUnwindStack(uptr pc, uptr bp, + uptr stack_top, uptr stack_bottom) { + CHECK(size == 0 && trace[0] == pc); + size = 1; + uptr *frame = (uptr*)bp; + uptr *prev_frame = frame; + while (frame >= prev_frame && + frame < (uptr*)stack_top - 2 && + frame > (uptr*)stack_bottom && + size < max_size) { + uptr pc1 = frame[1]; + if (pc1 != pc) { + trace[size++] = pc1; + } + prev_frame = frame; + frame = (uptr*)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. +SANITIZER_INTERFACE_ATTRIBUTE +uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) { +#if __WORDSIZE == 32 + // Don't compress, just copy. + uptr res = 0; + for (uptr 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. + uptr prev_pc = 0; + const uptr kMaxOffset = (1ULL << 30) - 1; + uptr c_index = 0; + uptr res = 0; + for (uptr i = 0, n = stack->size; i < n; i++) { + uptr pc = stack->trace[i]; + if (!pc) break; + if ((s64)pc < 0) break; + // Printf("C pc[%zu] %zx\n", i, pc); + if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { + uptr offset = (s64)(pc - prev_pc); + offset |= (1U << 31); + if (c_index >= size) break; + // Printf("C co[%zu] offset %zx\n", i, offset); + compressed[c_index++] = offset; + } else { + uptr hi = pc >> 32; + uptr lo = (pc << 32) >> 32; + CHECK_EQ((hi & (1 << 31)), 0); + if (c_index + 1 >= size) break; + // Printf("C co[%zu] hi/lo: %zx %zx\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 + StackTrace check_stack; + UncompressStack(&check_stack, compressed, size); + if (res < check_stack.size) { + Printf("res %zu check_stack.size %zu; c_size %zu\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_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace, + check_stack.size * sizeof(uptr))); +#endif + + return res; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void StackTrace::UncompressStack(StackTrace *stack, + u32 *compressed, uptr size) { +#if __WORDSIZE == 32 + // Don't uncompress, just copy. + stack->size = 0; + for (uptr i = 0; i < size && i < kStackTraceMax; i++) { + if (!compressed[i]) break; + stack->size++; + stack->trace[i] = compressed[i]; + } +#else // 64 bits, uncompress + uptr prev_pc = 0; + stack->size = 0; + for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { + u32 x = compressed[i]; + uptr pc = 0; + if (x & (1U << 31)) { + // Printf("U co[%zu] offset: %x\n", i, x); + // this is an offset + s32 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; + uptr hi = x; + uptr lo = compressed[i+1]; + // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); + i++; + pc = (hi << 32) | lo; + if (!pc) break; + } + // Printf("U pc[%zu] %zx\n", stack->size, pc); + stack->trace[stack->size++] = pc; + prev_pc = pc; + } +#endif // __WORDSIZE +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h new file mode 100644 index 00000000000..a7934c65e1e --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -0,0 +1,73 @@ +//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STACKTRACE_H +#define SANITIZER_STACKTRACE_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +static const uptr kStackTraceMax = 256; + +struct StackTrace { + typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, + int out_size); + uptr size; + uptr max_size; + uptr trace[kStackTraceMax]; + static void PrintStack(const uptr *addr, uptr size, + bool symbolize, const char *strip_file_prefix, + SymbolizeCallback symbolize_callback); + void CopyTo(uptr *dst, uptr dst_size) { + for (uptr i = 0; i < size && i < dst_size; i++) + dst[i] = trace[i]; + for (uptr i = size; i < dst_size; i++) + dst[i] = 0; + } + + void CopyFrom(uptr *src, uptr src_size) { + size = src_size; + if (size > kStackTraceMax) size = kStackTraceMax; + for (uptr i = 0; i < size; i++) { + trace[i] = src[i]; + } + } + + void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom); + + static uptr GetCurrentPc(); + + static uptr CompressStack(StackTrace *stack, + u32 *compressed, uptr size); + static void UncompressStack(StackTrace *stack, + u32 *compressed, uptr size); +}; + +} // namespace __sanitizer + +// Use this macro if you want to print stack trace with the caller +// of the current function in the top frame. +#define GET_CALLER_PC_BP_SP \ + uptr bp = GET_CURRENT_FRAME(); \ + uptr pc = GET_CALLER_PC(); \ + uptr local_stack; \ + uptr sp = (uptr)&local_stack + +// Use this macro if you want to print stack trace with the current +// function in the top frame. +#define GET_CURRENT_PC_BP_SP \ + uptr bp = GET_CURRENT_FRAME(); \ + uptr pc = StackTrace::GetCurrentPc(); \ + uptr local_stack; \ + uptr sp = (uptr)&local_stack + + +#endif // SANITIZER_STACKTRACE_H diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc new file mode 100644 index 00000000000..66ac3c8a246 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc @@ -0,0 +1,311 @@ +//===-- sanitizer_symbolizer.cc -------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. See sanitizer_symbolizer.h for details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +void AddressInfo::Clear() { + InternalFree(module); + InternalFree(function); + InternalFree(file); + internal_memset(this, 0, sizeof(AddressInfo)); +} + +LoadedModule::LoadedModule(const char *module_name, uptr base_address) { + full_name_ = internal_strdup(module_name); + base_address_ = base_address; + n_ranges_ = 0; +} + +void LoadedModule::addAddressRange(uptr beg, uptr end) { + CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); + ranges_[n_ranges_].beg = beg; + ranges_[n_ranges_].end = end; + n_ranges_++; +} + +bool LoadedModule::containsAddress(uptr address) const { + for (uptr i = 0; i < n_ranges_; i++) { + if (ranges_[i].beg <= address && address < ranges_[i].end) + return true; + } + return false; +} + +// Extracts the prefix of "str" that consists of any characters not +// present in "delims" string, and copies this prefix to "result", allocating +// space for it. +// Returns a pointer to "str" after skipping extracted prefix and first +// delimiter char. +static const char *ExtractToken(const char *str, const char *delims, + char **result) { + uptr prefix_len = internal_strcspn(str, delims); + *result = (char*)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') prefix_end++; + return prefix_end; +} + +// Same as ExtractToken, but converts extracted token to integer. +static const char *ExtractInt(const char *str, const char *delims, + int *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +// ExternalSymbolizer encapsulates communication between the tool and +// external symbolizer program, running in a different subprocess, +// For now we assume the following protocol: +// For each request of the form +// <module_name> <module_offset> +// passed to STDIN, external symbolizer prints to STDOUT response: +// <function_name> +// <file_name>:<line_number>:<column_number> +// <function_name> +// <file_name>:<line_number>:<column_number> +// ... +// <empty line> +class ExternalSymbolizer { + public: + ExternalSymbolizer(const char *path, int input_fd, int output_fd) + : path_(path), + input_fd_(input_fd), + output_fd_(output_fd), + times_restarted_(0) { + CHECK(path_); + CHECK_NE(input_fd_, kInvalidFd); + CHECK_NE(output_fd_, kInvalidFd); + } + + // Returns the number of frames for a given address, or zero if + // symbolization failed. + uptr SymbolizeCode(uptr addr, const char *module_name, uptr module_offset, + AddressInfo *frames, uptr max_frames) { + CHECK(module_name); + // FIXME: Make sure this buffer always has sufficient size to hold + // large debug info. + static const int kMaxBufferSize = 4096; + InternalScopedBuffer<char> buffer(kMaxBufferSize); + char *buffer_data = buffer.data(); + internal_snprintf(buffer_data, kMaxBufferSize, "%s 0x%zx\n", + module_name, module_offset); + if (!writeToSymbolizer(buffer_data, internal_strlen(buffer_data))) + return 0; + + if (!readFromSymbolizer(buffer_data, kMaxBufferSize)) + return 0; + const char *str = buffer_data; + uptr frame_id; + CHECK_GT(max_frames, 0); + for (frame_id = 0; frame_id < max_frames; frame_id++) { + AddressInfo *info = &frames[frame_id]; + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + break; + } + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + info->function = function_name; + // Parse <file>:<line>:<column> buffer. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + const char *line_info = ExtractToken(file_line_info, ":", &info->file); + line_info = ExtractInt(line_info, ":", &info->line); + line_info = ExtractInt(line_info, "", &info->column); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } + } + if (frame_id == 0) { + // Make sure we return at least one frame. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + frame_id = 1; + } + return frame_id; + } + + bool Restart() { + if (times_restarted_ >= kMaxTimesRestarted) return false; + times_restarted_++; + internal_close(input_fd_); + internal_close(output_fd_); + return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); + } + + private: + bool readFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read = internal_read(input_fd_, buffer + read_len, + max_length - read_len); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); + return false; + } + read_len += just_read; + // Empty line marks the end of symbolizer output. + if (read_len >= 2 && buffer[read_len - 1] == '\n' && + buffer[read_len - 2] == '\n') { + break; + } + } + return true; + } + bool writeToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = internal_write(output_fd_, buffer, length); + if (write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); + return false; + } + return true; + } + + const char *path_; + int input_fd_; + int output_fd_; + + static const uptr kMaxTimesRestarted = 5; + uptr times_restarted_; +}; + +static LowLevelAllocator symbolizer_allocator; // Linker initialized. + +class Symbolizer { + public: + uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { + if (max_frames == 0) + return 0; + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) + return 0; + const char *module_name = module->full_name(); + uptr module_offset = addr - module->base_address(); + uptr actual_frames = 0; + if (external_symbolizer_ == 0) { + ReportExternalSymbolizerError( + "WARNING: Trying to symbolize code, but external " + "symbolizer is not initialized!\n"); + } else { + while (true) { + actual_frames = external_symbolizer_->SymbolizeCode( + addr, module_name, module_offset, frames, max_frames); + if (actual_frames > 0) { + // Symbolization was successful. + break; + } + // Try to restart symbolizer subprocess. If we don't succeed, forget + // about it and don't try to use it later. + if (!external_symbolizer_->Restart()) { + ReportExternalSymbolizerError( + "WARNING: Failed to use and restart external symbolizer!\n"); + external_symbolizer_ = 0; + break; + } + } + } + if (external_symbolizer_ == 0) { + // External symbolizer was not initialized or failed. Fill only data + // about module name and offset. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + return 1; + } + // Otherwise, the data was filled by external symbolizer. + return actual_frames; + } + bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { + int input_fd, output_fd; + if (!StartSymbolizerSubprocess(path_to_symbolizer, &input_fd, &output_fd)) + return false; + void *mem = symbolizer_allocator.Allocate(sizeof(ExternalSymbolizer)); + external_symbolizer_ = new(mem) ExternalSymbolizer(path_to_symbolizer, + input_fd, output_fd); + return true; + } + + private: + LoadedModule *FindModuleForAddress(uptr address) { + if (modules_ == 0) { + modules_ = (LoadedModule*)(symbolizer_allocator.Allocate( + kMaxNumberOfModuleContexts * sizeof(LoadedModule))); + CHECK(modules_); + n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts); + CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); + } + for (uptr i = 0; i < n_modules_; i++) { + if (modules_[i].containsAddress(address)) { + return &modules_[i]; + } + } + return 0; + } + void ReportExternalSymbolizerError(const char *msg) { + // Don't use atomics here for now, as SymbolizeCode can't be called + // from multiple threads anyway. + static bool reported; + if (!reported) { + Report(msg); + reported = true; + } + } + + static const uptr kMaxNumberOfModuleContexts = 4096; + LoadedModule *modules_; // Array of module descriptions is leaked. + uptr n_modules_; + + ExternalSymbolizer *external_symbolizer_; // Leaked. +}; + +static Symbolizer symbolizer; // Linker initialized. + +uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) { + return symbolizer.SymbolizeCode(address, frames, max_frames); +} + +bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { + return symbolizer.InitializeExternalSymbolizer(path_to_symbolizer); +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h new file mode 100644 index 00000000000..83adf025282 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h @@ -0,0 +1,97 @@ +//===-- sanitizer_symbolizer.h ----------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Symbolizer is intended to be used by both +// AddressSanitizer and ThreadSanitizer to symbolize a given +// address. It is an analogue of addr2line utility and allows to map +// instruction address to a location in source code at run-time. +// +// Symbolizer is planned to use debug information (in DWARF format) +// in a binary via interface defined in "llvm/DebugInfo/DIContext.h" +// +// Symbolizer code should be called from the run-time library of +// dynamic tools, and generally should not call memory allocation +// routines or other system library functions intercepted by those tools. +// Instead, Symbolizer code should use their replacements, defined in +// "compiler-rt/lib/sanitizer_common/sanitizer_libc.h". +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_H +#define SANITIZER_SYMBOLIZER_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +// WARNING: Do not include system headers here. See details above. + +namespace __sanitizer { + +struct AddressInfo { + uptr address; + char *module; + uptr module_offset; + char *function; + char *file; + int line; + int column; + + AddressInfo() { + internal_memset(this, 0, sizeof(AddressInfo)); + } + // Deletes all strings and sets all fields to zero. + void Clear(); + + void FillAddressAndModuleInfo(uptr addr, const char *mod_name, + uptr mod_offset) { + address = addr; + module = internal_strdup(mod_name); + module_offset = mod_offset; + } +}; + +// Fills at most "max_frames" elements of "frames" with descriptions +// for a given address (in all inlined functions). Returns the number +// of descriptions actually filled. +// This function should NOT be called from two threads simultaneously. +uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames); + +// Starts external symbolizer program in a subprocess. Sanitizer communicates +// with external symbolizer via pipes. +bool InitializeExternalSymbolizer(const char *path_to_symbolizer); + +class LoadedModule { + public: + LoadedModule(const char *module_name, uptr base_address); + void addAddressRange(uptr beg, uptr end); + bool containsAddress(uptr address) const; + + const char *full_name() const { return full_name_; } + uptr base_address() const { return base_address_; } + + private: + struct AddressRange { + uptr beg; + uptr end; + }; + char *full_name_; + uptr base_address_; + static const uptr kMaxNumberOfAddressRanges = 8; + AddressRange ranges_[kMaxNumberOfAddressRanges]; + uptr n_ranges_; +}; + +// Creates external symbolizer connected via pipe, user should write +// to output_fd and read from input_fd. +bool StartSymbolizerSubprocess(const char *path_to_symbolizer, + int *input_fd, int *output_fd); + +// OS-dependent function that fills array with descriptions of at most +// "max_modules" currently loaded modules. Returns the number of +// initialized modules. +uptr GetListOfModules(LoadedModule *modules, uptr max_modules); + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_H diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc new file mode 100644 index 00000000000..50e39a75c3a --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc @@ -0,0 +1,162 @@ +//===-- sanitizer_symbolizer_linux.cc -------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Linux-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// +#ifdef __linux__ +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_symbolizer.h" + +#include <elf.h> +#include <errno.h> +#include <poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#if !defined(__ANDROID__) && !defined(ANDROID) +#include <link.h> +#endif + +namespace __sanitizer { + +bool StartSymbolizerSubprocess(const char *path_to_symbolizer, + int *input_fd, int *output_fd) { + int *infd = NULL; + int *outfd = NULL; + // The client program may close its stdin and/or stdout and/or stderr + // thus allowing socketpair to reuse file descriptors 0, 1 or 2. + // In this case the communication between the forked processes may be + // broken if either the parent or the child tries to close or duplicate + // these descriptors. The loop below produces two pairs of file + // descriptors, each greater than 2 (stderr). + int sock_pair[5][2]; + for (int i = 0; i < 5; i++) { + if (pipe(sock_pair[i]) == -1) { + for (int j = 0; j < i; j++) { + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + Report("WARNING: Can't create a socket pair to start " + "external symbolizer (errno: %d)\n", errno); + return false; + } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { + if (infd == NULL) { + infd = sock_pair[i]; + } else { + outfd = sock_pair[i]; + for (int j = 0; j < i; j++) { + if (sock_pair[j] == infd) continue; + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + break; + } + } + } + CHECK(infd); + CHECK(outfd); + + int pid = fork(); + if (pid == -1) { + // Fork() failed. + internal_close(infd[0]); + internal_close(infd[1]); + internal_close(outfd[0]); + internal_close(outfd[1]); + Report("WARNING: failed to fork external symbolizer " + " (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = getdtablesize(); fd > 2; fd--) + internal_close(fd); + execl(path_to_symbolizer, path_to_symbolizer, (char*)0); + Exit(1); + } + + // Continue execution in parent process. + internal_close(outfd[0]); + internal_close(infd[1]); + *input_fd = infd[0]; + *output_fd = outfd[1]; + return true; +} + +#if defined(__ANDROID__) || defined(ANDROID) +uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { + UNIMPLEMENTED(); + return 0; +} +#else // ANDROID +typedef ElfW(Phdr) Elf_Phdr; + +struct DlIteratePhdrData { + LoadedModule *modules; + uptr current_n; + uptr max_n; +}; + +static const uptr kMaxPathLength = 512; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + if (data->current_n == data->max_n) + return 0; + InternalScopedBuffer<char> module_name(kMaxPathLength); + module_name.data()[0] = '\0'; + if (data->current_n == 0) { + // First module is the binary itself. + uptr module_name_len = internal_readlink( + "/proc/self/exe", module_name.data(), module_name.size()); + CHECK_NE(module_name_len, (uptr)-1); + CHECK_LT(module_name_len, module_name.size()); + module_name[module_name_len] = '\0'; + } else if (info->dlpi_name) { + internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); + } + if (module_name.data()[0] == '\0') + return 0; + void *mem = &data->modules[data->current_n]; + LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), + info->dlpi_addr); + data->current_n++; + for (int i = 0; i < info->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &info->dlpi_phdr[i]; + if (phdr->p_type == PT_LOAD) { + uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; + uptr cur_end = cur_beg + phdr->p_memsz; + cur_module->addAddressRange(cur_beg, cur_end); + } + } + return 0; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { + CHECK(modules); + DlIteratePhdrData data = {modules, 0, max_modules}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return data.current_n; +} +#endif // ANDROID + +} // namespace __sanitizer + +#endif // __linux__ diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc new file mode 100644 index 00000000000..c5ca616d89d --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc @@ -0,0 +1,31 @@ +//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Mac-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// +#ifdef __APPLE__ +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +bool StartSymbolizerSubprocess(const char *path_to_symbolizer, + int *input_fd, int *output_fd) { + UNIMPLEMENTED(); + return false; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { + UNIMPLEMENTED(); + return 0; +} + +} // namespace __sanitizer + +#endif // __APPLE__ diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc new file mode 100644 index 00000000000..7e6ba53e128 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc @@ -0,0 +1,33 @@ +//===-- sanitizer_symbolizer_win.cc ---------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Windows-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 +#include <windows.h> + +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +bool StartSymbolizerSubprocess(const char *path_to_symbolizer, + int *input_fd, int *output_fd) { + UNIMPLEMENTED(); + return false; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { + UNIMPLEMENTED(); + return 0; +}; + +} // namespace __sanitizer + +#endif // _WIN32 diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc new file mode 100644 index 00000000000..314852304d8 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_win.cc @@ -0,0 +1,205 @@ +//===-- sanitizer_win.cc --------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements windows-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 +#include <windows.h> + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +// --------------------- sanitizer_common.h +int GetPid() { + return GetProcessId(GetCurrentProcess()); +} + +uptr GetThreadSelf() { + return GetCurrentThreadId(); +} + +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + CHECK(stack_top); + CHECK(stack_bottom); + MEMORY_BASIC_INFORMATION mbi; + CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0); + // FIXME: is it possible for the stack to not be a single allocation? + // Are these values what ASan expects to get (reserved, not committed; + // including stack guard page) ? + *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize; + *stack_bottom = (uptr)mbi.AllocationBase; +} + + +void *MmapOrDie(uptr size, const char *mem_type) { + void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (rv == 0) { + Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n", + size, size, mem_type); + CHECK("unable to mmap" && 0); + } + return rv; +} + +void UnmapOrDie(void *addr, uptr size) { + if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { + Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n", + size, size, addr); + CHECK("unable to unmap" && 0); + } +} + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { + return VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); +} + +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { + // FIXME: shall we do anything here on Windows? + return true; +} + +void *MapFileToMemory(const char *file_name, uptr *buff_size) { + UNIMPLEMENTED(); + return 0; +} + +const char *GetEnv(const char *name) { + static char env_buffer[32767] = {}; + + // Note: this implementation stores the result in a static buffer so we only + // allow it to be called just once. + static bool called_once = false; + if (called_once) + UNIMPLEMENTED(); + called_once = true; + + DWORD rv = GetEnvironmentVariableA(name, env_buffer, sizeof(env_buffer)); + if (rv > 0 && rv < sizeof(env_buffer)) + return env_buffer; + return 0; +} + +const char *GetPwd() { + UNIMPLEMENTED(); + return 0; +} + +void DumpProcessMap() { + UNIMPLEMENTED(); +} + +void DisableCoreDumper() { + UNIMPLEMENTED(); +} + +void ReExec() { + UNIMPLEMENTED(); +} + +bool StackSizeIsUnlimited() { + UNIMPLEMENTED(); + return false; +} + +void SetStackSizeLimitInBytes(uptr limit) { + UNIMPLEMENTED(); +} + +void SleepForSeconds(int seconds) { + Sleep(seconds * 1000); +} + +void SleepForMillis(int millis) { + Sleep(millis); +} + +void Exit(int exitcode) { + _exit(exitcode); +} + +void Abort() { + abort(); + _exit(-1); // abort is not NORETURN on Windows. +} + +int Atexit(void (*function)(void)) { + return atexit(function); +} + +// ------------------ sanitizer_libc.h +void *internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { + UNIMPLEMENTED(); + return 0; +} + +int internal_munmap(void *addr, uptr length) { + UNIMPLEMENTED(); + return 0; +} + +int internal_close(fd_t fd) { + UNIMPLEMENTED(); + return 0; +} + +fd_t internal_open(const char *filename, bool write) { + UNIMPLEMENTED(); + return 0; +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + UNIMPLEMENTED(); + return 0; +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + if (fd != 2) + UNIMPLEMENTED(); + HANDLE err = GetStdHandle(STD_ERROR_HANDLE); + if (err == 0) + return 0; // FIXME: this might not work on some apps. + DWORD ret; + if (!WriteFile(err, buf, count, &ret, 0)) + return 0; + return ret; +} + +uptr internal_filesize(fd_t fd) { + UNIMPLEMENTED(); + return 0; +} + +int internal_dup2(int oldfd, int newfd) { + UNIMPLEMENTED(); + return 0; +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { + UNIMPLEMENTED(); + return 0; +} + +int internal_sched_yield() { + UNIMPLEMENTED(); + return 0; +} + +} // namespace __sanitizer + +#endif // _WIN32 |