2 # Copyright (c) 2012 The Native Client 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.
14 # We only want to export validator functions when we build with scons, because
15 # they are used for tests.
16 # On the other hand, exporting unnecessary symbols causes chromium build
17 # problems, see https://code.google.com/p/nativeclient/issues/detail?id=3367 ,
18 # so we leave VALIDATOR_EXPORT undefined in gyp build.
20 env.Append(CCFLAGS=['-DVALIDATOR_EXPORT=DLLEXPORT'])
22 # Dir to place c files generated by 'dfagen' target (it's under source control).
23 gen_dir = '$MAIN_DIR/src/trusted/validator_ragel/gen'
24 # Currently we only provide ragel pre-built for Linux.
25 ragel_binary = '$MAIN_DIR/../third_party/ragel/ragel.linux'
27 INSTRUCTION_DEFINITIONS = map(env.File, [
28 'instruction_definitions/general_purpose_instructions.def',
29 'instruction_definitions/system_instructions.def',
30 'instruction_definitions/x87_instructions.def',
31 'instruction_definitions/mmx_instructions.def',
32 'instruction_definitions/xmm_instructions.def',
33 'instruction_definitions/nops.def',
34 'instruction_definitions/tls.def',
37 # These objects are included in both dfa_validate_x86_xx and rdfa_validator
38 # libraries, so we have to introduce intermediate scons nodes.
39 validator32 = env.ComponentObject('gen/validator_x86_32.c')
40 validator64 = env.ComponentObject('gen/validator_x86_64.c')
43 env.ComponentObject('validator_features_all.c'),
44 env.ComponentObject('validator_features_validator.c')
47 # Glue library called from service runtime. The source file depends on the
48 # target architecture. In library_deps.py this library is marked as
49 # dependant of dfa_validate_x86_xx.
50 if env.Bit('target_x86'):
51 caller_lib = 'dfa_validate_caller_x86_%s' % env.get('TARGET_SUBARCH')
54 ['dfa_validate_%s.c' % env.get('TARGET_SUBARCH'),
55 {'32': validator32, '64': validator64}[env.get('TARGET_SUBARCH')],
56 'dfa_validate_common.c',
59 # Low-level platform-independent interface supporting both 32 and 64 bit,
60 # used in ncval and in validator_benchmark.
61 env.ComponentLibrary('rdfa_validator',
62 [validator32, validator64] + features)
64 validator_benchmark = env.ComponentProgram(
65 'rdfa_validator_benchmark',
66 ['validator_benchmark.cc'],
67 EXTRA_LIBS=['rdfa_validator', 'platform', 'elf_load']
70 run_benchmark = env.AutoDepsCommand(
71 'run_validator_ragel_benchmark.out',
72 [validator_benchmark, env.GetIrtNexe(), '10000']
75 env.AlwaysBuild(env.Alias('dfavalidatorbenchmark', run_benchmark))
77 # We don't run this test under qemu because it attempts to execute host python
79 gen_dfa_test = env.CommandTest(
81 ['${PYTHON}', env.File('gen_dfa_test.py')] + INSTRUCTION_DEFINITIONS,
82 direct_emulation=False)
84 env.AddNodeToTestSuite(
86 ['small_tests', 'validator_tests'],
89 dll_env = env.Clone(COMPONENT_STATIC=False)
91 if env.Bit('windows'):
92 # On windows we don't need to recompile specifically for dynamic linking.
93 validator32_dll = validator32
94 validator64_dll = validator64
95 features_dll = features
97 dll_env.Append(CCFLAGS=['-fPIC'])
98 validator32_dll = dll_env.ComponentObject('gen/validator_x86_32.c')
99 validator64_dll = dll_env.ComponentObject('gen/validator_x86_64.c')
102 dll_env.ComponentObject('validator_features_all.c'),
103 dll_env.ComponentObject('validator_features_validator.c')
106 dll_utils = dll_env.ComponentObject('dll_utils.c')
108 validator_dll = dll_env.ComponentLibrary(
109 'rdfa_validator_dll',
110 [validator32_dll, validator64_dll, dll_utils] + features_dll)
112 # Here for simplicity we make assumption that python used to run scons
113 # is the same as the one invoked by scons to run tests.
114 python_bitness = {2**31 - 1: 32, 2**63 - 1: 64}[sys.maxsize]
115 bitness = int(env.get('TARGET_SUBARCH'))
116 assert bitness in [32, 64]
117 # Note that it is possible that we build on one machine and run tests on another
118 # one, so we build DLL even if python bitness does not match target bitness.
119 # Asan does not instrument DLLs (only executables), so we will be unable to
120 # run tests under asan.
121 python_can_load_dll = python_bitness == bitness and not env.Bit('asan')
123 # On ARM we still have some test bots that are running an armel system
124 # image (with armhf runtime supported added) which means that the python
125 # executable is armel and cannot load binaries built by the arm cross
126 # compiler that we use.
127 # TODO(sbc): remove this once the testbots get upgraded to armhf:
128 # https://code.google.com/p/nativeclient/issues/detail?id=3716
129 if platform.machine() == 'armv7l':
130 with open(sys.executable) as infile:
131 if '/lib/ld-linux-armhf' not in infile.read(1024):
132 python_can_load_dll = False
134 if python_can_load_dll:
135 validator_py_test = env.AutoDepsCommand(
138 env.File('validator.py'),
141 env.AddNodeToTestSuite(
143 ['small_tests', 'validator_tests'],
144 'run_validator_py_test')
148 # dfagen : Regenerate any autogenerated source files.
150 ragel_targets = set([
151 'dfagen', 'dfacheckdecoder', 'dfacheckvalidator'])
152 ragel_involved = ragel_targets.intersection(COMMAND_LINE_TARGETS)
154 gas = env.MakeUntrustedNativeEnv()['AS']
155 objdump = env.MakeUntrustedNativeEnv()['OBJDUMP']
158 if not env.Bit('host_linux'):
159 raise Exception('Right now DFA generation is only supported on Linux')
161 # Source generation step 1: Build generator of ragel files.
163 # We have generator which reads .def files and produced automaton definition.
165 # Ragel is included in most Linux distributions, but it's not standard tool
166 # on MacOS/Windows thus we only support gneration of automata under Linux.
167 # This also means that we don't need to make sure gen_dfa.cc is portable to
168 # non-POSIX platforms (in particular it's not Windows compatible).
170 env_gen_dfa = env.Clone()
171 env_gen_dfa.Append(CCFLAGS=['-DNACL_TRUSTED_BUT_NOT_TCB'])
173 # Generate byte_machines.rl from byte_machines.py
174 (byte_machines,) = env.AutoDepsCommand(
177 env.File('byte_machines.py'),
180 # Source generation step 2: Generate decoder automata.
182 # Now we are back to conditionally defining the large automata generated
185 def MakeAutomaton(bits, automaton):
186 (rl_instruction_file,) = env.AutoDepsCommand(
187 '%s_x86_%s_instruction.rl' % (automaton, bits),
189 env.File('gen_dfa.py'),
193 ] + INSTRUCTION_DEFINITIONS)
195 include_dir = rl_instruction_file.dir.get_abspath()
196 assert include_dir == byte_machines.dir.get_abspath()
198 # Don't use env.File because filename is written "as is" in XML file.
199 # We want relative path there, not an absolute one!
200 rl_file = 'src/trusted/validator_ragel/%s_x86_%s.rl' % (automaton, bits)
202 (xml_file,) = env.AutoDepsCommand(
203 '%s/%s_x86_%s.xml' % (gen_dir, automaton, bits),
208 '-I%s' % include_dir,
211 extra_deps=[byte_machines, rl_instruction_file])
213 c_file = env.AutoDepsCommand(
214 '%s/%s_x86_%s.c' % (gen_dir, automaton, bits),
216 env.File('codegen.py'),
220 # We need to at least update timestamp if dfagen is started.
221 # It is needed for presubmit script.
222 env.AlwaysBuild(c_file)
224 return c_file, xml_file
226 (decoder32, decoder32_xml) = MakeAutomaton(
228 (validator32, validator32_xml) = MakeAutomaton(
230 (decoder64, decoder64_xml) = MakeAutomaton(
232 (validator64, validator64_xml) = MakeAutomaton(
235 automata = [decoder32, validator32, decoder64, validator64]
237 # Prepair 'dfacheckdecoder' test.
239 # In this test, all acceptable instructions are enumerated
240 # by DFA traversal, and for each one objdump output and
241 # DFA-based decoder output are compared.
242 # It takes few hours to run the test, so it's not included
243 # into any suits and is supposed to be run manually when
244 # changes are made to DFA definitions.
245 # Also, since DFA generation is currently linux-only,
246 # this test is somewhat platform-dependent as well.
248 decoder32_obj = dll_env.ComponentObject(decoder32)
249 decoder64_obj = dll_env.ComponentObject(decoder64)
250 decoder_test_obj = dll_env.ComponentObject('unreviewed/decoder_test.cc')
252 # Command-line decoder.
253 decoder_test = env.ComponentProgram(
255 [decoder_test_obj, decoder32_obj, decoder64_obj])
257 decoder_dll = dll_env.ComponentLibrary(
259 [decoder_test_obj, decoder32_obj, decoder64_obj])
263 for bits, xml in [('32', decoder32_xml), ('64', decoder64_xml)]:
264 check_decoder = env.AutoDepsCommand(
265 'check_decoder_test_results_%s' % bits,
267 env.File('check_decoder.py'),
270 '--objdump', objdump,
271 '--decoder', decoder_test,
274 check_decoders.append(check_decoder)
276 # Never run decoder tests in parallel because they can take all CPU.
277 SideEffect('check_decoder', check_decoders)
279 env.AlwaysBuild(env.Alias('dfagen', automata))
280 env.AlwaysBuild(env.Alias('dfacheckdecoder', check_decoders))
282 if python_can_load_dll:
284 dfacheckvalidator_tests = []
286 for bitness, xml in [('32', validator32_xml), ('64', validator64_xml)]:
287 superinstruction = env.AutoDepsCommand(
288 'superinstructions_x86_%s.txt' % bitness,
290 env.File('verify_validators_dfa.py'),
294 superinstruction_verified = env.AutoDepsCommand(
295 'superinstructions_verified_x86_%s.out' % bitness,
297 env.File('verify_superinstructions.py'),
298 '--bitness', bitness,
300 '--objdump', objdump,
301 '--validator_dll', validator_dll,
304 env.AlwaysBuild(superinstruction_verified)
305 dfacheckvalidator_tests.append(superinstruction_verified)
307 regular_instructions_test = env.AutoDepsCommand(
308 'regular_instructions_test_x86_%s.out' % bitness,
310 env.File('verify_regular_instructions.py'),
312 '--bitness', bitness,
313 '--validator_dll', validator_dll,
314 '--decoder_dll', decoder_dll])
316 # Never run exhaustive tests in parallel because they can take all CPU.
317 env.AlwaysBuild(regular_instructions_test)
318 SideEffect('check_validator', regular_instructions_test)
319 dfacheckvalidator_tests.append(regular_instructions_test)
321 env32 = env.Clone(BUILD_TARGET_NAME='x86-32')
322 env64 = env.Clone(BUILD_TARGET_NAME='x86-64')
323 old_ncval32 = env32.File('$STAGING_DIR/ncval$PROGSUFFIX')
324 old_ncval64 = env64.File('$STAGING_DIR/ncval$PROGSUFFIX')
326 regular_instructions_old_test = env.AutoDepsCommand(
327 'regular_instructions_old_test_x86_%s.out' % bitness,
329 env.File('verify_regular_instructions_old.py'),
331 '--bitness', bitness,
333 '--objdump', objdump,
334 '--validator_dll', validator_dll,
335 '--ncval32', old_ncval32,
336 '--ncval64', old_ncval64,
337 '--errors', '${TARGET}'])
339 # Never run exhaustive tests in parallel because they can take all CPU.
340 env.AlwaysBuild(regular_instructions_old_test)
341 SideEffect('check_validator', regular_instructions_old_test)
342 dfacheckvalidator_tests.append(regular_instructions_old_test)
344 # There are three categories of protected files: validator files, generated
345 # files (those of validator files that lie withing 'gen' directory) and
346 # generating files (programs and input data used to produce generated
349 # When validator files are changed, exhaustive validator tests have to be
350 # rerun. When generating files are changed, dfagen must be run to ensure
351 # that generated files are up to date. Presubmit script uses
352 # protected_files.json to perform these checks.
354 # Files which directly affect the validator behavior.
356 'gen/validator_x86_32.c',
357 'gen/validator_x86_32.xml',
358 'gen/validator_x86_64.c',
359 'gen/validator_x86_64.xml',
360 'validator_internal.h',
365 # Files which are used to generate gen/{decoder,validator}_x86_{32,64}.c
368 INSTRUCTION_DEFINITIONS,
369 'validator_x86_32.rl',
370 'validator_x86_64.rl',
371 'parse_instruction.rl',
376 def CanonicalizeFilename(filename):
377 return 'native_client/' + env.File(filename).get_path().replace('\\', '/')
379 def CalculateHashSums(target, source, env):
380 target_filename = target[0].get_abspath()
386 for filename in source:
387 canonical_filename = CanonicalizeFilename(filename)
388 with open(filename.get_abspath(), 'r') as file_stream:
389 sha512 = hashlib.sha512(file_stream.read()).hexdigest()
390 protected_files['validator'][canonical_filename] = sha512
391 if canonical_filename.rsplit('/', 2)[1] == 'gen':
392 protected_files['generated'].append(canonical_filename)
393 for filename in Flatten(generating_files):
394 canonical_filename = CanonicalizeFilename(filename)
395 protected_files['generating'].append(canonical_filename)
396 with open(target_filename, 'w') as target_file:
397 json.dump(protected_files, target_file, sort_keys=True, indent=2)
399 calculate_hash_sums = env.Command(
400 target='%s/protected_files.json' % gen_dir,
401 source=protected_files,
402 action=CalculateHashSums)
403 env.Depends(calculate_hash_sums, dfacheckvalidator_tests)
405 env.Alias('dfacheckvalidator', calculate_hash_sums)
407 # Targeted tests: RDFA validator test, dis section checker and spec_val test.
409 for bits in ['32', '64']:
411 '${MAIN_DIR}/src/trusted/validator_ragel/testdata/%s/*.test' % bits)
413 if env.Bit('regenerate_golden'):
414 update_option = ['--update']
418 if env.Bit('target_x86'):
419 # In principle we can run these tests on ARM too, but first, there is no
420 # reason to, and second, there are problems with those bots that build
421 # on one machine and run tests on another (build machine does not recognize
422 # than new ncval is needed, so it's not copied to the test machine).
423 rdfaval = '$STAGING_DIR/ncval_new$PROGSUFFIX'
424 rdfa_targeted_test = env.AutoDepsCommand(
425 'run_rdfa_targeted_tests_%s.out' % bits,
427 env.File('run_rdfa_validator_tests.py'),
428 '--rdfaval', env.File(rdfaval),
430 tests_mask] + update_option)
432 env.AddNodeToTestSuite(
434 ['small_tests', 'validator_tests'],
435 node_name='run_rdfa_targeted_tests_%s' % bits)
437 if env.Bit('regenerate_golden'):
438 # Don't want these tests run in parallel because they write
440 SideEffect(tests_mask, rdfa_targeted_test)
442 dis_section_test = env.AutoDepsCommand(
443 'run_dis_section_test_%s.out' % bits,
445 env.File('check_dis_section.py'),
446 '--objdump', objdump,
448 tests_mask] + update_option)
450 env.AddNodeToTestSuite(
452 ['small_tests', 'validator_tests'],
453 node_name='run_dis_section_test_%s' % bits)
455 if env.Bit('regenerate_golden'):
456 # Don't want these tests run in parallel because they write
458 SideEffect(tests_mask, dis_section_test)
460 spec_test = env.AutoDepsCommand(
461 'run_spec_val_test_%s.out' % bits,
463 env.File('spec_val_test.py'),
465 tests_mask] + update_option)
467 env.AddNodeToTestSuite(
469 ['small_tests', 'validator_tests'],
470 node_name='run_spec_val_test_%s' % bits)
472 if env.Bit('regenerate_golden'):
473 # Don't want these tests run in parallel because they write
475 SideEffect(tests_mask, spec_test)
477 # Spec test uses @dis section in test files, which in turn is updated
478 # by dis_section_test.
479 if env.Bit('target_x86'):
480 env.Depends(spec_test, dis_section_test)