1#!/usr/bin/env python 2# Copyright 2017 Google Inc. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16################################################################################ 17 18from __future__ import print_function 19import os 20import subprocess 21import sys 22 23import msan_build 24 25GCC_ONLY_ARGS = [ 26 '-aux-info', 27] 28 29 30def InvokedAsGcc(): 31 """Return whether or not we're pretending to be GCC.""" 32 return sys.argv[0].endswith('gcc') or sys.argv[0].endswith('g++') 33 34 35def Is32Bit(args): 36 """Return whether or not we're 32-bit.""" 37 M32_BIT_ARGS = [ 38 '-m32', 39 '-mx32', 40 ] 41 42 return any(arg in M32_BIT_ARGS for arg in args) 43 44 45def FilterWlArg(arg): 46 """Remove -z,defs and equivalents from a single -Wl option.""" 47 parts = arg.split(',')[1:] 48 49 filtered = [] 50 for part in parts: 51 if part == 'defs': 52 removed = filtered.pop() 53 assert removed == '-z' 54 continue 55 56 if part == '--no-undefined': 57 continue 58 59 filtered.append(part) 60 61 if filtered: 62 return '-Wl,' + ','.join(filtered) 63 64 # Filtered entire argument. 65 return None 66 67 68def _RemoveLastMatching(l, find): 69 for i in xrange(len(l) - 1, -1, -1): 70 if l[i] == find: 71 del l[i] 72 return 73 74 raise IndexError('Not found') 75 76 77def RemoveZDefs(args): 78 """Remove unsupported -Wl,-z,defs linker option.""" 79 filtered = [] 80 81 for arg in args: 82 if arg == '-Wl,defs': 83 _RemoveLastMatching(filtered, '-Wl,-z') 84 continue 85 86 if arg == '-Wl,--no-undefined': 87 continue 88 89 if arg.startswith('-Wl,'): 90 arg = FilterWlArg(arg) 91 if not arg: 92 continue 93 94 filtered.append(arg) 95 96 return filtered 97 98 99def GetCompilerArgs(args, is_cxx): 100 """Generate compiler args.""" 101 compiler_args = args[1:] 102 103 if Is32Bit(args): 104 # 32 bit builds not supported. 105 compiler_args.extend([ 106 '-fno-sanitize=memory', 107 '-fno-sanitize-memory-track-origins', 108 ]) 109 110 return compiler_args 111 112 compiler_args = RemoveZDefs(compiler_args) 113 compiler_args.extend([ 114 # FORTIFY_SOURCE is not supported by sanitizers. 115 '-U_FORTIFY_SOURCE', 116 '-Wp,-U_FORTIFY_SOURCE', 117 # Reduce binary size. 118 '-gline-tables-only', 119 # Disable all warnings. 120 '-w', 121 # LTO isn't supported. 122 '-fno-lto', 123 ]) 124 125 if InvokedAsGcc(): 126 compiler_args.extend([ 127 # For better compatibility with flags passed via -Wa,... 128 '-fno-integrated-as', 129 ]) 130 131 if '-fsanitize=memory' not in args: 132 # If MSan flags weren't added for some reason, add them here. 133 compiler_args.extend(msan_build.GetInjectedFlags()) 134 135 if is_cxx: 136 compiler_args.append('-stdlib=libc++') 137 138 return compiler_args 139 140 141def FindRealClang(): 142 """Return path to real clang.""" 143 return os.environ['REAL_CLANG_PATH'] 144 145 146def FallbackToGcc(args): 147 """Check whether if we should fall back to GCC.""" 148 if not InvokedAsGcc(): 149 return False 150 151 return any(arg in GCC_ONLY_ARGS for arg in args[1:]) 152 153 154def main(args): 155 if FallbackToGcc(args): 156 sys.exit(subprocess.call(['/usr/bin/' + os.path.basename(args[0])] + 157 args[1:])) 158 159 is_cxx = args[0].endswith('++') 160 real_clang = FindRealClang() 161 162 if is_cxx: 163 real_clang += '++' 164 165 args = [real_clang] + GetCompilerArgs(args, is_cxx) 166 debug_log_path = os.getenv('WRAPPER_DEBUG_LOG_PATH') 167 if debug_log_path: 168 with open(debug_log_path, 'a') as f: 169 f.write(str(args) + '\n') 170 171 sys.exit(subprocess.call(args)) 172 173 174if __name__ == '__main__': 175 main(sys.argv) 176