In Subzero, AddressSanitizer depends on being able to find and instrument calls
to various functions such as malloc() and free(), and as such the .pexe file
-being translated must not have had those symbols stripped. Subzero will not
-complain if it is told to translate a .pexe file with its symbols stripped, but
-it will not be able to find calls to malloc() and free(), so AddressSanitizer
-will not work correctly in the final executable.
+being translated must not have had those symbols stripped or inlined. Subzero
+will not complain if it is told to translate a .pexe file with its symbols
+stripped, but it will not be able to find calls to malloc(), calloc(), free(),
+etc., so AddressSanitizer will not work correctly in the final executable.
+
+Furthermore, pnacl-clang automatically inlines some calls to calloc(),
+even with inlining turned off, so we provide wrapper scripts,
+sz-clang.py and sz-clang++.py, that normally just pass their arguments
+through to pnacl-clang or pnacl-clang++, but add instrumentation to
+replace calls to calloc() at the source level if they are passed
+-fsanitize-address.
These are the steps to compile hello.c to an instrumented object file::
- pnacl-clang -o hello.nonfinal.pexe hello.c
+ sz-clang.py -fsanitize-address -o hello.nonfinal.pexe hello.c
pnacl-finalize --no-strip-syms -o hello.pexe hello.nonfinal.pexe
pnacl-sz -fsanitize-address -filetype=obj -o hello.o hello.pexe
--- /dev/null
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+from utils import FindBaseNaCl, shellcmd
+
+def subsToMacros(subs, src):
+ macros = ['#include <stddef.h>',
+ '#ifdef __cplusplus',
+ 'extern "C" {',
+ '#endif']
+ for func in subs:
+ args = [('{atype} a{num}').format(atype=atype, num=i) for
+ i, atype in enumerate(subs[func]['sig'][1:])]
+ macros.append((
+ '{ftype} {name}({args});'
+ ).format(ftype=subs[func]['sig'][0],
+ name=subs[func]['sub'],
+ args=', '.join(args)))
+ macros.append((
+ '#define {func}(args...) ({sub}(args))'
+ ).format(func=func, sub=subs[func]['sub']))
+ macros += ['#ifdef __cplusplus',
+ '} // extern "C"',
+ '#endif',
+ '#line 1 "{src}"'.format(src=src)]
+ return '\n'.join(macros) + '\n'
+
+def run(is_cpp):
+ """Passes its arguments directly to pnacl-clang.
+
+ If -fsanitize-address is specified, extra information is passed to
+ pnacl-clang to ensure that later instrumentation in pnacl-sz can be
+ performed. For example, clang automatically inlines many memory allocation
+ functions, so this script will redefine them at compile time to make sure
+ they can be correctly instrumented by pnacl-sz.
+ """
+ pnacl_root = FindBaseNaCl()
+ dummy_subs = {'calloc': {'sig': ['void *', 'size_t', 'size_t'],
+ 'sub': '__asan_dummy_calloc'},
+ '_calloc': {'sig': ['void *', 'size_t', 'size_t'],
+ 'sub': '__asan_dummy_calloc'}}
+ subs_src = (
+ '{root}/toolchain_build/src/subzero/pydir/sz_clang_dummies.c'
+ ).format(root=pnacl_root)
+ clang = (
+ '{root}/toolchain/linux_x86/pnacl_newlib_raw/bin/pnacl-clang{pp}'
+ ).format(root=pnacl_root, pp='++' if is_cpp else '')
+ args = sys.argv
+ args[0] = clang
+ tmp_dir = ''
+ if '-fsanitize-address' in args:
+ args.remove('-fsanitize-address')
+ include_dirs = set()
+ tmp_dir = tempfile.mkdtemp()
+ for i, arg in enumerate(args[1:], 1):
+ if not os.path.isfile(arg):
+ continue
+ src = os.path.basename(arg)
+ ext = os.path.splitext(arg)[1]
+ if ext in ['.c', '.cc', '.cpp']:
+ include_dirs |= {os.path.dirname(arg)}
+ dest_name = os.path.join(tmp_dir, src)
+ with open(dest_name, 'w') as dest:
+ dest.write(subsToMacros(dummy_subs, arg))
+ with open(arg) as src:
+ for line in src:
+ dest.write(line)
+ args[i] = dest_name
+ # If linking (not single file compilation) then add dummy definitions
+ if not ('-o' in args and
+ ('-c' in args or '-S' in args or '-E' in args)):
+ args.append(subs_src)
+ for d in include_dirs:
+ args.append('-iquote {d}'.format(d=d))
+ if '-fno-inline' not in args:
+ args.append('-fno-inline')
+ err_code = 0
+ try:
+ shellcmd(args, echo=True)
+ except subprocess.CalledProcessError as e:
+ print e.output
+ err_code = e.returncode
+ if tmp_dir != '':
+ shutil.rmtree(tmp_dir)
+ exit(err_code)