Don't delete or unpack binutils and gcc: we get them from Git
[nativeclient.git] / ncv / ncval_test.py
blob162264a0cab91aa6ae1bbcd980c757f9912ee610
2 import os
3 import shutil
4 import subprocess
5 import tempfile
6 import unittest
8 import ncval_stubout
11 def write_file(filename, data):
12 fh = open(filename, "w")
13 try:
14 fh.write(data)
15 finally:
16 fh.close()
19 def read_file(filename):
20 fh = open(filename, "r")
21 try:
22 return fh.read()
23 finally:
24 fh.close()
27 class TempDirTestCase(unittest.TestCase):
29 def setUp(self):
30 self._on_teardown = []
32 def make_temp_dir(self):
33 temp_dir = tempfile.mkdtemp(prefix="tmp-%s-" % self.__class__.__name__)
34 def tear_down():
35 shutil.rmtree(temp_dir)
36 self._on_teardown.append(tear_down)
37 return temp_dir
39 def tearDown(self):
40 for func in reversed(self._on_teardown):
41 func()
44 class NcValTest(TempDirTestCase):
46 def assertEquals(self, x, y):
47 if x != y:
48 if type(x) == str and type(y) == str:
49 raise AssertionError('"%s"\n!="%s"' % (x, y))
50 raise AssertionError("%r != %r" % (x, y))
52 def _get_errors(self, obj_file):
53 proc = subprocess.Popen(["ncval", obj_file], stdout=subprocess.PIPE)
54 lines = [line for line in proc.stdout
55 if line.startswith("VALIDATOR")]
56 self.assertEquals(proc.wait(), 0)
57 return lines
59 def test_stub_out(self):
60 asm_file = os.path.join(self.make_temp_dir(), "example.S")
61 obj_file = os.path.join(self.make_temp_dir(), "example")
62 write_file(asm_file, """
63 .global _start
64 _start:
65 jmp 0x12345678
66 jmp *%ecx
67 mov %fs,%dx
68 ret
69 int $0x80
70 """)
71 subprocess.check_call(["nacl-gcc", "-nostartfiles", asm_file,
72 "-o", obj_file])
73 self.assertEquals("".join(self._get_errors(obj_file)), """\
74 VALIDATOR: 00010000 (00001000:?): Jump target out of range
75 VALIDATOR: 00010005 (00001005:2): Unsafe indirect jump
76 VALIDATOR: 00010007 (00001007:3): Bad prefix
77 VALIDATOR: 00010007 (00001007:3): Illegal instruction
78 VALIDATOR: 0001000a (0000100a:1): Illegal instruction
79 VALIDATOR: 0001000b (0000100b:2): Illegal instruction
80 """)
81 ncval_stubout.main([obj_file])
82 # Can't stub out bad jumps yet
83 self.assertEquals("".join(self._get_errors(obj_file)), """\
84 VALIDATOR: 00010000 (00001000:?): Jump target out of range
85 """)
87 def test_multiple_sections(self):
88 # Check that we handle file indexes correctly in the presence
89 # of multiple ELF sections.
90 asm_file = os.path.join(self.make_temp_dir(), "example.S")
91 obj_file = os.path.join(self.make_temp_dir(), "example")
92 write_file(asm_file, """
93 .global _start
94 _start:
95 nop
96 nop
97 .section .fini, "x", @progbits
98 ret
99 """)
100 subprocess.check_call(["nacl-gcc", "-nostartfiles", asm_file,
101 "-o", obj_file])
102 self.assertNotEquals(self._get_errors(obj_file), [])
103 ncval_stubout.main([obj_file])
104 self.assertEquals(self._get_errors(obj_file), [])
107 class JumpRewriterTest(TempDirTestCase):
109 def _assemble(self, asm_source):
110 asm_file = os.path.join(self.make_temp_dir(), "foo.S")
111 obj_file = os.path.join(self.make_temp_dir(), "foo.o")
112 write_file(asm_file, asm_source)
113 subprocess.check_call(["gcc", "-c", asm_file, "-o", obj_file])
114 return obj_file
116 def _disassemble(self, obj_file):
117 proc = subprocess.Popen(["objdump", "-d", obj_file],
118 stdout=subprocess.PIPE)
119 return proc.communicate()[0]
121 def assert_object_files_equal(self, obj_file, obj_file_expect):
122 if read_file(obj_file) != read_file(obj_file_expect):
123 raise AssertionError("Unexpected output:\n%s\nExpected:\n%s" %
124 (self._disassemble(obj_file),
125 self._disassemble(obj_file_expect)))
127 def test_rewriting(self):
128 original = """
129 // Instructions to be rewritten
130 nop; nop; nop
131 jmp *%eax
132 nop; nop; nop
133 jmp *%ebx
134 nop; nop; nop
135 jmp *%ecx
136 nop; nop; nop
137 jmp *%edx
139 nop; nop; nop
140 call *%eax
141 nop; nop; nop
142 call *%ebx
143 nop; nop; nop
144 call *%ecx
145 nop; nop; nop
146 call *%edx
148 rewritten = """
149 and $0xffffffe0, %eax
150 jmp *%eax
151 and $0xffffffe0, %ebx
152 jmp *%ebx
153 and $0xffffffe0, %ecx
154 jmp *%ecx
155 and $0xffffffe0, %edx
156 jmp *%edx
158 and $0xffffffe0, %eax
159 call *%eax
160 and $0xffffffe0, %ebx
161 call *%ebx
162 and $0xffffffe0, %ecx
163 call *%ecx
164 and $0xffffffe0, %edx
165 call *%edx
167 leave_alone = """
168 // These should be left alone
169 nop; nop; nop
170 jmp 0x12345678
171 mov $123, %eax
173 obj_file = self._assemble(original + leave_alone)
174 obj_file_expect = self._assemble(rewritten + leave_alone)
175 subprocess.check_call(["ncrewrite", obj_file])
176 self.assert_object_files_equal(obj_file, obj_file_expect)
178 obj_file = self._assemble(rewritten + leave_alone)
179 obj_file_expect = self._assemble(original + leave_alone)
180 subprocess.check_call(["ncrewrite", "--nop", obj_file])
181 self.assert_object_files_equal(obj_file, obj_file_expect)
183 def test_rewriting_missing_nops(self):
184 input_data = """
185 // Not enough preceding nops to rewrite
186 nop; nop
187 jmp *%ecx
189 obj_file = self._assemble(input_data * 2)
190 original = read_file(obj_file)
191 proc = subprocess.Popen(["ncrewrite", obj_file], stderr=subprocess.PIPE)
192 stderr = proc.communicate()[1]
193 self.assertEquals(stderr,
194 "00000002: cannot rewrite\n"
195 "00000006: cannot rewrite\n")
196 self.assertEquals(proc.wait(), 0)
197 # Object file should not have been changed.
198 self.assertEquals(original, read_file(obj_file))
201 if __name__ == "__main__":
202 unittest.main()