1# -*- python -*-
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.
5
6Import('env')
7
8# All tests below involve native assembler
9
10HALT_SLED_SIZE=32
11
12# Valgrind gets confused by these tests
13
14if env.IsRunningUnderValgrind():
15  Return()
16
17# glibc linker script enforces gap; test needs to be updated to use
18# custom linker script in this case.
19if env.Bit('nacl_glibc'):
20  Return()
21
22# TODO(dschuff): either re-enable this test after we have fully transitioned
23# to new-style layout and ironed out any potential gold issues, or remove
24# all pnacl-specific stuff below and in the asm files.
25#
26# It is unlikely that we will re-enable these tests under the PNaCl
27# toolchain, because they depend on invoking "ld" directly, which is not
28# really a supported use of the PNaCl toolchain.  These tests are too
29# dependent on the layout that the linker happens to produce, so the tests
30# are likely to break or become ineffective if the linker is changed.
31if env.Bit('bitcode'):
32  Return()
33
34# TODO(petarj): Enable these tests when llvm integrated assembler can handle
35# expressions from nacl_text_pad_test.S.
36if env.Bit('build_mips32') and env.Bit('nacl_clang'):
37  Return()
38
39# ----------------------------------------------------------
40# Tests that require a NaCl module
41# ----------------------------------------------------------
42
43if env.Bit('build_x86_32'):
44  nacl_text_pad_asm = 'arch/x86_32/nacl_text_pad_test.S'
45elif env.Bit('build_x86_64'):
46  nacl_text_pad_asm = 'arch/x86_64/nacl_text_pad_test.S'
47elif env.Bit('build_arm'):
48  nacl_text_pad_asm = 'arch/arm/nacl_text_pad_test.S'
49elif env.Bit('build_mips32'):
50  nacl_text_pad_asm = 'arch/mips/nacl_text_pad_test.S'
51else:
52  raise Exception('unknown architecture')
53
54def NewAsmEnv(env, defines, rodata_address, rwdata_address):
55  asm_env = env.Clone()
56  # NOTE(robertm): convert this to pure C code so that most of this
57  # special handling can be eliminated
58  link_cmd = ('${LD} -static -e _start ${TEXT_START} ${RO_START} ${RW_START}' +
59                ' -o ${TARGET} ${SOURCES}')
60
61  if env.Bit('build_x86_32'):
62    link_cmd += ' -melf_i386_nacl'
63  elif env.Bit('build_x86_64'):
64    link_cmd += ' -melf_x86_64_nacl'
65  elif env.Bit('build_arm'):
66    link_cmd += ' -marmelf_nacl'
67  elif env.Bit('build_mips32'):
68    link_cmd += ' -mmipselelf_nacl'
69  else:
70    raise Exception('unknown architecture')
71
72  asm_env['LINKCOM'] = link_cmd
73
74  asm_env.Append(CPPDEFINES = [
75                     ['NACL_BUILD_ARCH', '${BUILD_ARCHITECTURE}' ],
76                     ['NACL_BUILD_SUBARCH', '${BUILD_SUBARCH}' ],
77                 ])
78
79  asm_env.Append(CPPDEFINES=defines)
80
81  asm_env['TEXT_START'] = '--section-start .text=0x%x' % 0x20000
82  if rodata_address:
83    asm_env['RO_START'] = '-Trodata-segment=0x%x' % rodata_address
84  if rwdata_address:
85    asm_env['RW_START'] = '--section-start .data=0x%x' % rwdata_address
86
87  return asm_env
88
89
90ALLOCATION_SIZE   = 0x10000
91START_OF_TEXT     = 0x20000
92TEXT_SIZE_BOUND   = 0x10000  # estimate of test code size
93RODATA_SIZE_BOUND = 0x10000
94RWDATA_SIZE       = 0x4  # if we have rwdata, we must use exactly one word!
95
96
97def EndOfText(text_end_modulus):
98  return START_OF_TEXT + text_end_modulus
99
100
101def RoundUpToAllocSize(size):
102  return (size + ALLOCATION_SIZE - 1) & ~(ALLOCATION_SIZE - 1)
103
104
105def TextGap(text_end):
106  end_of_text = EndOfText(text_end)
107  rounded_end_of_text = RoundUpToAllocSize(end_of_text)
108  text_gap = rounded_end_of_text - end_of_text
109  return text_gap
110
111
112def NaturalRodataStart(text_end):
113  extra = 0
114  text_gap = TextGap(text_end)
115  if text_gap < HALT_SLED_SIZE:
116    extra = ALLOCATION_SIZE
117  return RoundUpToAllocSize(START_OF_TEXT + TEXT_SIZE_BOUND + extra)
118
119
120def ExpectedBreak(text_end, rodata_addr, rwdata_addr, rwdata_size):
121  # The new linker always pads the text segment out to a page boundary.
122  text_end = RoundUpToAllocSize(text_end)
123  if rwdata_addr is None:
124    if rodata_addr is None:
125      break_addr = NaturalRodataStart(text_end)
126    else:
127      break_addr = RoundUpToAllocSize(rodata_addr + RODATA_SIZE_BOUND)
128  else:
129    break_addr = rwdata_addr + rwdata_size
130  return break_addr
131
132test_specs = [ (0x10000,       'no'),
133               (0x10000 - 32, 'small'),
134               ( 0x8000,      'large'),
135               (0x10000 - 28, 'too_small') ]
136
137rwdata_address = None  # none for now
138
139for text_end, variant in test_specs:
140  for rodata_address, name_modifier in [ (None, ''),
141                                         (0, '_ro'),
142                                         (0x100000, '_ro_dyn') ]:
143
144    if rodata_address is None and env.UsesAbiNote():
145      # This toolchain always produces some rodata.
146      rodata_address = 0
147
148    # rodata_address is None when no .rodata section should appear
149    # in the generated nexe, and is 0 when it should appear in the
150    # natural location (as defined by the linker script); when it
151    # has a non-zero numeric value, then the .rodata section is
152    # forced to start at that address.
153    if rodata_address == 0:
154      rodata_address = NaturalRodataStart(text_end)
155    # rodata_address is None or a non-zero integer
156
157    break_address = ExpectedBreak(text_end,
158                                  rodata_address,
159                                  rwdata_address,
160                                  RWDATA_SIZE)
161
162    defines=[]
163    for (symbol, value) in [('TEXT_EXTEND', text_end),
164                            ('EXPECTED_BREAK', break_address),
165                            ('EXPECTED_RODATA', rodata_address),
166                            ('EXPECTED_RWDATA', rwdata_address)]:
167      if value is not None:
168        defines += [[symbol, str(value)]]
169
170    asm_env = NewAsmEnv(env, defines, rodata_address, rwdata_address)
171
172    base_name = 'nacl_text_' + variant + '_pad' + name_modifier + '_test'
173    obj = asm_env.ComponentObject(base_name + '.o', nacl_text_pad_asm)
174
175    nexe = asm_env.ComponentProgram(base_name, [obj])
176
177    if (rodata_address is not None and
178        rodata_address <= START_OF_TEXT + ALLOCATION_SIZE):
179      # With the new linker, the code segment is padded out to
180      # ALLOCATION_SIZE with nops, so the rodata segment starts immediately
181      # thereafter with no space for a halt sled.  Ensure sel_ldr catches
182      # that case.  NaClAbort() behaves differently when code coverage is
183      # enabled: it calls exit() rather than abort().
184      if 'TRUSTED_ENV' in env and env['TRUSTED_ENV'].Bit('coverage_enabled'):
185        expected_exit_status = 'naclabort_coverage'
186      else:
187        expected_exit_status = 'trusted_sigabrt'
188    else:
189      expected_exit_status = 0
190
191    node = env.CommandSelLdrTestNacl(base_name + '.out', nexe,
192                                     exit_status=expected_exit_status)
193
194    env.AddNodeToTestSuite(node,
195                           ['small_tests', 'sel_ldr_sled_tests'],
196                           'run_' + base_name)
197