From patchwork Thu Apr 17 13:49:37 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bernhard Reutner-Fischer X-Patchwork-Id: 339938 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D2E0214008F for ; Fri, 18 Apr 2014 00:02:41 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=HSRj/Kf1hY0egA0ybMOCSd/PG3GZSr3zPlp9Pv9dOh6BMtWVOYfZx A7h9URg7TNdzjNm7Ny8cpSL7gYhUnuZ69KVPRSpJVq4xhEG/jOOptRDIJ/Ge4R1L iFXL6zCK4PFY0GZZDGuPnmkmGbo0vtcFFJ5H32ju+qHxaASpcg/e+Y= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=3GYaW23zDeeg2YXqV98D2w/4pFY=; b=F/QIgwm9mqFQR3VRWych b9+sS7BFpcsLM6Qua0Is4lqBTF85svFchXF6Btp2niA8m02Bg5NBmq0d8y6g0K40 zv6cPGBYJMnyoPiZi+MSZTIq+WdDtPAkx+OPf3HUU3D1Z7qfDe+UQuMxmQsLAFKY RsQn/BN3GvJ8MiMXsqC7swo= Received: (qmail 31503 invoked by alias); 17 Apr 2014 14:01:57 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 31446 invoked by uid 89); 17 Apr 2014 14:01:56 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.6 required=5.0 tests=BAYES_00, FREEMAIL_FROM, RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: grace.univie.ac.at Received: from grace.univie.ac.at (HELO grace.univie.ac.at) (131.130.3.115) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 17 Apr 2014 14:01:51 +0000 Received: from jarvis.univie.ac.at ([131.130.3.112] helo=jarvis.univie.ac.at) by grace.univie.ac.at with esmtp (Exim 4.82) (envelope-from ) id 1Wamst-0003e3-4m; Thu, 17 Apr 2014 16:01:47 +0200 Received: from [2001:62a:4:203:2e59:e5ff:feb9:72ed] (helo=nbbrfq.loc) by jarvis.univie.ac.at with esmtps (TLSv1.2:DHE-RSA-AES256-SHA256:256) (Exim 4.82) (envelope-from ) id 1Wamst-0002dv-35; Thu, 17 Apr 2014 16:01:47 +0200 Received: from b by nbbrfq.loc with local (Exim 4.82) (envelope-from ) id 1WamhE-0005xD-NE; Thu, 17 Apr 2014 15:49:44 +0200 From: Bernhard Reutner-Fischer To: Konstantin Serebryany Cc: Bernhard Reutner-Fischer , Jakub Jelinek , GCC Patches , Dodji Seketeli , Kostya Serebryany , Dmitry Vyukov , llvm-commits@cs.uiuc.edu Subject: [PATCH 3/3] [LLVM] [sanitizer] add conditionals for libc Date: Thu, 17 Apr 2014 15:49:37 +0200 Message-Id: <1397742577-22841-4-git-send-email-rep.dot.nop@gmail.com> In-Reply-To: <1397742577-22841-1-git-send-email-rep.dot.nop@gmail.com> References: <20130405094956.GB20334@tucnak.redhat.com> <1397742577-22841-1-git-send-email-rep.dot.nop@gmail.com> X-Univie-Virus-Scan: scanned by ClamAV on jarvis.univie.ac.at X-IsSubscribed: yes Conditionalize usage of dlvsym(), nanosleep(), usleep(); Conditionalize layout of struct sigaction and type of it's member sa_flags. Conditionalize glob_t members gl_closedir, gl_readdir, gl_opendir, gl_flags, gl_lstat, gl_stat. Check for availability of glob.h for use with above members. Check for availability of netrom/netrom.h, sys/ustat.h (for obsolete ustat() function), utime.h (for obsolete utime() function), wordexp.h. Determine size of sigset_t instead of hardcoding it. Determine size of struct statfs64, if available. Leave defaults to match what glibc expects but probe them for uClibc. Signed-off-by: Bernhard Reutner-Fischer --- CMakeLists.txt | 58 +++++++ cmake/Modules/CompilerRTUtils.cmake | 15 ++ cmake/Modules/FunctionExistsNotStub.cmake | 56 +++++++ lib/interception/interception_linux.cc | 2 + lib/interception/interception_linux.h | 9 ++ .../sanitizer_common_interceptors.inc | 101 +++++++++++- .../sanitizer_platform_limits_posix.cc | 44 ++++- .../sanitizer_platform_limits_posix.h | 27 +++- lib/sanitizer_common/sanitizer_posix_libcdep.cc | 9 ++ make/platform/clang_linux.mk | 180 +++++++++++++++++++++ make/platform/clang_linux_test_libc.c | 68 ++++++++ 11 files changed, 561 insertions(+), 8 deletions(-) create mode 100644 cmake/Modules/FunctionExistsNotStub.cmake create mode 100644 make/platform/clang_linux_test_libc.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e1a7a1f..af8073e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,6 +330,64 @@ if(APPLE) -isysroot ${IOSSIM_SDK_DIR}) endif() +set(ct_c ${COMPILER_RT_SOURCE_DIR}/make/platform/clang_linux_test_libc.c) +check_include_file(sys/ustat.h HAVE_SYS_USTAT_H) +check_include_file(utime.h HAVE_UTIME_H) +check_include_file(wordexp.h HAVE_WORDEXP_H) +check_include_file(glob.h HAVE_GLOB_H) +include(FunctionExistsNotStub) +check_function_exists_not_stub(${ct_c} nanosleep HAVE_NANOSLEEP) +check_function_exists_not_stub(${ct_c} usleep HAVE_USLEEP) +include(CheckTypeSize) +# check for sizeof sigset_t +set(oCMAKE_EXTRA_INCLUDE_FILES "${CMAKE_EXTRA_INCLUDE_FILES}") +set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} signal.h) +check_type_size("sigset_t" SIZEOF_SIGSET_T BUILTIN_TYPES_ONLY) +if(EXISTS HAVE_SIZEOF_SIGSET_T) + set(SIZEOF_SIGSET_T ${HAVE_SIZEOF_SIGSET_T}) +endif() +set(CMAKE_EXTRA_INCLUDE_FILES "${oCMAKE_EXTRA_INCLUDE_FILES}") +# check for sizeof struct statfs64 +set(oCMAKE_EXTRA_INCLUDE_FILES "${CMAKE_EXTRA_INCLUDE_FILES}") +check_include_file(sys/statfs.h HAVE_SYS_STATFS_H) +check_include_file(sys/vfs.h HAVE_SYS_VFS_H) +if(HAVE_SYS_STATFS_H) + set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} sys/statfs.h) +endif() +if(HAVE_SYS_VFS_H) + set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} sys/vfs.h) +endif() +# Have to pass _LARGEFILE64_SOURCE otherwise there is no struct statfs64. +# We forcefully enable LFS to retain glibc legacy behaviour herein. +set(oCMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) +set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_LARGEFILE64_SOURCE) +check_type_size("struct statfs64" SIZEOF_STRUCT_STATFS64) +if(EXISTS HAVE_SIZEOF_STRUCT_STATFS64) + set(SIZEOF_STRUCT_STATFS64 ${HAVE_SIZEOF_STRUCT_STATFS64}) +else() + set(CMAKE_REQUIRED_DEFINITIONS ${oCMAKE_REQUIRED_DEFINITIONS}) +endif() +set(CMAKE_EXTRA_INCLUDE_FILES "${oCMAKE_EXTRA_INCLUDE_FILES}") +# do not set(CMAKE_REQUIRED_DEFINITIONS ${oCMAKE_REQUIRED_DEFINITIONS}) +# it back here either way. +include(CheckStructHasMember) +check_struct_has_member(glob_t gl_flags glob.h HAVE_GLOB_T_GL_FLAGS) +check_struct_has_member(glob_t gl_closedir glob.h HAVE_GLOB_T_GL_CLOSEDIR) +check_struct_has_member(glob_t gl_readdir glob.h HAVE_GLOB_T_GL_READDIR) +check_struct_has_member(glob_t gl_opendir glob.h HAVE_GLOB_T_GL_OPENDIR) +check_struct_has_member(glob_t gl_lstat glob.h HAVE_GLOB_T_GL_LSTAT) +check_struct_has_member(glob_t gl_stat glob.h HAVE_GLOB_T_GL_STAT) + +# folks seem to have an aversion to configure_file? So be it.. +foreach(x HAVE_SYS_USTAT_H HAVE_UTIME_H HAVE_WORDEXP_H HAVE_GLOB_H +HAVE_NANOSLEEP HAVE_USLEEP SIZEOF_SIGSET_T SIZEOF_STRUCT_STATFS64 +HAVE_GLOB_T_GL_FLAGS HAVE_GLOB_T_GL_CLOSEDIR +HAVE_GLOB_T_GL_READDIR HAVE_GLOB_T_GL_OPENDIR +HAVE_GLOB_T_GL_LSTAT HAVE_GLOB_T_GL_STAT) +def_undef_string(${x} SANITIZER_COMMON_CFLAGS) +endforeach() + + # Architectures supported by Sanitizer runtimes. Specific sanitizers may # support only subset of these (e.g. TSan works on x86_64 only). filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake index e22e775..3a0beec 100644 --- a/cmake/Modules/CompilerRTUtils.cmake +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -59,3 +59,18 @@ macro(append_no_rtti_flag list) append_if(COMPILER_RT_HAS_FNO_RTTI_FLAG -fno-rtti ${list}) append_if(COMPILER_RT_HAS_GR_FLAG /GR- ${list}) endmacro() + +# Appends -Dvalue=${value}/-Uvalue to all strings in ARGN +macro(def_undef_string condition) + if("${${condition}}" STREQUAL "") + foreach(str ${ARGN}) + set(${str} "${${str}} -U${condition}") + endforeach() + else() + foreach(str ${ARGN}) + set(${str} "${${str}} '-D${condition}=${${condition}}'") + endforeach() + endif() +endmacro() + + diff --git a/cmake/Modules/FunctionExistsNotStub.cmake b/cmake/Modules/FunctionExistsNotStub.cmake new file mode 100644 index 0000000..9f944dd --- /dev/null +++ b/cmake/Modules/FunctionExistsNotStub.cmake @@ -0,0 +1,56 @@ +INCLUDE(CheckFunctionExists) + +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories + +macro(CHECK_FUNCTION_EXISTS_NOT_STUB CSRC FUNCTION VARIABLE) + if(NOT DEFINED CHECKED_STUB_${VARIABLE}) + set(CHECKED_STUB_${VARIABLE} "done" CACHE INTERNAL "checked stub of ${FUNCTION}") + CHECK_FUNCTION_EXISTS("${FUNCTION}" "${VARIABLE}") + if(DEFINED ${VARIABLE}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Looking for stubbed out ${FUNCTION}") + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(MACRO_CHECK_FUNCTION_EXISTS_NOT_STUB_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else() + set(MACRO_CHECK_FUNCTION_EXISTS_NOT_STUB_INCLUDES) + endif() + set(MACRO_CHECK_FUNCTION_EXISTS_NOT_STUB_FLAGS ${CMAKE_REQUIRED_FLAGS}) + try_compile(${VARIABLE} + ${CMAKE_BINARY_DIR} + "${CSRC}" + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS + "-DCOMPILE_DEFINITIONS:STRING=-DL_features_h -DL_AC_CHECK_FUNC=${FUNCTION} -DL_AC_CHECK_FUNC_stub='defined __stub_${FUNCTION} || defined __stub___${FUNCTION}'" + "${MACRO_CHECK_FUNCTION_EXISTS_NOT_STUB_FLAGS}" + "${MACRO_CHECK_FUNCTION_EXISTS_NOT_STUB_INCLUDES}" + OUTPUT_VARIABLE OUTPUT + ) + endif() + if(${VARIABLE}) + set(${VARIABLE} 1 CACHE INTERNAL "Have function ${FUNCTION}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if function ${FUNCTION} is a stub " + "passed with the following output:\n" + "${OUTPUT}\n\n") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Looking for stubbed out ${FUNCTION} - no stub") + endif() + else() + set(${VARIABLE} "" CACHE INTERNAL "Have function ${FUNCTION}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if function ${FUNCTION} is a stub " + "failed with the following output:\n" + "${OUTPUT}\n\n") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Looking for stubbed out ${FUNCTION} - stub found") + endif() + endif() + endif() +endmacro() diff --git a/lib/interception/interception_linux.cc b/lib/interception/interception_linux.cc index 6e908ac..c9b4a6e 100644 --- a/lib/interception/interception_linux.cc +++ b/lib/interception/interception_linux.cc @@ -24,11 +24,13 @@ bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, return real == wrapper; } +#ifdef HAVE_DLVSYM #if !defined(__ANDROID__) // android does not have dlvsym void *GetFuncAddrVer(const char *func_name, const char *ver) { return dlvsym(RTLD_NEXT, func_name, ver); } #endif // !defined(__ANDROID__) +#endif // HAVE_DLVSYM } // namespace __interception diff --git a/lib/interception/interception_linux.h b/lib/interception/interception_linux.h index d3f774b..4802fe5 100644 --- a/lib/interception/interception_linux.h +++ b/lib/interception/interception_linux.h @@ -25,7 +25,9 @@ namespace __interception { // returns true if a function with the given name was found. bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, uptr real, uptr wrapper); +# ifdef HAVE_DLVSYM void *GetFuncAddrVer(const char *func_name, const char *ver); +# endif /* HAVE_DLVSYM */ } // namespace __interception #define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \ @@ -43,5 +45,12 @@ void *GetFuncAddrVer(const char *func_name, const char *ver); INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) #endif // !defined(__ANDROID__) +#ifndef HAVE_DLVSYM +/* Undo marketing crap above. Probe functionality, use result to decide. */ +# undef INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD +# define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ + INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) +#endif + #endif // INTERCEPTION_LINUX_H #endif // __linux__ || __FreeBSD__ diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc index 0d076a0..0a767d4 100644 --- a/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -1273,33 +1273,43 @@ static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { static THREADLOCAL __sanitizer_glob_t *pglob_copy; +#ifdef HAVE_GLOB_T_GL_CLOSEDIR static void wrapped_gl_closedir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); IndirectExternCall(pglob_copy->gl_closedir)(dir); } +#endif +#ifdef HAVE_GLOB_T_GL_READDIR static void *wrapped_gl_readdir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); return IndirectExternCall(pglob_copy->gl_readdir)(dir); } +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR static void *wrapped_gl_opendir(const char *s) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return IndirectExternCall(pglob_copy->gl_opendir)(s); } +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT static int wrapped_gl_lstat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return IndirectExternCall(pglob_copy->gl_lstat)(s, st); } +#endif +#ifdef HAVE_GLOB_T_GL_STAT static int wrapped_gl_stat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return IndirectExternCall(pglob_copy->gl_stat)(s, st); } +#endif INTERCEPTOR(int, glob, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), @@ -1307,24 +1317,64 @@ INTERCEPTOR(int, glob, const char *pattern, int flags, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); __sanitizer_glob_t glob_copy = { - 0, 0, 0, - 0, wrapped_gl_closedir, wrapped_gl_readdir, - wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + 0, + 0, + 0 +#ifdef HAVE_GLOB_T_GL_FLAGS + ,0 +#endif +#ifdef HAVE_GLOB_T_GL_CLOSEDIR + ,wrapped_gl_closedir +#endif +#ifdef HAVE_GLOB_T_GL_READDIR + ,wrapped_gl_readdir +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR + ,wrapped_gl_opendir +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT + ,wrapped_gl_lstat +#endif +#ifdef HAVE_GLOB_T_GL_STAT + ,wrapped_gl_stat +#endif + }; + if (flags & glob_altdirfunc) { +#ifdef HAVE_GLOB_T_GL_CLOSEDIR Swap(pglob->gl_closedir, glob_copy.gl_closedir); +#endif +#ifdef HAVE_GLOB_T_GL_READDIR Swap(pglob->gl_readdir, glob_copy.gl_readdir); +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR Swap(pglob->gl_opendir, glob_copy.gl_opendir); +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT Swap(pglob->gl_lstat, glob_copy.gl_lstat); +#endif +#ifdef HAVE_GLOB_T_GL_STAT Swap(pglob->gl_stat, glob_copy.gl_stat); +#endif pglob_copy = &glob_copy; } int res = REAL(glob)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { +#ifdef HAVE_GLOB_T_GL_CLOSEDIR Swap(pglob->gl_closedir, glob_copy.gl_closedir); +#endif +#ifdef HAVE_GLOB_T_GL_READDIR Swap(pglob->gl_readdir, glob_copy.gl_readdir); +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR Swap(pglob->gl_opendir, glob_copy.gl_opendir); +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT Swap(pglob->gl_lstat, glob_copy.gl_lstat); +#endif +#ifdef HAVE_GLOB_T_GL_STAT Swap(pglob->gl_stat, glob_copy.gl_stat); +#endif } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); @@ -1337,24 +1387,63 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); __sanitizer_glob_t glob_copy = { - 0, 0, 0, - 0, wrapped_gl_closedir, wrapped_gl_readdir, - wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + 0, + 0, + 0 +#ifdef HAVE_GLOB_T_GL_FLAGS + ,0 +#endif +#ifdef HAVE_GLOB_T_GL_CLOSEDIR + ,wrapped_gl_closedir +#endif +#ifdef HAVE_GLOB_T_GL_READDIR + ,wrapped_gl_readdir +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR + ,wrapped_gl_opendir +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT + ,wrapped_gl_lstat +#endif +#ifdef HAVE_GLOB_T_GL_STAT + ,wrapped_gl_stat +#endif + }; if (flags & glob_altdirfunc) { +#ifdef HAVE_GLOB_T_GL_CLOSEDIR Swap(pglob->gl_closedir, glob_copy.gl_closedir); +#endif +#ifdef HAVE_GLOB_T_GL_READDIR Swap(pglob->gl_readdir, glob_copy.gl_readdir); +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR Swap(pglob->gl_opendir, glob_copy.gl_opendir); +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT Swap(pglob->gl_lstat, glob_copy.gl_lstat); +#endif +#ifdef HAVE_GLOB_T_GL_STAT Swap(pglob->gl_stat, glob_copy.gl_stat); +#endif pglob_copy = &glob_copy; } int res = REAL(glob64)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { +#ifdef HAVE_GLOB_T_GL_CLOSEDIR Swap(pglob->gl_closedir, glob_copy.gl_closedir); +#endif +#ifdef HAVE_GLOB_T_GL_READDIR Swap(pglob->gl_readdir, glob_copy.gl_readdir); +#endif +#ifdef HAVE_GLOB_T_GL_OPENDIR Swap(pglob->gl_opendir, glob_copy.gl_opendir); +#endif +#ifdef HAVE_GLOB_T_GL_LSTAT Swap(pglob->gl_lstat, glob_copy.gl_lstat); +#endif +#ifdef HAVE_GLOB_T_GL_STAT Swap(pglob->gl_stat, glob_copy.gl_stat); +#endif } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index 9ae4870..645777f 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -103,14 +103,18 @@ #endif #if SANITIZER_LINUX || SANITIZER_FREEBSD +# ifdef HAVE_UTIME_H # include +# endif # include #endif #if !SANITIZER_ANDROID #include #include +# ifdef HAVE_WORDEXP_H #include +# endif #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID @@ -119,7 +123,9 @@ #include #include #include +# ifdef HAVE_NETROM_NETROM_H #include +# endif #include #include #include @@ -128,7 +134,9 @@ #include #include #include +# ifdef HAVE_SYS_USTAT_H #include +# endif #include #include #include @@ -215,12 +223,16 @@ namespace __sanitizer { #if SANITIZER_LINUX || SANITIZER_FREEBSD unsigned struct_rlimit_sz = sizeof(struct rlimit); unsigned struct_timespec_sz = sizeof(struct timespec); +# ifdef HAVE_UTIME_H unsigned struct_utimbuf_sz = sizeof(struct utimbuf); +# endif unsigned struct_itimerspec_sz = sizeof(struct itimerspec); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID +# ifdef HAVE_SYS_USTAT_H unsigned struct_ustat_sz = sizeof(struct ustat); +# endif unsigned struct_rlimit64_sz = sizeof(struct rlimit64); unsigned struct_statvfs64_sz = sizeof(struct statvfs64); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID @@ -266,7 +278,9 @@ namespace __sanitizer { #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID int glob_nomatch = GLOB_NOMATCH; +# ifdef GLOB_ALTDIRFUNC int glob_altdirfunc = GLOB_ALTDIRFUNC; +# endif #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ @@ -370,7 +384,9 @@ namespace __sanitizer { unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode); unsigned struct_kbsentry_sz = sizeof(struct kbsentry); unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo); +# ifdef HAVE_NETROM_NETROM_H unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct); +# endif unsigned struct_scc_modem_sz = sizeof(struct scc_modem); unsigned struct_scc_stat_sz = sizeof(struct scc_stat); unsigned struct_serial_multiport_struct_sz @@ -805,10 +821,18 @@ namespace __sanitizer { unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS; unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP; unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA; +# ifdef SIOCNRDECOBS unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS; +# endif +# ifdef SIOCNRGETPARMS unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS; +# endif +# ifdef SIOCNRRTCTL unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL; +# endif +# ifdef SIOCNRSETPARMS unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS; +# endif unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL; unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; @@ -890,12 +914,24 @@ CHECK_TYPE_SIZE(glob_t); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv); CHECK_SIZE_AND_OFFSET(glob_t, gl_offs); +# ifdef HAVE_GLOB_T_GL_FLAGS CHECK_SIZE_AND_OFFSET(glob_t, gl_flags); +# endif +# ifdef HAVE_GLOB_T_GL_CLOSEDIR CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir); +# endif +# ifdef HAVE_GLOB_T_GL_READDIR CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); +# endif +# ifdef HAVE_GLOB_T_GL_OPENDIR CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); +# endif +# ifdef HAVE_GLOB_T_GL_LSTAT CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); +# endif +# ifdef HAVE_GLOB_T_GL_STAT CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); +# endif #endif CHECK_TYPE_SIZE(addrinfo); @@ -967,11 +1003,17 @@ CHECK_TYPE_SIZE(sigset_t); COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); // Can't write checks for sa_handler and sa_sigaction due to them being // preprocessor macros. +#if HAVE_STRUCT_SIGACTION_SA_MASK_LAST +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); +#else // HAVE_STRUCT_SIGACTION_SA_MASK_LAST CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); #if SANITIZER_LINUX CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); #endif +#endif // HAVE_STRUCT_SIGACTION_SA_MASK_LAST #if SANITIZER_LINUX CHECK_TYPE_SIZE(__sysctl_args); @@ -991,7 +1033,7 @@ CHECK_TYPE_SIZE(__kernel_loff_t); CHECK_TYPE_SIZE(__kernel_fd_set); #endif -#if !SANITIZER_ANDROID +#ifdef HAVE_WORDEXP_H CHECK_TYPE_SIZE(wordexp_t); CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc); CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h index a780ee2..04c6a96 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -478,7 +478,8 @@ namespace __sanitizer { #elif SANITIZER_LINUX struct __sanitizer_sigset_t { // The size is determined by looking at sizeof of real sigset_t on linux. - uptr val[128 / sizeof(uptr)]; + /* .. except this should be * sizeof(uptr), not '/', no? */ + uptr val[SIZEOF_SIGSET_T / sizeof(uptr)]; }; #elif SANITIZER_FREEBSD struct __sanitizer_sigset_t { @@ -487,6 +488,17 @@ namespace __sanitizer { }; #endif +#if HAVE_STRUCT_SIGACTION_SA_MASK_LAST + struct __sanitizer_sigaction { + union { + void (*sigaction)(int sig, void *siginfo, void *uctx); + void (*handler)(int sig); + }; + STRUCT_SIGACTION_SA_FLAGS_TYPE sa_flags; + void (*sa_restorer)(); + __sanitizer_sigset_t sa_mask; + }; +#else // HAVE_STRUCT_SIGACTION_SA_MASK_LAST // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. struct __sanitizer_sigaction { union { @@ -504,6 +516,7 @@ namespace __sanitizer { void (*sa_restorer)(); #endif }; +#endif // HAVE_STRUCT_SIGACTION_SA_MASK_LAST #if SANITIZER_FREEBSD typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t; @@ -588,13 +601,25 @@ namespace __sanitizer { uptr gl_pathc; char **gl_pathv; uptr gl_offs; +# ifdef HAVE_GLOB_T_GL_FLAGS int gl_flags; +# endif +# ifdef HAVE_GLOB_T_GL_CLOSEDIR void (*gl_closedir)(void *dirp); +# endif +# ifdef HAVE_GLOB_T_GL_READDIR void *(*gl_readdir)(void *dirp); +# endif +# ifdef HAVE_GLOB_T_GL_OPENDIR void *(*gl_opendir)(const char *); +# endif +# ifdef HAVE_GLOB_T_GL_LSTAT int (*gl_lstat)(const char *, void *); +# endif +# ifdef HAVE_GLOB_T_GL_STAT int (*gl_stat)(const char *, void *); +# endif }; # elif SANITIZER_FREEBSD struct __sanitizer_glob_t { diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc index bb6e587..973f698 100644 --- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -73,7 +73,16 @@ void SleepForSeconds(int seconds) { } void SleepForMillis(int millis) { +#ifdef HAVE_NANOSLEEP + struct timespec ts; + ts.tv_sec = millis / 1000; + ts.tv_nsec = (millis % 1000) * 1000000; + nanosleep(&ts, NULL); /* could as well loop here */ +#elif defined HAVE_USLEEP usleep(millis * 1000); +# else + dunno how to SleepForMillis +#endif } void Abort() { diff --git a/make/platform/clang_linux.mk b/make/platform/clang_linux.mk index c6921ea..5ce1567 100644 --- a/make/platform/clang_linux.mk +++ b/make/platform/clang_linux.mk @@ -74,6 +74,185 @@ Arch.dfsan-x86_64 := x86_64 Arch.lsan-x86_64 := x86_64 endif +# TryCompile2 compiler source flags +# Returns exit code of running a compiler invocation. +# Same as TryCompile but in outer scope, don't want to touch the other one +TryCompile2 = \ + $(shell \ + cflags=""; \ + for flag in $(3); do \ + cflags="$$cflags $$flag"; \ + done; \ + $(1) $$cflags $(2) -o /dev/null > /dev/null 2> /dev/null ; \ + echo $$?) + +# our conftest.c +ct_c = $(ProjSrcRoot)/make/platform/clang_linux_test_libc.c + +HAVE_DLVSYM := 1 +HAVE_NETROM_NETROM_H := 1 +HAVE_GLOB_H := 1 +HAVE_NANOSLEEP := 1 +HAVE_GLOB_T_GL_CLOSEDIR := 1 +HAVE_GLOB_T_GL_FLAGS := 1 +HAVE_GLOB_T_GL_LSTAT := 1 +HAVE_GLOB_T_GL_OPENDIR := 1 +HAVE_GLOB_T_GL_READDIR := 1 +HAVE_GLOB_T_GL_STAT := 1 +HAVE_STRUCT_SIGACTION_SA_MASK_LAST := 0 +HAVE_SYS_USTAT_H := 1 +HAVE_USLEEP := 1 +HAVE_UTIME_H := 1 +HAVE_WORDEXP_H := 1 +SIZEOF_STRUCT_STATFS64 := 120 +SIZEOF_SIGSET_T := 128 +STRUCT_SIGACTION_SA_FLAGS_TYPE := int + +#ifneq ($(findstring -uclibc,$(CompilerTargetTriple)),) +# does not work, cross-compilation seems to be non-working? +ifeq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN -DL_AC_CHECK_UCLIBC -DL_AC_CHECK_HEADER="",),0) + HAVE_DLVSYM := + HAVE_NETROM_NETROM_H := + STRUCT_SIGACTION_SA_FLAGS_TYPE := unsigned long +ifneq ($(filter alpha%,$(CompilerTargetTriple)),) + STRUCT_SIGACTION_SA_FLAGS_TYPE := unsigned +endif +ifneq ($(filter mips%,$(CompilerTargetTriple)),) + STRUCT_SIGACTION_SA_FLAGS_TYPE := unsigned +endif + HAVE_STRUCT_SIGACTION_SA_MASK_LAST := 1 +ifneq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN -DL_AC_CHECK_HEADER="",),0) + HAVE_SYS_USTAT_H := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN -DL_AC_CHECK_HEADER="",),0) + HAVE_UTIME_H := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN -DL_AC_CHECK_HEADER="",),0) + HAVE_WORDEXP_H := +endif +glob_h := -DL_AC_CHECK_HEADER="" +ifneq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN $(glob_h),),0) + HAVE_GLOB_H := +endif +# check for struct glob members (check for GNU extensions) +glob_h += -DL_AC_STRUCT="glob_t" +ifneq ($(call TryCompile2,$(CC),$(ct_c) $(glob_h) -DL_AC_CHECK_STRUCT_MEMBER=gl_flags,),0) + HAVE_GLOB_T_GL_FLAGS := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) $(glob_h) -DL_AC_CHECK_STRUCT_MEMBER=gl_closedir,),0) + HAVE_GLOB_T_GL_CLOSEDIR := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) $(glob_h) -DL_AC_CHECK_STRUCT_MEMBER=gl_readdir,),0) + HAVE_GLOB_T_GL_READDIR := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) $(glob_h) -DL_AC_CHECK_STRUCT_MEMBER=gl_opendir,),0) + HAVE_GLOB_T_GL_OPENDIR := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) $(glob_h) -DL_AC_CHECK_STRUCT_MEMBER=gl_lstat,),0) + HAVE_GLOB_T_GL_LSTAT := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) $(glob_h) -DL_AC_CHECK_STRUCT_MEMBER=gl_stat,),0) + HAVE_GLOB_T_GL_STAT := +endif +# check misc functions +# for __stub_* on glibc and uClibc including features.h is enough +ifneq ($(call TryCompile2,$(CC),$(ct_c) -DL_features_h -DL_AC_CHECK_FUNC=nanosleep -DL_AC_CHECK_FUNC_stub="defined __stub_nanosleep || defined __stub___nanosleep",),0) + HAVE_NANOSLEEP := +endif +ifneq ($(call TryCompile2,$(CC),$(ct_c) -DL_features_h -DL_AC_CHECK_FUNC=usleep -DL_AC_CHECK_FUNC_stub="defined __stub_usleep || defined __stub___usleep",),0) + HAVE_USLEEP := +endif + +# AC_CHECK_SIZEOF, in make +define ac_check_sizeof +$(shell \ +if test $$($(CC) $(1) -DL_AC_SIZEOF_GE=0 -o /dev/null > /dev/null 2> /dev/null; echo $$?) -eq 0; \ +then \ + ac_lo=0 ac_mid=0; \ + while :; \ + do \ + if test $$($(CC) $(1) -DL_AC_SIZEOF_LE=$$ac_mid -o /dev/null > /dev/null 2> /dev/null; echo $$?) -eq 0; then \ + ac_hi=$$ac_mid; \ + break; \ + else \ + ac_lo=$$(( $$ac_mid + 1 )); \ + if test $$ac_lo -le $$ac_mid; then ac_lo= ac_hi=; break; fi; \ + ac_mid=$$(( 2 * $$ac_mid + 1 )); \ + fi; \ + done; \ +else \ + if test $$($(CC) $(1) -DL_AC_SIZEOF_LT=0 -o /dev/null > /dev/null 2> /dev/null; echo $$?) -eq 0; then \ + ac_hi=-1 ac_mid=-1; \ + while :; \ + do \ + if test $$($(CC) $(1) -DL_AC_SIZEOF_GE=$$ac_mid -o /dev/null > /dev/null 2> /dev/null; echo $$?) -eq 0; then \ + ac_lo=$$ac_mid; \ + break; \ + else \ + ac_hi=$$(( ($$ac_mid) - 1 )); \ + if test $$ac_mid -le $$ac_hi; then ac_lo= ac_hi=; break; fi; \ + ac_mid=$$(( 2 * $$ac_mid )); \ + fi; \ + done; \ + else \ + ac_lo= ac_hi=; \ + fi; \ +fi; \ +while test "x$$ac_lo" != "x$$ac_hi"; \ +do \ + ac_mid=$$(( ($$ac_hi - $$ac_lo) / 2 + $$ac_lo )); \ + if test $$($(CC) $(1) -DL_AC_SIZEOF_LE=$$ac_mid -o /dev/null > /dev/null 2> /dev/null; echo $$?) -eq 0; then \ + ac_hi=$$ac_mid; \ + else \ + ac_lo=$$(( ($$ac_mid) + 1 )); \ + fi; \ +done; \ +echo $$(( $$ac_lo + 0 )); \ +) +endef + +# determine sizeof sigset_t +SIZEOF_SIGSET_T := $(call ac_check_sizeof,$(ct_c) -DL_AC_CHECK_HEADER="" -DL_AC_SIZEOF="sigset_t") + +# determine sizeof struct statfs64 +SIZEOF_STRUCT_STATFS64 := 0 +ifeq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN -DL_AC_CHECK_HEADER="",),0) +SIZEOF_STRUCT_STATFS64 := $(call ac_check_sizeof,$(ct_c) -DL_AC_CHECK_HEADER="" -DL_AC_SIZEOF="struct statfs64") +else + ifeq ($(call TryCompile2,$(CC),$(ct_c) -DL_MAIN -DL_AC_CHECK_HEADER="",),0) + SIZEOF_STRUCT_STATFS64 := $(call ac_check_sizeof,$(ct_c) -DL_AC_CHECK_HEADER="" -DL_AC_SIZEOF="struct statfs64") + endif +endif +# end of -uclibc handling +endif + +define add_config_h_cppflag +ifeq ($($(1)),) +CONFIG_H_CPPFLAGS += -U$(1) +else +CONFIG_H_CPPFLAGS += '-D$(1)=$($(1))' +endif +endef + +CONFIG_H_CPPFLAGS := +$(eval $(call add_config_h_cppflag,HAVE_DLVSYM)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_H)) +$(eval $(call add_config_h_cppflag,HAVE_NANOSLEEP)) +$(eval $(call add_config_h_cppflag,HAVE_NETROM_NETROM_H)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_T_GL_CLOSEDIR)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_T_GL_FLAGS)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_T_GL_LSTAT)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_T_GL_OPENDIR)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_T_GL_READDIR)) +$(eval $(call add_config_h_cppflag,HAVE_GLOB_T_GL_STAT)) +$(eval $(call add_config_h_cppflag,HAVE_STRUCT_SIGACTION_SA_MASK_LAST)) +$(eval $(call add_config_h_cppflag,HAVE_SYS_USTAT_H)) +$(eval $(call add_config_h_cppflag,HAVE_USLEEP)) +$(eval $(call add_config_h_cppflag,HAVE_UTIME_H)) +$(eval $(call add_config_h_cppflag,HAVE_WORDEXP_H)) +$(eval $(call add_config_h_cppflag,SIZEOF_STRUCT_STATFS64)) +$(eval $(call add_config_h_cppflag,SIZEOF_SIGSET_T)) +$(eval $(call add_config_h_cppflag,STRUCT_SIGACTION_SA_FLAGS_TYPE)) endif ifneq ($(LLVM_ANDROID_TOOLCHAIN_DIR),) @@ -87,6 +266,7 @@ endif CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer SANITIZER_CFLAGS := -fPIE -fno-builtin -gline-tables-only +CFLAGS += $(CONFIG_H_CPPFLAGS) CFLAGS.full-i386 := $(CFLAGS) -m32 CFLAGS.full-x86_64 := $(CFLAGS) -m64 diff --git a/make/platform/clang_linux_test_libc.c b/make/platform/clang_linux_test_libc.c new file mode 100644 index 0000000..2f9bba8 --- /dev/null +++ b/make/platform/clang_linux_test_libc.c @@ -0,0 +1,68 @@ +/* This file is used to check for libc characteristics and features */ +#ifdef L_features_h +# include +#endif + +#ifdef L_AC_CHECK_HEADER +/* compile-time check for availability of a header */ +# include L_AC_CHECK_HEADER +#endif + +#ifdef L_AC_CHECK_UCLIBC +# ifndef __UCLIBC__ +choke me /* not uClibc */ +# endif +#endif + +#ifdef L_MAIN +/* provide a dummy main for the linker */ +int main() +{ + return 0; +} +#endif + +#ifdef L_AC_CHECK_STRUCT_MEMBER +/* compile-time check for presence of struct member */ +int main() +{ + static L_AC_STRUCT ac_aggr; + if (ac_aggr.L_AC_CHECK_STRUCT_MEMBER) + return 0; + return 0; +} +#endif + +#ifdef L_AC_CHECK_FUNC +/* check if function (or macro) is available */ +# ifdef __cplusplus +extern "C" +# endif +# if L_AC_CHECK_FUNC_stub +choke me /* function 'L_AC_CHECK_FUNC' stubbed out! */ +# endif +char L_AC_CHECK_FUNC (); +int main () +{ + return L_AC_CHECK_FUNC (); +} +#endif + +#ifdef L_AC_SIZEOF +/* Determine sizeof expression */ +int main () +{ +# define s (long int) (sizeof (L_AC_SIZEOF)) +# if defined L_AC_SIZEOF_GE + static int test_array [1 - 2 * !((s) >= L_AC_SIZEOF_GE)]; +# elif defined L_AC_SIZEOF_LE + static int test_array [1 - 2 * !((s) <= L_AC_SIZEOF_LE)]; +# elif defined L_AC_SIZEOF_LT + static int test_array [1 - 2 * !((s) < L_AC_SIZEOF_LT)]; +# else +# error no such comparison operator +# endif + test_array [0] = 0; + return 0; +} +#endif