def test_computed_gotos(self): # Test for toolchain bug. # Bug 1: gcc outputs "jmp *%eax", fails to validate. # Bug 2: gcc outputs "nacljmp *%eax", fails to assemble. # Correct assembly output is "nacljmp %eax". code = """ int foo(int i) { void *addr[] = { &&label1, &&label2 }; goto *addr[i]; label1: return 1234; label2: return 4568; } int main() { return 0; } """ temp_dir = self.make_temp_dir() write_file(os.path.join(temp_dir, "code.c"), code) # ncval doesn't seem to accept .o files any more. # TODO: fix ncval. testutils.check_call([ "nacl-gcc", # "-c", os.path.join(temp_dir, "code.c"), "-o", os.path.join(temp_dir, "prog") ]) run_ncval(os.path.join(temp_dir, "prog"))
def test_ncval_handles_many_errors(self): # This tests for # http://code.google.com/p/nativeclient/issues/detail?id=915, # where ncval_annotate would truncate the number of results at 100. disallowed = """ #ifdef __i386__ __asm__("int $0x80"); #else # error Update this test for other architectures! #endif """ source = "int main() { %s }" % (disallowed * 150) temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call(["nacl-gcc", "-g", source_file, "-o", os.path.join(temp_dir, "prog")]) dest_file = os.path.join(self.make_temp_dir(), "file") subprocess.call([sys.executable, os.path.join(PARENT_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog")], stdout=open(dest_file, "w")) failures = len([line for line in open(dest_file, "r") if line.startswith("VALIDATOR")]) self.assertEquals(failures, 150)
def test_custom_linker_scripts_via_search_path(self): # Check that the linker will pick up linker scripts from the # "ldscripts" directory in the library search path (which is # specified with -L). # To test this, try to link to a symbol that is defined in our # example linker script. temp_dir = self.make_temp_dir() os.mkdir(os.path.join(temp_dir, "ldscripts")) write_file( os.path.join(temp_dir, "ldscripts", "elf_nacl.x"), """ foo = 0x1234; """, ) write_file( os.path.join(temp_dir, "prog.c"), """ void foo(); int _start() { foo(); return 0; } """, ) testutils.check_call( [ "nacl-gcc", "-nostartfiles", "-nostdlib", "-L%s" % temp_dir, os.path.join(temp_dir, "prog.c"), "-o", os.path.join(temp_dir, "prog"), ] )
def test_ncval_handles_many_errors(self): # This tests for # http://code.google.com/p/nativeclient/issues/detail?id=915, # where ncval_annotate would truncate the number of results at 100. disallowed = """ #if defined(__i386__) || defined(__x86_64__) __asm__("int $0x80"); #else # error Update this test for other architectures! #endif """ source = "void _start() { %s }" % (disallowed * 150) temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call([ "x86_64-nacl-gcc", "-g", "-nostartfiles", "-nostdlib", source_file, "-o", os.path.join(temp_dir, "prog") ] + NACL_CFLAGS) dest_file = os.path.join(self.make_temp_dir(), "file") subprocess.call([ sys.executable, os.path.join(PARENT_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog") ], stdout=open(dest_file, "w")) failures = len([ line for line in open(dest_file, "r") if line.startswith("VALIDATOR") ]) self.assertEquals(failures, 150)
def test_computed_gotos(self): # Test for toolchain bug. # Bug 1: gcc outputs "jmp *%eax", fails to validate. # Bug 2: gcc outputs "nacljmp *%eax", fails to assemble. # Correct assembly output is "nacljmp %eax". code = """ int foo(int i) { void *addr[] = { &&label1, &&label2 }; goto *addr[i]; label1: return 1234; label2: return 4568; } int main() { return 0; } """ temp_dir = self.make_temp_dir() write_file(os.path.join(temp_dir, "code.c"), code) # ncval doesn't seem to accept .o files any more. # TODO: fix ncval. testutils.check_call( ["nacl-gcc", os.path.join(temp_dir, "code.c"), "-o", os.path.join(temp_dir, "prog")] # "-c", ) run_ncval(os.path.join(temp_dir, "prog"))
def test_ncval_handles_many_errors(self): # This tests for # http://code.google.com/p/nativeclient/issues/detail?id=915, # where ncval_annotate would truncate the number of results at 100. disallowed = """ #if defined(__i386__) || defined(__x86_64__) __asm__("int $0x80"); #else # error Update this test for other architectures! #endif """ source = "void _start() { %s }" % (disallowed * 150) temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call(["x86_64-nacl-gcc", "-g", "-nostartfiles", "-nostdlib", source_file, "-o", os.path.join(temp_dir, "prog")] + NACL_CFLAGS) dest_file = os.path.join(self.make_temp_dir(), "file") subprocess.call([sys.executable, os.path.join(ANNOTATE_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog")], stdout=open(dest_file, "w")) # Filter unrecognized instructions that are printed, one per bundle. filter_pattern = ".*<<<<$" failures = len([line for line in open(dest_file, "r") if re.match(filter_pattern, line)]) self.assertEquals(failures, 10)
def test_ncval_returns_errors(self): # Check that ncval returns a non-zero return code when there is a # validation failure. source = """ int main() { #ifdef __i386__ __asm__("ret"); /* This comment appears in output */ #else # error Update this test for other architectures! #endif return 0; } """ temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call([ "nacl-gcc", "-g", source_file, "-o", os.path.join(temp_dir, "prog") ]) dest_file = os.path.join(self.make_temp_dir(), "file") rc = subprocess.call([ sys.executable, os.path.join(PARENT_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog") ], stdout=open(dest_file, "w")) # ncval_annotate should propagate the exit code through so that it # can be used as a substitute for ncval. self.assertEquals(rc, 1) # For some reason ncval produces two errors for the same instruction. # TODO(mseaborn): Tidy that up. expected = """ VALIDATOR: ADDRESS: ret instruction (not allowed) code: c3 ret func: main file: FILENAME:4 __asm__("ret"); /* This comment appears in output */ VALIDATOR: ADDRESS: Illegal instruction code: c3 ret func: main file: FILENAME:4 __asm__("ret"); /* This comment appears in output */ """ expected_pattern = re.escape(expected) expected_pattern = expected_pattern.replace("ADDRESS", "[0-9a-f]+") # Cygwin mixes \ and / in filenames, so be liberal in what we accept. expected_pattern = expected_pattern.replace("FILENAME", "\S+code.c") actual = read_file(dest_file).replace("\r", "") if re.match(expected_pattern + "$", actual) is None: raise AssertionError( "Output did not match.\n\nEXPECTED:\n%s\nACTUAL:\n%s" % (expected, actual))
def test_ncval_returns_errors(self): # Check that ncval returns a non-zero return code when there is a # validation failure. source = """ void _start() { #if defined(__i386__) || defined(__x86_64__) __asm__("ret"); /* This comment appears in output */ #else # error Update this test for other architectures! #endif return 0; } """ temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call([ "x86_64-nacl-gcc", "-g", "-nostartfiles", "-nostdlib", source_file, "-o", os.path.join(temp_dir, "prog") ] + NACL_CFLAGS) dest_file = os.path.join(self.make_temp_dir(), "file") rc = subprocess.call([ sys.executable, os.path.join(ANNOTATE_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog") ], stdout=open(dest_file, "w")) # ncval_annotate should propagate the exit code through so that it # can be used as a substitute for ncval. self.assertEquals(rc, 1) # Errors printed in two lines, with interspersed objdump output. # The first line starts with an ADDRESS and file/line. # The second is the error on the instruction, which ends with "<<<<". filter_pattern = "^[0-9a-f]+.*|.*<<<<$" actual_lines = [ line for line in open(dest_file, "r") if re.match(filter_pattern, line) ] actual = "".join(actual_lines) # Strip windows' carriage return characters. actual = actual.replace("\r", "") expected_pattern = """ ADDRESS \(FILENAME:[0-9]+, function _start\): unrecognized instruction \s+ADDRESS:\s+c3\s+retq?\s+<<<< """ expected_pattern = expected_pattern.replace("ADDRESS", "[0-9a-f]+") # Cygwin mixes \ and / in filenames, so be liberal in what we accept. expected_pattern = expected_pattern.replace("FILENAME", "code.c") if re.match(expected_pattern, "\n" + actual) is None: raise AssertionError( "Output did not match.\n\nEXPECTED:\n%s\nACTUAL:\n%s" % (expected_pattern, actual))
def test_ncval_returns_errors(self): # Check that ncval returns a non-zero return code when there is a # validation failure. source = """ int main() { #ifdef __i386__ __asm__("ret"); /* This comment appears in output */ #else # error Update this test for other architectures! #endif return 0; } """ temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call(["nacl-gcc", "-g", source_file, "-o", os.path.join(temp_dir, "prog")]) dest_file = os.path.join(self.make_temp_dir(), "file") rc = subprocess.call([sys.executable, os.path.join(PARENT_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog")], stdout=open(dest_file, "w")) # ncval_annotate should propagate the exit code through so that it # can be used as a substitute for ncval. self.assertEquals(rc, 1) # For some reason ncval produces two errors for the same instruction. # TODO(mseaborn): Tidy that up. expected = """ VALIDATOR: ADDRESS: ret instruction (not allowed) code: c3 ret func: main file: FILENAME:4 __asm__("ret"); /* This comment appears in output */ VALIDATOR: ADDRESS: Illegal instruction code: c3 ret func: main file: FILENAME:4 __asm__("ret"); /* This comment appears in output */ """ expected_pattern = re.escape(expected) expected_pattern = expected_pattern.replace("ADDRESS", "[0-9a-f]+") # Cygwin mixes \ and / in filenames, so be liberal in what we accept. expected_pattern = expected_pattern.replace("FILENAME", "\S+code.c") actual = read_file(dest_file).replace("\r", "") if re.match(expected_pattern + "$", actual) is None: raise AssertionError( "Output did not match.\n\nEXPECTED:\n%s\nACTUAL:\n%s" % (expected, actual))
def test_ncval_returns_errors(self): # Check that ncval returns a non-zero return code when there is a # validation failure. source = """ void _start() { #if defined(__i386__) || defined(__x86_64__) __asm__("ret"); /* This comment appears in output */ #else # error Update this test for other architectures! #endif return 0; } """ temp_dir = self.make_temp_dir() source_file = os.path.join(temp_dir, "code.c") write_file(source_file, source) testutils.check_call(["x86_64-nacl-gcc", "-g", "-nostartfiles", "-nostdlib", source_file, "-o", os.path.join(temp_dir, "prog")] + NACL_CFLAGS) dest_file = os.path.join(self.make_temp_dir(), "file") rc = subprocess.call([sys.executable, os.path.join(ANNOTATE_DIR, "ncval_annotate.py"), os.path.join(temp_dir, "prog")], stdout=open(dest_file, "w")) # ncval_annotate should propagate the exit code through so that it # can be used as a substitute for ncval. self.assertEquals(rc, 1) # Errors printed in two lines, with interspersed objdump output. # The first line starts with an ADDRESS and file/line. # The second is the error on the instruction, which ends with "<<<<". filter_pattern = "^[0-9a-f]+.*|.*<<<<$" actual_lines = [line for line in open(dest_file, "r") if re.match(filter_pattern, line)] actual = "".join(actual_lines) # Strip windows' carriage return characters. actual = actual.replace("\r", "") expected_pattern = """ ADDRESS \(FILENAME:[0-9]+, function _start\): unrecognized instruction \s+ADDRESS:\s+c3\s+retq?\s+<<<< """ expected_pattern = expected_pattern.replace("ADDRESS", "[0-9a-f]+") # Cygwin mixes \ and / in filenames, so be liberal in what we accept. expected_pattern = expected_pattern.replace("FILENAME", "code.c") if re.match(expected_pattern, "\n" + actual) is None: raise AssertionError( "Output did not match.\n\nEXPECTED:\n%s\nACTUAL:\n%s" % (expected_pattern, actual))
def test_library_plt_entries(self): # Checks that the PLT entries generated by ld for relocatable # (PIC) objects pass the validator. library_code = """ void bar(void); void foo(void) { bar(); /* Linker creates a PLT entry for this reference. */ } """ temp_dir = self.make_temp_dir() write_file(os.path.join(temp_dir, "code.c"), library_code) testutils.check_call([ "nacl-gcc", "-nostdlib", "-shared", "-fPIC", "-Wl,-T,%s" % os.path.join(LDSCRIPTS_DIR, "elf_nacl.xs"), os.path.join(temp_dir, "code.c"), "-o", os.path.join(temp_dir, "code.so") ]) run_ncval(os.path.join(temp_dir, "code.so"))
def test_ncval_returns_errors(self): # Check that ncval returns a non-zero return code when there is a # validation failure. code = """ int main() { #ifdef __i386__ __asm__("ret"); #else # error Update this test for other architectures! #endif return 0; } """ temp_dir = self.make_temp_dir() write_file(os.path.join(temp_dir, "code.c"), code) testutils.check_call(["nacl-gcc", os.path.join(temp_dir, "code.c"), "-o", os.path.join(temp_dir, "prog")]) rc = subprocess.call(["ncval", os.path.join(temp_dir, "prog")], stdout=open(os.devnull, "w")) self.assertEquals(rc, 1)
def test_ncval_returns_errors(self): # Check that ncval returns a non-zero return code when there is a # validation failure. code = """ int main() { #ifdef __i386__ __asm__("ret"); #else # error Update this test for other architectures! #endif return 0; } """ temp_dir = self.make_temp_dir() write_file(os.path.join(temp_dir, "code.c"), code) testutils.check_call([ "nacl-gcc", os.path.join(temp_dir, "code.c"), "-o", os.path.join(temp_dir, "prog") ]) rc = subprocess.call(["ncval", os.path.join(temp_dir, "prog")], stdout=open(os.devnull, "w")) self.assertEquals(rc, 1)
def test_custom_linker_scripts_via_search_path(self): # Check that the linker will pick up linker scripts from the # "ldscripts" directory in the library search path (which is # specified with -L). # To test this, try to link to a symbol that is defined in our # example linker script. temp_dir = self.make_temp_dir() os.mkdir(os.path.join(temp_dir, "ldscripts")) write_file(os.path.join(temp_dir, "ldscripts", "elf_nacl.x"), """ foo = 0x1234; """) write_file(os.path.join(temp_dir, "prog.c"), """ void foo(); int _start() { foo(); return 0; } """) testutils.check_call([ "nacl-gcc", "-nostartfiles", "-nostdlib", "-L%s" % temp_dir, os.path.join(temp_dir, "prog.c"), "-o", os.path.join(temp_dir, "prog") ])
def test_executable_plt_entries(self): # Checks that the PLT entries generated by ld for non-relocatable # (non-PIC) objects pass the validator. library_code = """ void foo(void) { } """ executable_code = """ void foo(void); int _start(void) { foo(); /* Linker creates a PLT entry for this reference. */ return 0; } """ temp_dir = self.make_temp_dir() write_file(os.path.join(temp_dir, "library.c"), library_code) write_file(os.path.join(temp_dir, "executable.c"), executable_code) testutils.check_call([ "nacl-gcc", "-nostdlib", "-shared", "-fPIC", "-Wl,-T,%s" % os.path.join(LDSCRIPTS_DIR, "elf_nacl.xs"), os.path.join(temp_dir, "library.c"), "-o", os.path.join(temp_dir, "library.so") ]) # TODO: Make this work with elf_nacl.x. # elf_nacl.xs is supposed to be for shared libraries, not executables. testutils.check_call([ "nacl-gcc", "-nostdlib", "-fPIC", "-Wl,-T,%s" % os.path.join(LDSCRIPTS_DIR, "elf_nacl.xs"), os.path.join(temp_dir, "executable.c"), os.path.join(temp_dir, "library.so"), "-o", os.path.join(temp_dir, "prog") ]) run_ncval(os.path.join(temp_dir, "prog")) # Also validate the library as a sanity check, but it shouldn't # contain any PLT entries. run_ncval(os.path.join(temp_dir, "library.so"))