Test for pollution of the global namespace
authorDavid Neto <dneto@google.com>
Wed, 22 Nov 2017 22:03:55 +0000 (17:03 -0500)
committerDavid Neto <dneto@google.com>
Fri, 24 Nov 2017 02:27:21 +0000 (21:27 -0500)
Works on Linux only for now.  That's a good start.

Move ValidateBinaryUsingContextAndValidationState into anonymous
namespace in source/validate.cpp.

CMakeLists.txt
source/CMakeLists.txt
source/comp/CMakeLists.txt
source/link/CMakeLists.txt
source/opt/CMakeLists.txt
source/validate.cpp
utils/check_symbol_exports.py [new file with mode: 0755]

index 48eb5a0..3381fd6 100644 (file)
@@ -48,6 +48,7 @@ else()
   message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
 endif()
 
+
 if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
   message(STATUS "No build type selected, default to Debug")
   set(CMAKE_BUILD_TYPE "Debug")
@@ -162,6 +163,17 @@ endif()
 
 find_host_package(PythonInterp)
 
+if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+  macro(spvtools_check_symbol_exports TARGET)
+    add_test(NAME spirv-tools-symbol-exports-${TARGET}
+             COMMAND ${PYTHON_EXECUTABLE}
+             ${spirv-tools_SOURCE_DIR}/utils/check_symbol_exports.py "$<TARGET_FILE:${TARGET}>")
+  endmacro()
+else()
+  macro(spvtools_check_symbol_exports TARGET)
+    message("Skipping symbol exports test for ${TARGET}")
+  endmacro()
+endif()
 
 # Defaults to OFF if the user didn't set it.
 option(SPIRV_SKIP_EXECUTABLES
@@ -207,4 +219,4 @@ if (NOT "${SPIRV_SKIP_TESTS}")
   add_test(NAME spirv-tools-copyrights
            COMMAND ${PYTHON_EXECUTABLE} utils/check_copyright.py
            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
- endif()
+endif()
index 1522f38..93d0c20 100644 (file)
@@ -297,6 +297,7 @@ target_include_directories(${SPIRV_TOOLS}
   PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
   )
 set_property(TARGET ${SPIRV_TOOLS} PROPERTY FOLDER "SPIRV-Tools libraries")
+spvtools_check_symbol_exports(${SPIRV_TOOLS})
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
   install(TARGETS ${SPIRV_TOOLS}
index b1d3e5c..ff52d5e 100644 (file)
@@ -26,6 +26,7 @@ if(SPIRV_BUILD_COMPRESSION)
     PUBLIC ${SPIRV_TOOLS})
 
   set_property(TARGET SPIRV-Tools-comp PROPERTY FOLDER "SPIRV-Tools libraries")
+  spvtools_check_symbol_exports(SPIRV-Tools-comp)
 
   if(ENABLE_SPIRV_TOOLS_INSTALL)
     install(TARGETS SPIRV-Tools-comp
index 9db3cab..8ca4df3 100644 (file)
@@ -26,6 +26,7 @@ target_link_libraries(SPIRV-Tools-link
   PUBLIC SPIRV-Tools-opt)
 
 set_property(TARGET SPIRV-Tools-link PROPERTY FOLDER "SPIRV-Tools libraries")
+spvtools_check_symbol_exports(SPIRV-Tools-link)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
   install(TARGETS SPIRV-Tools-link
index b8935df..2964ebd 100644 (file)
@@ -121,6 +121,7 @@ target_link_libraries(SPIRV-Tools-opt
   PUBLIC ${SPIRV_TOOLS})
 
 set_property(TARGET SPIRV-Tools-opt PROPERTY FOLDER "SPIRV-Tools libraries")
+spvtools_check_symbol_exports(SPIRV-Tools-opt)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
   install(TARGETS SPIRV-Tools-opt
index ef5f8ae..741364a 100644 (file)
@@ -230,14 +230,6 @@ UNUSED(void PrintDotGraph(ValidationState_t& _, libspirv::Function func)) {
     printf("}\n");
   }
 }
-}  // anonymous namespace
-
-spv_result_t spvValidate(const spv_const_context context,
-                         const spv_const_binary binary,
-                         spv_diagnostic* pDiagnostic) {
-  return spvValidateBinary(context, binary->code, binary->wordCount,
-                           pDiagnostic);
-}
 
 spv_result_t ValidateBinaryUsingContextAndValidationState(
     const spv_context_t& context, const uint32_t* words, const size_t num_words,
@@ -342,6 +334,14 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
                         context.opcode_table, context.operand_table,
                         context.ext_inst_table, *vstate, &position);
 }
+}  // anonymous namespace
+
+spv_result_t spvValidate(const spv_const_context context,
+                         const spv_const_binary binary,
+                         spv_diagnostic* pDiagnostic) {
+  return spvValidateBinary(context, binary->code, binary->wordCount,
+                           pDiagnostic);
+}
 
 spv_result_t spvValidateBinary(const spv_const_context context,
                                const uint32_t* words, const size_t num_words,
diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py
new file mode 100755 (executable)
index 0000000..624b33d
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# Copyright (c) 2017 Google Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Checks names of global exports from a library."""
+
+from __future__ import print_function
+
+import os.path
+import re
+import subprocess
+import sys
+
+
+PROG = 'check_symbol_exports'
+
+
+def command_output(cmd, directory):
+    """Runs a command in a directory and returns its standard output stream.
+
+    Captures the standard error stream.
+
+    Raises a RuntimeError if the command fails to launch or otherwise fails.
+    """
+    p = subprocess.Popen(cmd,
+                         cwd=directory,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+    (stdout, _) = p.communicate()
+    if p.returncode != 0:
+        raise RuntimeError('Failed to run %s in %s' % (cmd, directory))
+    return stdout
+
+
+def check_library(library):
+    """Scans the given library file for global exports.  If all such
+    exports are namespaced or begin with spv (in either C or C++ styles)
+    then return 0.  Otherwise emit a message and return 1."""
+
+    # The pattern for a global symbol record
+    symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)')
+
+    # Ok patterns are as follows, assuming Itanium name mangling:
+    #   spv[A-Z]          :  extern "C" symbol starting with spv
+    #   _ZN               :  something in a namespace
+    #   _Z[0-9]+spv[A-Z_] :  C++ symbol starting with spv[A-Z_]
+    symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
+    seen = set()
+    result = 0
+    for line in command_output(['objdump', '-t', library], '.').split('\n'):
+        match = symbol_pattern.search(line)
+        if match:
+            symbol = match.group(1)
+            if symbol not in seen:
+                seen.add(symbol)
+                #print("look at '{}'".format(symbol))
+                if not symbol_ok_pattern.match(symbol):
+                    print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
+                    result = 1
+    return result
+
+
+def main():
+    import argparse
+    parser = argparse.ArgumentParser(description='Check global names exported from a library')
+    parser.add_argument('library', help='The static library to examine')
+    args = parser.parse_args()
+
+    if not os.path.isfile(args.library):
+        print('{}: error: {} does not exist'.format(PROG, args.library))
+        sys.exit(1)
+
+    if os.name is 'posix':
+        status = check_library(args.library)
+        sys.exit(status)
+    else:
+        print('Passing test since not on Posix')
+        sys.exit(0)
+
+
+if __name__ == '__main__':
+    main()