2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """A utility script to help building Syzygy-instrumented Chrome binaries."""
17 # The default directory containing the Syzygy toolchain.
18 _DEFAULT_SYZYGY_DIR
= os
.path
.abspath(os
.path
.join(
19 os
.path
.dirname(__file__
), '../../../..',
20 'third_party/syzygy/binaries/exe/'))
22 # Basenames of various tools.
23 _INSTRUMENT_EXE
= 'instrument.exe'
24 _GENFILTER_EXE
= 'genfilter.exe'
25 _ASAN_AGENT_DLL
= 'syzyasan_rtl.dll'
27 # Default agents for known modes.
28 _DEFAULT_AGENT_DLLS
= { 'asan': _ASAN_AGENT_DLL
}
30 _LOGGER
= logging
.getLogger()
33 def _Shell(*cmd
, **kw
):
34 """Shells out to "cmd". Returns a tuple of cmd's stdout, stderr."""
35 _LOGGER
.info('Running command "%s".', cmd
)
36 prog
= subprocess
.Popen(cmd
, **kw
)
38 stdout
, stderr
= prog
.communicate()
39 if prog
.returncode
!= 0:
40 raise RuntimeError('Command "%s" returned %d.' % (cmd
, prog
.returncode
))
45 def _CompileFilter(syzygy_dir
, executable
, symbol
, filter_file
,
47 """Compiles the provided filter writing the compiled filter file to
50 cmd
= [os
.path
.abspath(os
.path
.join(syzygy_dir
, _GENFILTER_EXE
)),
52 '--input-image=%s' % executable
,
53 '--input-pdb=%s' % symbol
,
54 '--output-file=%s' % output_filter_file
,
56 os
.path
.abspath(filter_file
)]
59 if not os
.path
.exists(output_filter_file
):
60 raise RuntimeError('Compiled filter file missing: %s' % output_filter_file
)
64 def _InstrumentBinary(syzygy_dir
, mode
, executable
, symbol
, dst_dir
,
66 """Instruments the executable found in input_dir, and writes the resultant
67 instrumented executable and symbol files to dst_dir.
69 cmd
= [os
.path
.abspath(os
.path
.join(syzygy_dir
, _INSTRUMENT_EXE
)),
73 '--input-image=%s' % executable
,
74 '--input-pdb=%s' % symbol
,
75 '--output-image=%s' % os
.path
.abspath(
76 os
.path
.join(dst_dir
, os
.path
.basename(executable
))),
77 '--output-pdb=%s' % os
.path
.abspath(
78 os
.path
.join(dst_dir
, os
.path
.basename(symbol
)))]
81 cmd
.append('--no-augment-pdb')
83 # If a filter was specified then pass it on to the instrumenter.
85 cmd
.append('--filter=%s' % os
.path
.abspath(filter_file
))
90 def _CopyAgentDLL(agent_dll
, destination_dir
):
91 """Copy the agent DLL and PDB to the destination directory."""
92 dirname
, agent_name
= os
.path
.split(agent_dll
);
93 agent_dst_name
= os
.path
.join(destination_dir
, agent_name
);
94 shutil
.copyfile(agent_dll
, agent_dst_name
)
96 # Search for the corresponding PDB file. We use this approach because
97 # the naming convention for PDBs has changed recently (from 'foo.pdb'
98 # to 'foo.dll.pdb') and we want to support both conventions during the
100 agent_pdbs
= glob
.glob(os
.path
.splitext(agent_dll
)[0] + '*.pdb')
101 if len(agent_pdbs
) != 1:
102 raise RuntimeError('Failed to locate PDB file for %s' % agent_name
)
103 agent_pdb
= agent_pdbs
[0]
104 agent_dst_pdb
= os
.path
.join(destination_dir
, os
.path
.split(agent_pdb
)[1])
105 shutil
.copyfile(agent_pdb
, agent_dst_pdb
)
109 # Make sure the destination directory exists.
110 if not os
.path
.isdir(options
.destination_dir
):
111 _LOGGER
.info('Creating destination directory "%s".',
112 options
.destination_dir
)
113 os
.makedirs(options
.destination_dir
)
115 # Compile the filter if one was provided.
117 _CompileFilter(options
.syzygy_dir
,
118 options
.input_executable
,
119 options
.input_symbol
,
121 options
.output_filter_file
)
123 # Instruments the binaries into the destination directory.
124 _InstrumentBinary(options
.syzygy_dir
,
126 options
.input_executable
,
127 options
.input_symbol
,
128 options
.destination_dir
,
129 options
.output_filter_file
)
131 # Copy the agent DLL and PDB to the destination directory.
132 _CopyAgentDLL(options
.agent_dll
, options
.destination_dir
);
136 option_parser
= optparse
.OptionParser()
137 option_parser
.add_option('--input_executable',
138 help='The path to the input executable.')
139 option_parser
.add_option('--input_symbol',
140 help='The path to the input symbol file.')
141 option_parser
.add_option('--mode',
142 help='Specifies which instrumentation mode is to be used.')
143 option_parser
.add_option('--agent_dll',
144 help='The agent DLL used by this instrumentation. If not specified a '
145 'default will be searched for.')
146 option_parser
.add_option('--syzygy-dir', default
=_DEFAULT_SYZYGY_DIR
,
147 help='Instrumenter executable to use, defaults to "%default".')
148 option_parser
.add_option('-d', '--destination_dir',
149 help='Destination directory for instrumented files.')
150 option_parser
.add_option('--filter',
151 help='An optional filter. This will be compiled and passed to the '
152 'instrumentation executable.')
153 option_parser
.add_option('--output-filter-file',
154 help='The path where the compiled filter will be written. This is '
155 'required if --filter is specified.')
156 options
, args
= option_parser
.parse_args()
159 option_parser
.error('You must provide an instrumentation mode.')
160 if not options
.input_executable
:
161 option_parser
.error('You must provide an input executable.')
162 if not options
.input_symbol
:
163 option_parser
.error('You must provide an input symbol file.')
164 if not options
.destination_dir
:
165 option_parser
.error('You must provide a destination directory.')
166 if options
.filter and not options
.output_filter_file
:
167 option_parser
.error('You must provide a filter output file.')
169 if not options
.agent_dll
:
170 if not options
.mode
in _DEFAULT_AGENT_DLLS
:
171 option_parser
.error('No known default agent DLL for mode "%s".' %
173 options
.agent_dll
= os
.path
.abspath(os
.path
.join(options
.syzygy_dir
,
174 _DEFAULT_AGENT_DLLS
[options
.mode
]))
175 _LOGGER
.info('Using default agent DLL: %s' % options
.agent_dll
)
180 if '__main__' == __name__
:
181 logging
.basicConfig(level
=logging
.INFO
)
182 sys
.exit(main(_ParseOptions()))