aboutsummaryrefslogtreecommitdiffstats
path: root/clang-plugin
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-11-09 18:51:25 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-11-09 18:51:25 +0000
commit5570704c53a565e9eb70e362ee341d6aacf8b823 (patch)
treeabe233dc7aed3f7b66267bac172c15b0b43aea71 /clang-plugin
parent7d7d5953c9b9a64acd6bf3bc5995992761a52deb (diff)
downloadrspamd-5570704c53a565e9eb70e362ee341d6aacf8b823.tar.gz
rspamd-5570704c53a565e9eb70e362ee341d6aacf8b823.zip
Start implemetation of static analysis plugin
Diffstat (limited to 'clang-plugin')
-rw-r--r--clang-plugin/CMakeLists.txt20
-rw-r--r--clang-plugin/FindLLVM.cmake127
-rw-r--r--clang-plugin/plugin.cc114
3 files changed, 261 insertions, 0 deletions
diff --git a/clang-plugin/CMakeLists.txt b/clang-plugin/CMakeLists.txt
new file mode 100644
index 000000000..02ffdc173
--- /dev/null
+++ b/clang-plugin/CMakeLists.txt
@@ -0,0 +1,20 @@
+IF (ENABLE_CLANG_PLUGIN MATCHES "ON")
+ # Clang plugin for static analysis
+ if (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ MESSAGE(FATAL_ERROR "Cannot build clang plugin when compiler is not clang")
+ endif ()
+
+ SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}")
+ ENABLE_LANGUAGE(CXX)
+ FIND_PACKAGE(LLVM REQUIRED)
+
+ SET(CLANGPLUGINSRC plugin.cc)
+
+ ADD_LIBRARY(rspamd-clang SHARED ${CLANGPLUGINSRC})
+ SET_TARGET_PROPERTIES(rspamd-clang PROPERTIES
+ COMPILE_FLAGS "${LLVM_CXX_FLAGS} ${LLVM_CPP_FLAGS} ${LLVM_C_FLAGS}"
+ INCLUDE_DIRECTORIES ${LIBCLANG_INCLUDE_DIR}
+ LINKER_LANGUAGE CXX)
+ TARGET_LINK_LIBRARIES(rspamd-clang ${LIBCLANG_LIBRARIES})
+ LINK_DIRECTORIES(${LLVM_LIBRARY_DIRS})
+ENDIF()
diff --git a/clang-plugin/FindLLVM.cmake b/clang-plugin/FindLLVM.cmake
new file mode 100644
index 000000000..fca9984ea
--- /dev/null
+++ b/clang-plugin/FindLLVM.cmake
@@ -0,0 +1,127 @@
+# Copyright (c) 2007-2015 University of Illinois at Urbana-Champaign.
+# Copyright (c) 2015, Vsevolod Stakhov
+# - Find LLVM
+# This module can be used to find LLVM.
+# It requires that the llvm-config executable be available on the system path.
+# Once found, llvm-config is used for everything else.
+#
+# Typical usage could be:
+# find_package(LLVM QUIET REQUIRED COMPONENTS jit native interpreter)
+#
+# If the QUIET flag is not set, the specified components and LLVM version are
+# outputted.
+#
+# If the COMPONENTS are not set, the default set of "all" is used.
+#
+# The following variables are set:
+#
+# LLVM_FOUND - Set to YES if LLVM is found.
+# LLVM_VERSION - Set to the decimal version of the LLVM library.
+# LLVM_C_FLAGS - All flags that should be passed to a C compiler.
+# LLVM_CXX_FLAGS - All flags that should be passed to a C++ compiler.
+# LLVM_CPP_FLAGS - All flags that should be passed to the C pre-processor.
+# LLVM_LD_FLAGS - Additional flags to pass to the linker.
+# LLVM_LIBRARY_DIRS - A list of directories where the LLVM libraries are located.
+# LLVM_INCLUDE_DIRS - A list of directories where the LLVM headers are located.
+# LLVM_LIBRARIES - A list of libraries which should be linked against.
+
+# A macro to run llvm config
+macro(_llvm_config _var_name)
+ # Firstly, locate the LLVM config executable
+ find_program(_llvm_config_exe
+ NAMES llvm-config
+ DOC "llvm-config executable location"
+ )
+
+ # If no llvm-config executable was found, set the output variable to not
+ # found.
+ if (NOT _llvm_config_exe)
+ set(${_var_name} "${_var_name}-NOTFOUND")
+ else (NOT _llvm_config_exe)
+ # Otherwise, run llvm-config
+ execute_process(
+ COMMAND ${_llvm_config_exe} ${ARGN}
+ OUTPUT_VARIABLE ${_var_name}
+ RESULT_VARIABLE _llvm_config_retval
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if (RESULT_VARIABLE)
+ message(SEND_ERROR
+ "Error running llvm-config with arguments: ${ARGN}")
+ endif (RESULT_VARIABLE)
+ endif (NOT _llvm_config_exe)
+endmacro(_llvm_config)
+
+# The default set of components
+set(_llvm_components all)
+
+# If components have been specified via find_package, use them
+if (LLVM_FIND_COMPONENTS)
+ set(_llvm_components ${LLVM_FIND_COMPONENTS})
+endif (LLVM_FIND_COMPONENTS)
+
+if (NOT LLVM_FIND_QUIETLY)
+ message(STATUS "Looking for LLVM components: ${_llvm_components}")
+endif (NOT LLVM_FIND_QUIETLY)
+
+_llvm_config(LLVM_VERSION --version)
+_llvm_config(LLVM_C_FLAGS --cflags)
+_llvm_config(LLVM_CXX_FLAGS --cxxflags)
+_llvm_config(LLVM_CPP_FLAGS --cppflags)
+_llvm_config(LLVM_LD_FLAGS --ldflags)
+_llvm_config(LLVM_LIBRARY_DIRS --libdir)
+_llvm_config(LLVM_INCLUDE_DIRS --includedir)
+_llvm_config(LLVM_LIBRARIES --libs ${_llvm_components})
+
+if (NOT LLVM_FIND_QUIETLY)
+ message(STATUS "Found LLVM version: ${LLVM_VERSION}")
+endif (NOT LLVM_FIND_QUIETLY)
+
+SET(libclang_llvm_header_search_paths
+ # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/
+ "/usr/lib/llvm-${LLVM_VERSION}/include/"
+ # LLVM MacPorts
+ "/opt/local/libexec/llvm-${LLVM_VERSION}/include"
+ # LLVM Homebrew
+ "/usr/local/Cellar/llvm/${LLVM_VERSION}/include"
+ # LLVM Homebrew/versions
+ "/usr/local/lib/llvm-${LLVM_VERSION}/include"
+ # FreeBSD ports versions
+ "/usr/local/llvm${LLVM_VERSION}/include"
+)
+
+SET(libclang_llvm_lib_search_paths
+ # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/
+ "/usr/lib/llvm-${LLVM_VERSION}/lib/"
+ # LLVM MacPorts
+ "/opt/local/libexec/llvm-${LLVM_VERSION}/lib"
+ # LLVM Homebrew
+ "/usr/local/Cellar/llvm/${LLVM_VERSION}/lib"
+ # LLVM Homebrew/versions
+ "/usr/local/lib/llvm-${LLVM_VERSION}/lib"
+ # FreeBSD ports versions
+ "/usr/local/llvm${LLVM_VERSION}/lib"
+)
+
+find_path(LIBCLANG_INCLUDE_DIR clang-c/Index.h
+ PATHS ${libclang_llvm_header_search_paths}
+ PATH_SUFFIXES LLVM/include #Windows package from http://llvm.org/releases/
+ DOC "The path to the directory that contains clang-c/Index.h")
+find_library(LIBCLANG_LIBRARY NAMES libclang.imp libclang clang
+ PATHS ${libclang_llvm_lib_search_paths}
+ PATH_SUFFIXES LLVM/lib #Windows package from http://llvm.org/releases/
+ DOC "The file that corresponds to the libclang library.")
+
+get_filename_component(LIBCLANG_LIBRARY_DIR ${LIBCLANG_LIBRARY} PATH)
+
+set(LIBCLANG_LIBRARIES ${LIBCLANG_LIBRARY})
+set(LIBCLANG_INCLUDE_DIRS ${LIBCLANG_INCLUDE_DIR})
+
+# handle the QUIETLY and REQUIRED arguments and set LLVM_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LLVM
+ DEFAULT_MSG
+ LLVM_LIBRARIES
+ LLVM_INCLUDE_DIRS
+ LLVM_LIBRARY_DIRS)
diff --git a/clang-plugin/plugin.cc b/clang-plugin/plugin.cc
new file mode 100644
index 000000000..204ce7b6f
--- /dev/null
+++ b/clang-plugin/plugin.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2007-2015 University of Illinois at Urbana-Champaign.
+ * Copyright (c) 2015, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+namespace {
+
+ class RspamdASTConsumer : public ASTConsumer {
+ CompilerInstance &Instance;
+
+ public:
+ RspamdASTConsumer (CompilerInstance &Instance)
+ : Instance (Instance)
+ {
+ }
+
+ bool HandleTopLevelDecl (DeclGroupRef DG) override
+ {
+ for (DeclGroupRef::iterator i = DG.begin (), e = DG.end (); i != e;
+ ++i) {
+ const Decl *D = *i;
+ if (const NamedDecl *ND = dyn_cast<NamedDecl> (D))
+ llvm::errs () << "top-level-decl: \"" <<
+ ND->getNameAsString () << "\"\n";
+ }
+
+ return true;
+ }
+
+ void HandleTranslationUnit (ASTContext &context) override
+ {
+ struct Visitor : public RecursiveASTVisitor<Visitor> {
+
+ Visitor (void)
+ {
+ }
+
+ bool VisitFunctionDecl (FunctionDecl *FD)
+ {
+ if (FD->isLateTemplateParsed ())
+ LateParsedDecls.insert (FD);
+ return true;
+ }
+
+ std::set<FunctionDecl *> LateParsedDecls;
+ } v;
+ v.TraverseDecl (context.getTranslationUnitDecl ());
+ clang::Sema &sema = Instance.getSema ();
+ for (const FunctionDecl *FD : v.LateParsedDecls) {
+ clang::LateParsedTemplate *LPT = sema.LateParsedTemplateMap.lookup (
+ FD);
+ sema.LateTemplateParser (sema.OpaqueParser, *LPT);
+ llvm::errs () << "late-parsed-decl: \"" <<
+ FD->getNameAsString () << "\"\n";
+ }
+ }
+ };
+
+ class RspamdASTAction : public PluginASTAction {
+ protected:
+ std::unique_ptr <ASTConsumer> CreateASTConsumer (CompilerInstance &CI,
+ llvm::StringRef) override
+ {
+ return llvm::make_unique<RspamdASTConsumer> (CI);
+ }
+
+ bool ParseArgs (const CompilerInstance &CI,
+ const std::vector <std::string> &args) override
+ {
+ return true;
+ }
+
+ void PrintHelp (llvm::raw_ostream &ros)
+ {
+ ros << "Nothing here\n";
+ }
+
+ };
+
+}
+
+static FrontendPluginRegistry::Add <RspamdASTAction>
+ X ("rspamd-ast", "rspamd ast checker");