Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # -*- python -*- | 1 # -*- python -*- |
| 2 # Copyright (c) 2012 The Native Client Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 from SCons.Errors import UserError | 6 from SCons.Errors import UserError |
| 7 | 7 |
| 8 Import('env') | 8 Import('env') |
| 9 | 9 |
| 10 # | 10 # |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 | 23 |
| 24 INST_DEFS = [ | 24 INST_DEFS = [ |
| 25 'unreviewed/general_purpose_instructions.def', | 25 'unreviewed/general_purpose_instructions.def', |
| 26 'unreviewed/system_instructions.def', | 26 'unreviewed/system_instructions.def', |
| 27 'unreviewed/x87_instructions.def', | 27 'unreviewed/x87_instructions.def', |
| 28 'unreviewed/mmx_instructions.def', | 28 'unreviewed/mmx_instructions.def', |
| 29 'unreviewed/xmm_instructions.def', | 29 'unreviewed/xmm_instructions.def', |
| 30 'unreviewed/nops.def' | 30 'unreviewed/nops.def' |
| 31 ] | 31 ] |
| 32 | 32 |
| 33 # Generate 32 and 64 bit versions of decoders and validators. Both libraries | |
| 34 # are used for command-line decoder and validator those detect specific | |
| 35 # architecture of the ELF file provided. | |
| 36 env.ComponentLibrary('dfa_validate_x86_32', | |
| 37 ['gen/validator_x86_32.c']) | |
| 38 env.ComponentLibrary('dfa_validate_x86_64', | |
| 39 ['gen/validator_x86_64.c']) | |
| 40 env.ComponentLibrary('dfa_decode_x86_32', | |
| 41 ['gen/decoder_x86_32.c']) | |
| 42 env.ComponentLibrary('dfa_decode_x86_64', | |
| 43 ['gen/decoder_x86_64.c']) | |
| 44 | |
| 45 # Glue library called from service runtime. The source file depends on the | |
| 46 # target architecture. | |
| 47 caller_lib = 'dfa_validate_caller_x86_%s' % env.get('TARGET_SUBARCH') | |
| 48 env.ComponentLibrary( | |
| 49 caller_lib, | |
| 50 'unreviewed/dfa_validate_%s.c' % env.get('TARGET_SUBARCH') | |
| 51 ) | |
| 52 | |
| 53 # Command-line decoder. | |
| 54 decoder_test = env.ComponentProgram( | |
| 55 'decoder_test', | |
| 56 ['unreviewed/decoder_test.c'], | |
| 57 EXTRA_LIBS=['dfa_decode_x86_32', 'dfa_decode_x86_64']) | |
| 58 | |
| 59 # Command-line validator. | |
| 60 validator_test_exe = env.ComponentProgram( | |
| 61 'validator_test', | |
| 62 ['unreviewed/validator_test.c'], | |
| 63 EXTRA_LIBS=['dfa_validate_x86_32', 'dfa_validate_x86_64']) | |
| 64 | |
| 65 # Python-based regression test. TODO(pasko): remove it when validator_tests are | |
| 66 # migrated to be able to run with the new validator. | |
| 67 if env.Bit('validator_ragel'): | |
| 68 test_node = env.CommandTest('validator_test_py.out', | |
| 69 command=['${PYTHON}', | |
| 70 env.File('unreviewed/validator_test.py'), | |
| 71 validator_test_exe]) | |
| 72 env.AddNodeToTestSuite(test_node, ['small_tests'], 'run_validator_test_py') | |
| 73 | |
| 33 # Source generation: | 74 # Source generation: |
| 34 # | 75 # |
| 35 # dfagen : Regenerate any autogenerated source files. | 76 # dfagen : Regenerate any autogenerated source files. |
| 36 | 77 |
| 37 generate = False | 78 dfa_aliases = 'dfagen', 'dfaclean', 'dfacheckdecoder' |
| 38 if 'dfagen' in COMMAND_LINE_TARGETS or 'dfaclean' in COMMAND_LINE_TARGETS: | 79 |
| 39 generate = True | 80 generate = any(a in COMMAND_LINE_TARGETS for a in dfa_aliases) |
| 40 | 81 |
| 41 if generate: | 82 if generate: |
| 42 if not env.Bit('host_linux'): | 83 if not env.Bit('host_linux'): |
| 43 raise UserError('Right now DFA generation is only supported on Linux') | 84 raise UserError('Right now DFA generation is only supported on Linux') |
| 44 | 85 |
| 45 # Source generation step 1: Build generator of ragel files. | 86 # Source generation step 1: Build generator of ragel files. |
| 46 # | 87 # |
| 47 # We have generator which reads .def files and produced automaton definition. | 88 # We have generator which reads .def files and produced automaton definition. |
| 48 # | 89 # |
| 49 # Ragel is included in most Linux distributions, but it's not standard tool | 90 # Ragel is included in most Linux distributions, but it's not standard tool |
| 50 # on MacOS/Windows thus we only support gneration of automata under Linux. | 91 # on MacOS/Windows thus we only support gneration of automata under Linux. |
| 51 # This also means that we don't need to make sure gen_dfa.cc is portable to | 92 # This also means that we don't need to make sure gen_dfa.cc is portable to |
| 52 # non-POSIX platforms (in particular it's not Windows compatible). | 93 # non-POSIX platforms (in particular it's not Windows compatible). |
| 53 | 94 |
| 54 env_gen_dfa = env.Clone() | 95 env_gen_dfa = env.Clone() |
| 55 env_gen_dfa.Append(CCFLAGS=['-std=c++0x', '-DNACL_TRUSTED_BUT_NOT_TCB']) | 96 env_gen_dfa.Append(CCFLAGS=['-std=c++0x', '-DNACL_TRUSTED_BUT_NOT_TCB']) |
| 56 | 97 |
| 57 gen_dfa = env_gen_dfa.ComponentProgram( | 98 gen_dfa = env_gen_dfa.ComponentProgram( |
| 58 'gen_dfa', | 99 'gen_dfa', |
| 59 ['unreviewed/gen_dfa.cc']) | 100 ['unreviewed/gen_dfa.cc']) |
| 60 | 101 |
| 61 # Source generation step 2: Generate decoder automatas. | 102 # Source generation step 2: Generate decoder automata. |
| 62 # | 103 # |
| 63 # Now we are back to conditionally defining the large automata generated | 104 # Now we are back to conditionally defining the large automata generated |
| 64 # by gen_dfa. | 105 # by gen_dfa. |
| 65 | 106 |
| 66 def MakeAutomaton(bits, automaton, dfa_gen_actions, ragel_flags): | 107 def MakeAutomaton(bits, automaton, dfa_gen_actions, ragel_flags): |
| 67 rl_file = '%s_x86_%s_instruction.rl' % (automaton, bits) | 108 rl_file = '%s_x86_%s_instruction.rl' % (automaton, bits) |
| 68 | 109 |
| 69 # We are cheating here: there are two autogenerated files: | 110 # We are cheating here: there are two autogenerated files: |
| 70 # .rl and _consts.c, but we only track .rl one. This is safe because | 111 # .rl and _consts.c, but we only track .rl one. This is safe because |
| 71 # _consts.c file includes constants referenced by .rl file and if .rl | 112 # _consts.c file includes constants referenced by .rl file and if .rl |
| 72 # file is not changed _consts.c is guaranteed to be the same (reverse | 113 # file is not changed _consts.c is guaranteed to be the same (reverse |
| 73 # is not true). | 114 # is not true). |
| 115 | |
| 74 const_file = '%s/%s_x86_%s_instruction_consts.c' % ( | 116 const_file = '%s/%s_x86_%s_instruction_consts.c' % ( |
| 75 val_src_dir, automaton, bits) | 117 val_src_dir, automaton, bits) |
| 76 | 118 |
| 77 exe_path = '${STAGING_DIR}/${PROGPREFIX}gen_dfa${PROGSUFFIX}' | |
| 78 env.Command( | 119 env.Command( |
| 79 target=rl_file, | 120 target=rl_file, |
| 80 source=[exe_path] + INST_DEFS, | 121 source=[gen_dfa] + INST_DEFS, |
| 81 action=( | 122 action=( |
| 82 '${SOURCES[0]} -o ${TARGET} -c %s -m %s -d %s %s') % ( | 123 '${SOURCES[0]} -o ${TARGET} -c %s -m %s -d %s %s') % ( |
| 83 # Const file (-c): not tracked by SCONS (see above) | 124 # Const file (-c): not tracked by SCONS (see above) |
| 84 const_file, | 125 const_file, |
| 85 # Argument for CPU type (-m): either "ia32" or "amd64". | 126 # Argument for CPU type (-m): either "ia32" or "amd64". |
| 86 {'32': 'ia32', '64': 'amd64'}[bits], | 127 {'32': 'ia32', '64': 'amd64'}[bits], |
| 87 # (-d): | 128 # (-d): |
| 88 dfa_gen_actions, | 129 dfa_gen_actions, |
| 89 # pass inst defs as remaining parameters | 130 # pass inst defs as remaining parameters |
| 90 ' '.join('${SOURCES[%d]}' % (i+1) | 131 ' '.join('${SOURCES[%d]}' % (i + 1) |
| 91 for i in range(len(INST_DEFS))) | 132 for i in range(len(INST_DEFS))) |
| 92 ) | 133 ) |
| 93 ) | 134 ) |
| 94 c_file = '%s_x86_%s.c' % (automaton, bits) | 135 c_file = '%s_x86_%s.c' % (automaton, bits) |
| 95 c_full_filename = '%s/%s' % (val_src_dir, c_file) | 136 c_full_filename = '%s/%s' % (val_src_dir, c_file) |
| 96 env.Command( | 137 env.Command( |
| 97 target=c_file, | 138 target=c_file, |
| 98 source=['unreviewed/%s_x86_%s.rl' % (automaton, bits), rl_file], | 139 source=['unreviewed/%s_x86_%s.rl' % (automaton, bits), rl_file], |
| 99 action=['%s %s -LL -I%s ${SOURCES[0]} -o ${TARGET}' % ( | 140 action=['%s %s -LL -I%s ${SOURCES[0]} -o ${TARGET}' % ( |
| 100 ragel_binary, ragel_flags, rl_src_dir)] | 141 ragel_binary, ragel_flags, rl_src_dir)] |
| 101 ) | 142 ) |
| 102 | 143 |
| 103 def InjectGeneratedFileHeader(target, source, env): | 144 def InjectGeneratedFileHeader(target, source, env): |
| 104 source_filename = source[0].get_abspath() | 145 source_filename = source[0].get_abspath() |
| 105 target_filename = target[0].get_abspath() | 146 target_filename = target[0].get_abspath() |
| 106 architecture = {'x86_32': 'ia32', | 147 architecture = {'x86_32': 'ia32', |
| 107 'x86_64': 'x86-64'}[target_filename[-8:-2]] | 148 'x86_64': 'x86-64'}[target_filename[-8:-2]] |
| 149 | |
| 108 with open(source_filename, 'r') as source_file: | 150 with open(source_filename, 'r') as source_file: |
| 109 with open(target_filename, 'w') as target_file: | 151 comment, sep, rest = source_file.read().partition('*/') |
| 110 target_file.write( | 152 if sep == '': |
| 111 """/* native_client/%s | 153 raise UserError('Generated file %s does not have ' |
| 112 * THIS FILE IS AUTO-GENERATED. DO NOT EDIT. | 154 'header comment block' % source_filename) |
| 113 * Compiled for %s mode. | |
| 114 */""" % (target_filename, architecture)) | |
| 115 | 155 |
| 116 comment, sep, rest = source_file.read().partition('*/') | 156 with open(target_filename, 'w') as target_file: |
| 117 if sep == '': | 157 target_file.write( |
| 118 raise UserError('Generated file %s does not have ' | 158 ('/* native_client/%s\n' |
| 119 'header comment block' % source_filename) | 159 ' * THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\n' |
| 120 target_file.write(rest) | 160 ' * Compiled for %s mode.\n' |
| 161 ' */') % (target_filename, architecture)) | |
| 121 | 162 |
| 163 target_file.write(rest) | |
| 164 | |
| 165 # inject comments and place files to appropriate dir | |
| 122 env.Command( | 166 env.Command( |
| 123 target=c_full_filename, | 167 target=c_full_filename, |
| 124 source=['%s/%s' % (rl_src_dir, c_file)], | 168 source=['%s/%s' % (rl_src_dir, c_file)], |
| 125 action=InjectGeneratedFileHeader | 169 action=InjectGeneratedFileHeader |
| 126 ) | 170 ) |
| 127 | 171 |
| 128 return rl_file, c_file, c_full_filename | 172 return rl_file, c_file, c_full_filename |
| 129 | 173 |
| 130 decoder32 = MakeAutomaton( | 174 decoder32 = MakeAutomaton( |
| 131 '32', 'decoder', | 175 '32', 'decoder', |
| 132 'check_access,opcode,parse_operands_states,mark_data_fields', | 176 'check_access,opcode,parse_operands_states,mark_data_fields', |
| 133 '-T0') | 177 '-T0') |
| 134 validator32 = MakeAutomaton( | 178 validator32 = MakeAutomaton( |
| 135 '32', 'validator', | 179 '32', 'validator', |
| 136 ('check_access,opcode,parse_operands,parse_operands_states,' | 180 ('check_access,opcode,parse_operands,parse_operands_states,' |
| 137 'instruction_name,mark_data_fields,nacl-forbidden,' | 181 'instruction_name,mark_data_fields,nacl-forbidden,' |
| 138 'imm_operand_action,rel_operand_action'), | 182 'imm_operand_action,rel_operand_action'), |
| 139 '-G2') | 183 '-G2') |
| 140 decoder64 = MakeAutomaton( | 184 decoder64 = MakeAutomaton( |
| 141 '64', 'decoder', | 185 '64', 'decoder', |
| 142 'check_access,opcode,parse_operands_states,mark_data_fields', | 186 'check_access,opcode,parse_operands_states,mark_data_fields', |
| 143 '-T0') | 187 '-T0') |
| 144 validator64 = MakeAutomaton( | 188 validator64 = MakeAutomaton( |
| 145 '64', 'validator', | 189 '64', 'validator', |
| 146 ('opcode,instruction_name,mark_data_fields,imm_operand_action,' | 190 ('opcode,instruction_name,mark_data_fields,imm_operand_action,' |
| 147 'rel_operand_action,nacl-forbidden,parse_nonwrite_registers,' | 191 'rel_operand_action,nacl-forbidden,parse_nonwrite_registers,' |
| 148 'parse_x87_operands,parse_mmx_operands,parse_xmm_operands,' | 192 'parse_x87_operands,parse_mmx_operands,parse_xmm_operands,' |
| 149 'parse_ymm_operands,parse_relative_operands,' | 193 'parse_ymm_operands,parse_relative_operands,' |
| 150 'parse_immediate_operands,parse_operands_states,' | 194 'parse_immediate_operands,parse_operands_states,' |
| 151 'parse_operand_positions'), | 195 'parse_operand_positions'), |
| 152 '-GT2') | 196 '-GT2') |
| 153 | 197 |
| 154 automata = list(decoder32 + validator32 + decoder64 + validator64) | 198 automata = list(decoder32 + validator32 + decoder64 + validator64) |
| 155 | 199 |
| 200 # Prepair 'dfacheckdecoder' test. | |
| 201 # | |
| 202 # In this test, all acceptable instructions are enumerated | |
| 203 # by DFA traversal, and for each one objdump output and | |
| 204 # DFA-based decoder output are compared. | |
| 205 # It takes few hours to run the test, so it's not included | |
| 206 # into any suits and is supposed to be run manually when | |
| 207 # changes are made to DFA definitions. | |
| 208 # Also, since DFA generation is currently linux-only, | |
| 209 # this test is somewhat platform-dependent as well. | |
| 210 | |
| 211 test_env = env.Clone() | |
| 212 test_env.Append( | |
| 213 CCFLAGS=['-g', '-Wno-unused-function'], | |
| 214 LINKFLAGS='-g', | |
| 215 CPPPATH='unreviewed') | |
| 216 | |
| 217 test_dfa_object = test_env.Object('unreviewed/test_dfa.c') | |
| 218 | |
| 219 objdump, gas = env.Command( | |
| 220 target=['objdump', 'gas'], | |
| 221 source=['obtain_binutils.py'], | |
| 222 action='python ${SOURCES[0]} ${TARGETS[0]} ${TARGETS[1]}' | |
| 223 ) | |
| 224 | |
| 225 check_decoders = [] | |
| 226 | |
| 227 for bits in ('32', '64'): | |
| 228 (one_valid_instr_rl,) = env.Command( | |
| 229 target='one_valid_instruction_x86_%s.rl' % bits, | |
| 230 source=[gen_dfa] + INST_DEFS, | |
| 231 action=('${SOURCES[0]} -o ${TARGET} %s ' | |
| 232 '-d check_access,rex_prefix,vex_prefix,opcode ' | |
| 233 '-d parse_operands_states,parse_operands,instruction_name ' | |
| 234 '-m %s') % ( | |
| 235 ' '.join('${SOURCES[%d]}' % (i + 1) | |
|
Nick Bray
2012/08/09 20:16:20
Line breaking a generator expression makes it hard
Vlad Shcherbina
2012/08/10 15:17:39
Since this construction relies on the fact that SO
| |
| 236 for i in range(len(INST_DEFS))), | |
| 237 {'32': 'ia32', '64': 'amd64'}[bits]) | |
| 238 ) | |
| 239 | |
| 240 include_dir = one_valid_instr_rl.dir.get_abspath() | |
| 241 | |
| 242 (one_instr_dot,) = env.Command( | |
| 243 target='one_instruction_x86_%s.dot' % bits, | |
| 244 source=[ | |
| 245 'unreviewed/one_instruction_x86_%s.rl' % bits, | |
| 246 one_valid_instr_rl], | |
| 247 action=[ | |
| 248 '%s -V -I%s ${SOURCES[0]} -o ${TARGET}' % | |
| 249 (ragel_binary, include_dir)] | |
| 250 ) | |
| 251 | |
| 252 test_dfa_transitions = test_env.Command( | |
| 253 target='test_dfa_transitions_x86_%s.c' % bits, | |
| 254 source=['unreviewed/parse_dfa.py', one_instr_dot], | |
| 255 action='python ${SOURCES[0]} <"${SOURCES[1]}" >"${TARGET}"' | |
|
Nick Bray
2012/08/09 20:16:20
Not critical for this CL, but I'd delegate the log
Vlad Shcherbina
2012/08/10 15:17:39
I'll make it in a separate small CL.
| |
| 256 ) | |
| 257 | |
| 258 test_dfa_transitions_object = test_env.Object(test_dfa_transitions) | |
| 259 | |
| 260 test_dfa = test_env.ComponentProgram( | |
| 261 'test_dfa_x86_%s' % bits, | |
| 262 [test_dfa_object, test_dfa_transitions_object]) | |
| 263 | |
| 264 fast_temp_for_test = '/dev/shm' | |
| 265 | |
| 266 (check_decoder,) = env.Command( | |
| 267 target='objdump_test_fake_file_%s' % bits, | |
| 268 source=[ | |
| 269 'unreviewed/run_objdump_test.py', | |
| 270 gas, | |
| 271 objdump, | |
| 272 decoder_test, | |
| 273 'unreviewed/decoder_test_one_file.sh', | |
| 274 test_dfa, | |
| 275 ], | |
| 276 action=[( | |
| 277 'python ${SOURCES[0]} ' | |
| 278 '--gas="${SOURCES[1]} --%s" ' | |
| 279 '--objdump="${SOURCES[2]}" ' | |
| 280 '--decoder="${SOURCES[3]}" ' | |
| 281 '--tester="${SOURCES[4]}" ' | |
| 282 '-- ' | |
| 283 '"${SOURCES[5]}" "%s"' | |
| 284 ) % (bits, fast_temp_for_test), | |
| 285 ]) | |
| 286 check_decoders.append(check_decoder) | |
| 287 | |
| 288 SideEffect(fast_temp_for_test, check_decoders) | |
| 289 | |
| 156 env.AlwaysBuild(env.Alias('dfagen', automata)) | 290 env.AlwaysBuild(env.Alias('dfagen', automata)) |
| 157 env.AlwaysBuild(env.Alias('dfaclean', action=map(Delete, automata))) | 291 env.AlwaysBuild(env.Alias('dfaclean', action=map(Delete, automata))) |
| 158 | 292 env.AlwaysBuild(env.Alias('dfacheckdecoder', check_decoders)) |
| 159 # Generate 32 and 64 bit versions of decoders and validators. Both libraries | |
| 160 # are used for command-line decoder and validator those detect specific | |
| 161 # architecture of the ELF file provided. | |
| 162 env.ComponentLibrary('dfa_validate_x86_32', | |
| 163 ['gen/validator_x86_32.c']) | |
| 164 env.ComponentLibrary('dfa_validate_x86_64', | |
| 165 ['gen/validator_x86_64.c']) | |
| 166 env.ComponentLibrary('dfa_decode_x86_32', | |
| 167 ['gen/decoder_x86_32.c']) | |
| 168 env.ComponentLibrary('dfa_decode_x86_64', | |
| 169 ['gen/decoder_x86_64.c']) | |
| 170 | |
| 171 # Glue library called from service runtime. The source file depends on the | |
| 172 # target architecture. | |
| 173 caller_lib_bits = None | |
| 174 if env.Bit('target_x86_32'): | |
| 175 caller_lib_bits = '32' | |
| 176 if env.Bit('target_x86_64'): | |
| 177 caller_lib_bits = '64' | |
| 178 if caller_lib_bits: | |
| 179 caller_lib = 'dfa_validate_caller_x86_%s' % caller_lib_bits | |
| 180 env.ComponentLibrary(caller_lib, | |
| 181 ['unreviewed/dfa_validate_%s.c' % caller_lib_bits]) | |
| 182 | |
| 183 # Command-line decoder. | |
| 184 env.ComponentProgram( | |
| 185 'decoder_test', | |
| 186 ['unreviewed/decoder_test.c'], | |
| 187 EXTRA_LIBS=['dfa_decode_x86_32', 'dfa_decode_x86_64']) | |
| 188 | |
| 189 # Command-line validator. | |
| 190 validator_test_exe = env.ComponentProgram( | |
| 191 'validator_test', | |
| 192 ['unreviewed/validator_test.c'], | |
| 193 EXTRA_LIBS=['dfa_validate_x86_32', 'dfa_validate_x86_64']) | |
| 194 | |
| 195 # Python-based regression test. TODO(pasko): remove it when validator_tests are | |
| 196 # migrated to be able to run with the new validator. | |
| 197 if env.Bit('validator_ragel'): | |
| 198 test_node = env.CommandTest('validator_test_py.out', | |
| 199 command=['${PYTHON}', | |
| 200 env.File('unreviewed/validator_test.py'), | |
| 201 validator_test_exe]) | |
| 202 env.AddNodeToTestSuite(test_node, ['small_tests'], 'run_validator_test_py') | |
| OLD | NEW |