| 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_bits = None |
| 48 if env.Bit('target_x86_32'): |
| 49 caller_lib_bits = '32' |
| 50 if env.Bit('target_x86_64'): |
| 51 caller_lib_bits = '64' |
| 52 if caller_lib_bits: |
| 53 # TODO(shcherbina): |
| 54 # interpolate env.get('TARGET_FULLARCH') / env.get('TARGET_SUBARCH') instead |
| 55 caller_lib = 'dfa_validate_caller_x86_%s' % caller_lib_bits |
| 56 env.ComponentLibrary(caller_lib, |
| 57 ['unreviewed/dfa_validate_%s.c' % caller_lib_bits]) |
| 58 |
| 59 # Command-line decoder. |
| 60 decoder_test = env.ComponentProgram( |
| 61 'decoder_test', |
| 62 ['unreviewed/decoder_test.c'], |
| 63 EXTRA_LIBS=['dfa_decode_x86_32', 'dfa_decode_x86_64']) |
| 64 |
| 65 # Command-line validator. |
| 66 validator_test_exe = env.ComponentProgram( |
| 67 'validator_test', |
| 68 ['unreviewed/validator_test.c'], |
| 69 EXTRA_LIBS=['dfa_validate_x86_32', 'dfa_validate_x86_64']) |
| 70 |
| 71 # Python-based regression test. TODO(pasko): remove it when validator_tests are |
| 72 # migrated to be able to run with the new validator. |
| 73 if env.Bit('validator_ragel'): |
| 74 test_node = env.CommandTest('validator_test_py.out', |
| 75 command=['${PYTHON}', |
| 76 env.File('unreviewed/validator_test.py'), |
| 77 validator_test_exe]) |
| 78 env.AddNodeToTestSuite(test_node, ['small_tests'], 'run_validator_test_py') |
| 79 |
| 33 # Source generation: | 80 # Source generation: |
| 34 # | 81 # |
| 35 # dfagen : Regenerate any autogenerated source files. | 82 # dfagen : Regenerate any autogenerated source files. |
| 36 | 83 |
| 37 generate = False | 84 dfa_aliases = 'dfagen', 'dfaclean', 'dfacheckdecoder' |
| 38 if 'dfagen' in COMMAND_LINE_TARGETS or 'dfaclean' in COMMAND_LINE_TARGETS: | 85 |
| 39 generate = True | 86 generate = any(a in COMMAND_LINE_TARGETS for a in dfa_aliases) |
| 40 | 87 |
| 41 if generate: | 88 if generate: |
| 42 if not env.Bit('host_linux'): | 89 if not env.Bit('host_linux'): |
| 43 raise UserError('Right now DFA generation is only supported on Linux') | 90 raise UserError('Right now DFA generation is only supported on Linux') |
| 44 | 91 |
| 45 # Source generation step 1: Build generator of ragel files. | 92 # Source generation step 1: Build generator of ragel files. |
| 46 # | 93 # |
| 47 # We have generator which reads .def files and produced automaton definition. | 94 # We have generator which reads .def files and produced automaton definition. |
| 48 # | 95 # |
| 49 # Ragel is included in most Linux distributions, but it's not standard tool | 96 # 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. | 97 # 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 | 98 # 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). | 99 # non-POSIX platforms (in particular it's not Windows compatible). |
| 53 | 100 |
| 54 env_gen_dfa = env.Clone() | 101 env_gen_dfa = env.Clone() |
| 55 env_gen_dfa.Append(CCFLAGS=['-std=c++0x', '-DNACL_TRUSTED_BUT_NOT_TCB']) | 102 env_gen_dfa.Append(CCFLAGS=['-std=c++0x', '-DNACL_TRUSTED_BUT_NOT_TCB']) |
| 56 | 103 |
| 57 gen_dfa = env_gen_dfa.ComponentProgram( | 104 gen_dfa = env_gen_dfa.ComponentProgram( |
| 58 'gen_dfa', | 105 'gen_dfa', |
| 59 ['unreviewed/gen_dfa.cc']) | 106 ['unreviewed/gen_dfa.cc']) |
| 60 | 107 |
| 61 # Source generation step 2: Generate decoder automatas. | 108 # Source generation step 2: Generate decoder automata. |
| 62 # | 109 # |
| 63 # Now we are back to conditionally defining the large automata generated | 110 # Now we are back to conditionally defining the large automata generated |
| 64 # by gen_dfa. | 111 # by gen_dfa. |
| 65 | 112 |
| 66 def MakeAutomaton(bits, automaton, dfa_gen_actions, ragel_flags): | 113 def MakeAutomaton(bits, automaton, dfa_gen_actions, ragel_flags): |
| 67 rl_file = '%s_x86_%s_instruction.rl' % (automaton, bits) | 114 rl_file = '%s_x86_%s_instruction.rl' % (automaton, bits) |
| 68 | 115 |
| 69 # We are cheating here: there are two autogenerated files: | 116 # We are cheating here: there are two autogenerated files: |
| 70 # .rl and _consts.c, but we only track .rl one. This is safe because | 117 # .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 | 118 # _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 | 119 # file is not changed _consts.c is guaranteed to be the same (reverse |
| 73 # is not true). | 120 # is not true). |
| 121 |
| 74 const_file = '%s/%s_x86_%s_instruction_consts.c' % ( | 122 const_file = '%s/%s_x86_%s_instruction_consts.c' % ( |
| 75 val_src_dir, automaton, bits) | 123 val_src_dir, automaton, bits) |
| 76 | 124 |
| 77 exe_path = '${STAGING_DIR}/${PROGPREFIX}gen_dfa${PROGSUFFIX}' | |
| 78 env.Command( | 125 env.Command( |
| 79 target=rl_file, | 126 target=rl_file, |
| 80 source=[exe_path] + INST_DEFS, | 127 source=[gen_dfa] + INST_DEFS, |
| 81 action=( | 128 action=( |
| 82 '${SOURCES[0]} -o ${TARGET} -c %s -m %s -d %s %s') % ( | 129 '${SOURCES[0]} -o ${TARGET} -c %s -m %s -d %s %s') % ( |
| 83 # Const file (-c): not tracked by SCONS (see above) | 130 # Const file (-c): not tracked by SCONS (see above) |
| 84 const_file, | 131 const_file, |
| 85 # Argument for CPU type (-m): either "ia32" or "amd64". | 132 # Argument for CPU type (-m): either "ia32" or "amd64". |
| 86 {'32': 'ia32', '64': 'amd64'}[bits], | 133 {'32': 'ia32', '64': 'amd64'}[bits], |
| 87 # (-d): | 134 # (-d): |
| 88 dfa_gen_actions, | 135 dfa_gen_actions, |
| 89 # pass inst defs as remaining parameters | 136 # pass inst defs as remaining parameters |
| 90 ' '.join('${SOURCES[%d]}' % (i+1) | 137 ' '.join('${SOURCES[%d]}' % (i + 1) |
| 91 for i in range(len(INST_DEFS))) | 138 for i in range(len(INST_DEFS))) |
| 92 ) | 139 ) |
| 93 ) | 140 ) |
| 94 c_file = '%s_x86_%s.c' % (automaton, bits) | 141 c_file = '%s_x86_%s.c' % (automaton, bits) |
| 95 c_full_filename = '%s/%s' % (val_src_dir, c_file) | 142 c_full_filename = '%s/%s' % (val_src_dir, c_file) |
| 96 env.Command( | 143 env.Command( |
| 97 target=c_file, | 144 target=c_file, |
| 98 source=['unreviewed/%s_x86_%s.rl' % (automaton, bits), rl_file], | 145 source=['unreviewed/%s_x86_%s.rl' % (automaton, bits), rl_file], |
| 99 action=['%s %s -LL -I%s ${SOURCES[0]} -o ${TARGET}' % ( | 146 action=['%s %s -LL -I%s ${SOURCES[0]} -o ${TARGET}' % ( |
| 100 ragel_binary, ragel_flags, rl_src_dir)] | 147 ragel_binary, ragel_flags, rl_src_dir)] |
| 101 ) | 148 ) |
| 102 | 149 |
| 103 def InjectGeneratedFileHeader(target, source, env): | 150 def InjectGeneratedFileHeader(target, source, env): |
| 104 source_filename = source[0].get_abspath() | 151 source_filename = source[0].get_abspath() |
| 105 target_filename = target[0].get_abspath() | 152 target_filename = target[0].get_abspath() |
| 106 architecture = {'x86_32': 'ia32', | 153 architecture = {'x86_32': 'ia32', |
| 107 'x86_64': 'x86-64'}[target_filename[-8:-2]] | 154 'x86_64': 'x86-64'}[target_filename[-8:-2]] |
| 155 |
| 108 with open(source_filename, 'r') as source_file: | 156 with open(source_filename, 'r') as source_file: |
| 109 with open(target_filename, 'w') as target_file: | 157 comment, sep, rest = source_file.read().partition('*/') |
| 110 target_file.write( | 158 if sep == '': |
| 111 """/* native_client/%s | 159 raise UserError('Generated file %s does not have ' |
| 112 * THIS FILE IS AUTO-GENERATED. DO NOT EDIT. | 160 'header comment block' % source_filename) |
| 113 * Compiled for %s mode. | |
| 114 */""" % (target_filename, architecture)) | |
| 115 | 161 |
| 116 comment, sep, rest = source_file.read().partition('*/') | 162 with open(target_filename, 'w') as target_file: |
| 117 if sep == '': | 163 target_file.write( |
| 118 raise UserError('Generated file %s does not have ' | 164 ('/* native_client/%s\n' |
| 119 'header comment block' % source_filename) | 165 ' * THIS FILE IS AUTO-GENERATED. DO NOT EDIT.\n' |
| 120 target_file.write(rest) | 166 ' * Compiled for %s mode.\n' |
| 167 ' */') % (target_filename, architecture)) |
| 121 | 168 |
| 169 target_file.write(rest) |
| 170 |
| 171 # inject comments and place files to appropriate dir |
| 122 env.Command( | 172 env.Command( |
| 123 target=c_full_filename, | 173 target=c_full_filename, |
| 124 source=['%s/%s' % (rl_src_dir, c_file)], | 174 source=['%s/%s' % (rl_src_dir, c_file)], |
| 125 action=InjectGeneratedFileHeader | 175 action=InjectGeneratedFileHeader |
| 126 ) | 176 ) |
| 127 | 177 |
| 128 return rl_file, c_file, c_full_filename | 178 return rl_file, c_file, c_full_filename |
| 129 | 179 |
| 130 decoder32 = MakeAutomaton( | 180 decoder32 = MakeAutomaton( |
| 131 '32', 'decoder', | 181 '32', 'decoder', |
| 132 'check_access,opcode,parse_operands_states,mark_data_fields', | 182 'check_access,opcode,parse_operands_states,mark_data_fields', |
| 133 '-T0') | 183 '-T0') |
| 134 validator32 = MakeAutomaton( | 184 validator32 = MakeAutomaton( |
| 135 '32', 'validator', | 185 '32', 'validator', |
| 136 ('check_access,opcode,parse_operands,parse_operands_states,' | 186 ('check_access,opcode,parse_operands,parse_operands_states,' |
| 137 'instruction_name,mark_data_fields,nacl-forbidden,' | 187 'instruction_name,mark_data_fields,nacl-forbidden,' |
| 138 'imm_operand_action,rel_operand_action'), | 188 'imm_operand_action,rel_operand_action'), |
| 139 '-G2') | 189 '-G2') |
| 140 decoder64 = MakeAutomaton( | 190 decoder64 = MakeAutomaton( |
| 141 '64', 'decoder', | 191 '64', 'decoder', |
| 142 'check_access,opcode,parse_operands_states,mark_data_fields', | 192 'check_access,opcode,parse_operands_states,mark_data_fields', |
| 143 '-T0') | 193 '-T0') |
| 144 validator64 = MakeAutomaton( | 194 validator64 = MakeAutomaton( |
| 145 '64', 'validator', | 195 '64', 'validator', |
| 146 ('opcode,instruction_name,mark_data_fields,imm_operand_action,' | 196 ('opcode,instruction_name,mark_data_fields,imm_operand_action,' |
| 147 'rel_operand_action,nacl-forbidden,parse_nonwrite_registers,' | 197 'rel_operand_action,nacl-forbidden,parse_nonwrite_registers,' |
| 148 'parse_x87_operands,parse_mmx_operands,parse_xmm_operands,' | 198 'parse_x87_operands,parse_mmx_operands,parse_xmm_operands,' |
| 149 'parse_ymm_operands,parse_relative_operands,' | 199 'parse_ymm_operands,parse_relative_operands,' |
| 150 'parse_immediate_operands,parse_operands_states,' | 200 'parse_immediate_operands,parse_operands_states,' |
| 151 'parse_operand_positions'), | 201 'parse_operand_positions'), |
| 152 '-GT2') | 202 '-GT2') |
| 153 | 203 |
| 154 automata = list(decoder32 + validator32 + decoder64 + validator64) | 204 automata = list(decoder32 + validator32 + decoder64 + validator64) |
| 155 | 205 |
| 206 # Generate checkdecoder test |
| 207 |
| 208 test_env = env.Clone() |
| 209 test_env.Append( |
| 210 CCFLAGS=['-g', '-Wno-unused-function'], |
| 211 LINKFLAGS='-g', |
| 212 CPPPATH='unreviewed') |
| 213 |
| 214 test_dfa_object = test_env.Object('unreviewed/test_dfa.c') |
| 215 |
| 216 objdump, gas = env.Command( |
| 217 target=['objdump', 'gas'], |
| 218 source=['obtain_binutils.py'], |
| 219 action='python ${SOURCES[0]} ${TARGETS[0]} ${TARGETS[1]}' |
| 220 ) |
| 221 |
| 222 check_decoders = [] |
| 223 |
| 224 for bits in ('32', '64'): |
| 225 (one_valid_instr_rl,) = env.Command( |
| 226 target='one_valid_instruction_x86_%s.rl' % bits, |
| 227 source=[gen_dfa] + INST_DEFS, |
| 228 action=('${SOURCES[0]} -o ${TARGET} %s ' |
| 229 '-d check_access,rex_prefix,vex_prefix,opcode ' |
| 230 '-d parse_operands_states,parse_operands,instruction_name ' |
| 231 '-m %s') % ( |
| 232 ' '.join('${SOURCES[%d]}' % (i + 1) |
| 233 for i in range(len(INST_DEFS))), |
| 234 {'32': 'ia32', '64': 'amd64'}[bits]) |
| 235 ) |
| 236 |
| 237 include_dir = one_valid_instr_rl.dir.get_abspath() |
| 238 |
| 239 (one_instr_dot,) = env.Command( |
| 240 target='one_instruction_x86_%s.dot' % bits, |
| 241 source=[ |
| 242 'unreviewed/one_instruction_x86_%s.rl' % bits, |
| 243 one_valid_instr_rl], |
| 244 action=[ |
| 245 '%s -V -I%s ${SOURCES[0]} -o ${TARGET}' % |
| 246 (ragel_binary, include_dir)] |
| 247 ) |
| 248 |
| 249 test_dfa_transitions = test_env.Command( |
| 250 target='test_dfa_transitions_x86_%s.c' % bits, |
| 251 source=['unreviewed/parse_dfa.py', one_instr_dot], |
| 252 action='python ${SOURCES[0]} <"${SOURCES[1]}" >"${TARGET}"' |
| 253 ) |
| 254 |
| 255 test_dfa_transitions_object = test_env.Object(test_dfa_transitions) |
| 256 |
| 257 test_dfa = test_env.ComponentProgram( |
| 258 'test_dfa_x86_%s' % bits, |
| 259 [test_dfa_object, test_dfa_transitions_object]) |
| 260 |
| 261 fast_temp_for_test = '/dev/shm' |
| 262 |
| 263 (check_decoder,) = env.Command( |
| 264 target='objdump_test_fake_file_%s' % bits, |
| 265 source=[ |
| 266 'unreviewed/run_objdump_test.py', |
| 267 gas, |
| 268 objdump, |
| 269 decoder_test, |
| 270 'unreviewed/decoder_test_one_file.sh', |
| 271 test_dfa, |
| 272 ], |
| 273 action=[( |
| 274 'python ${SOURCES[0]} ' |
| 275 '--gas="${SOURCES[1]} --%s" ' |
| 276 '--objdump="${SOURCES[2]}" ' |
| 277 '--decoder="${SOURCES[3]}" ' |
| 278 '--tester="${SOURCES[4]}" ' |
| 279 '-- ' |
| 280 '"${SOURCES[5]}" "%s"' |
| 281 ) % (bits, fast_temp_for_test), |
| 282 ]) |
| 283 check_decoders.append(check_decoder) |
| 284 |
| 285 SideEffect(fast_temp_for_test, check_decoders) |
| 286 |
| 156 env.AlwaysBuild(env.Alias('dfagen', automata)) | 287 env.AlwaysBuild(env.Alias('dfagen', automata)) |
| 157 env.AlwaysBuild(env.Alias('dfaclean', action=map(Delete, automata))) | 288 env.AlwaysBuild(env.Alias('dfaclean', action=map(Delete, automata))) |
| 158 | 289 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 |