1# -*- python -*-
2# Copyright (c) 2015 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 os
7import os.path
8
9Import('env')
10
11if env.Bit('bitcode'):
12  Return()
13
14# elf_loader requires some IRT interfaces.
15if not env.Bit('tests_use_irt'):
16  Return()
17
18# The builds here don't make sense for dynamic linking.
19if not env.Bit('nacl_static_link'):
20  Return()
21
22# elf_loader only handles ELFCLASS32, not ELFCLASS64.
23# TODO(mcgrathr): Remove this check when x86-64 switches to ELFCLASS32.
24if env.Bit('build_x86_64'):
25  Return()
26
27nacl_env = env['NACL_ENV']
28loader_nexe = nacl_env.File('${STAGING_DIR}/elf_loader.nexe')
29
30golden_file = env.File('echo.stdout')
31echo_args = open(str(golden_file)).read().splitlines()
32
33def BuildWithFlags(name, source, link_flags, c_flags=[], cppdefines=[]):
34  prog_env = env.Clone()
35  prog_env.Append(LINKFLAGS=link_flags)
36  prog_env.Append(CCFLAGS=c_flags)
37  prog_env.Append(CPPDEFINES=cppdefines)
38  obj = prog_env.ComponentObject(name + '.o', source)
39  prog = prog_env.ComponentProgram(name, obj)
40  return prog
41
42def LoaderTest(name, depends_on=None, **kwargs):
43  out_name = 'elf_loader_%s_test.out' % name
44  run_name = 'run_elf_loader_%s_test' % name
45  node = env.CommandSelLdrTestNacl(out_name,
46                                   loader_nexe,
47                                   sel_ldr_flags=['-a'],
48                                   **kwargs)
49  if depends_on is not None:
50    env.Depends(node, depends_on)
51  env.AddNodeToTestSuite(node, ['small_tests', 'sel_ldr_tests'], run_name)
52
53# For each test foo we run it in two variants: foo, where mmap works;
54# and foo_dyncode, where mmap refuses to work for the code segment.
55# This tests the fallback path using dyncode_create, replicating the
56# situation in the browser for a nexe not in a "blessed" location.
57def LoaderTests(name, depends_on=None, **kwargs):
58  LoaderTest(name, depends_on, **kwargs)
59  LoaderTest(name + '_dyncode', depends_on,
60             osenv=['NACL_FAULT_INJECTION=' +
61                    'MMAP_FORCE_DESCRIPTOR_SAFETY_CHECK_FAIL=GF@'],
62             **kwargs)
63
64# These are arbitrary, but should be different from each, and positive.
65dyn_exit_status = 17
66pie_exit_status = 23
67
68# Build a tiny standalone ET_DYN object that will stand in for
69# a dynamic linker.  Test loading that as the main program.
70interp_nexe = BuildWithFlags('interp', 'interp.c',
71                             ['-nostdlib', '-shared'],
72                             ['-fPIC', '-fno-asynchronous-unwind-tables'],
73                             [['TEST_EXIT', str(dyn_exit_status)]])
74LoaderTests('dyn', args=[interp_nexe], exit_status=dyn_exit_status)
75
76# nacl-clang fails to grok -pie. See:
77#       https://code.google.com/p/nativeclient/issues/detail?id=4148
78# TODO(mcgrathr): Remove the conditional when that's fixed.
79if not env.Bit('nacl_clang'):
80  # Build a tiny PIE that uses 'interp' (above) as its "dynamic linker".
81  # Test loading an ET_DYN object with a PT_INTERP.
82  pie_nexe = BuildWithFlags('pie', 'interp.c',
83                            ['-nostdlib', '-pie',
84                             '-Wl,-dynamic-linker=%s' % interp_nexe.abspath],
85                            ['-fPIE', '-fno-asynchronous-unwind-tables'],
86                            [['TEST_EXIT', str(pie_exit_status)]])
87  LoaderTests('pie', args=[pie_nexe], exit_status=pie_exit_status)
88
89# elf_loader itself loads at the canonical "main nexe" address of 0x20000.
90# This address just needs to be high enough above that to leave space for
91# elf_loader's own code segment.
92text_start = '-Ttext-segment=0x100000'
93
94# Build a normal executable both with and without a PT_INTERP.
95def EchoTest(name, interpreter=None, loader_args=[]):
96  nexe_name = 'echo_' + name
97  cppdefines = []
98  if interpreter is not None:
99    cppdefines.append(['INTERPRETER', interpreter])
100  nexe = BuildWithFlags(nexe_name, 'echo.c',
101                        ['-Wl,' + text_start],
102                        cppdefines=cppdefines)
103  LoaderTests(nexe_name,
104              depends_on=interp_nexe if interpreter is not None else None,
105              args=loader_args + [nexe] + echo_args,
106              stdout_golden=golden_file)
107
108EchoTest('plain')
109
110# Turn a native pathname on Windows into a '/'-separated one.
111interp_abspath = os.path.splitdrive(interp_nexe.abspath)[1].split(os.sep)
112EchoTest('interp', '/'.join(interp_abspath))
113
114interp_name = '/' + interp_abspath[-1]
115interp_prefix = '/'.join(interp_abspath[:-1])
116EchoTest('interp_prefix', interp_name,
117         ['--interp-prefix', interp_prefix])
118
119onlycode_obj = env.ComponentObject('onlycode.o', 'onlycode.S')
120# Newer toolchains include an allocated SHT_NOTE section (.note.NaCl.ABI.*)
121# in every object file, that just contributes to RODATA.  We do not want
122# any RODATA at all for this test.
123onlycode_text_obj = env.Command('onlycode_text.o', onlycode_obj,
124                                '${OBJCOPY} -j .text ${SOURCES} ${TARGET}')
125onlycode_env = env.Clone()
126onlycode_env.Append(LINKFLAGS=[
127    '-nostdlib',
128    '-Wl,--build-id=none',  # A build ID is just more RODATA we don't want.
129    '-Wl,--section-start=.text=0x100000',
130    ])
131nexe = onlycode_env.ComponentProgram('onlycode', onlycode_text_obj)
132LoaderTests('onlycode', args=[nexe])
133