blob: 1c7fb63ed423f5a5c374636315867d2c6840a047 [file] [log] [blame]
David Brazdil6c63a262019-12-23 13:23:46 +00001#!/usr/bin/env python3
David Brazdild623d312019-12-19 16:04:06 +00002#
3# Copyright 2019 The Hafnium Authors.
4#
Andrew Walbrane959ec12020-06-17 15:01:09 +01005# Use of this source code is governed by a BSD-style
6# license that can be found in the LICENSE file or at
7# https://opensource.org/licenses/BSD-3-Clause.
David Brazdild623d312019-12-19 16:04:06 +00008
David Brazdild623d312019-12-19 16:04:06 +00009"""Check ELF file for assembly-level regressions.
10
11Objdumps the given ELF file and detects known assembly patterns, checking for
12regressions on bugs such as CPU erratas. Throws an exception if a broken pattern
13is detected.
14"""
15
16import argparse
17import os
18import re
19import subprocess
20import sys
21
22HF_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
23CLANG_ROOT = os.path.join(HF_ROOT, "prebuilts", "linux-x64", "clang")
24OBJDUMP = os.path.join(CLANG_ROOT, "bin", "llvm-objdump")
David Brazdila2f45212020-01-20 13:19:31 +000025NM = os.path.join(CLANG_ROOT, "bin", "llvm-nm")
David Brazdild623d312019-12-19 16:04:06 +000026
David Brazdila2f45212020-01-20 13:19:31 +000027def check_eret_speculation_barrier(args):
David Brazdild623d312019-12-19 16:04:06 +000028 """
29 Some ARM64 CPUs speculatively execute instructions after ERET.
30 Check that every ERET is followed by DSB NSH and ISB.
31 """
David Brazdila2f45212020-01-20 13:19:31 +000032
33 objdump_stdout = subprocess\
34 .check_output([ OBJDUMP, "-d", args.input_elf ])\
35 .decode("utf-8")\
36 .splitlines()
37
David Brazdild623d312019-12-19 16:04:06 +000038 found_eret = False
39
40 STATE_DEFAULT = 1
41 STATE_EXPECT_DSB_NSH = 2
42 STATE_EXPECT_ISB = 3
43
44 REGEX_ERET = re.compile(r"^\s*[0-9a-f]+:\s*e0 03 9f d6\s+eret$")
45 REGEX_DSB_NSH = re.compile(r"^\s*[0-9a-f]+:\s*9f 37 03 d5\s*dsb\s+nsh$")
46 REGEX_ISB = re.compile(r"^\s*[0-9a-f]+:\s*df 3f 03 d5\s+isb$")
47
48 state = STATE_DEFAULT
49 for line in objdump_stdout:
50 if state == STATE_DEFAULT:
51 if re.match(REGEX_ERET, line):
52 found_eret = True
53 state = STATE_EXPECT_DSB_NSH
54 elif state == STATE_EXPECT_DSB_NSH:
55 if re.match(REGEX_DSB_NSH, line):
56 state = STATE_EXPECT_ISB
57 else:
58 raise Exception("ERET not followed by DSB NSH")
59 elif state == STATE_EXPECT_ISB:
60 if re.match(REGEX_ISB, line):
61 state = STATE_DEFAULT
62 else:
63 raise Exception("ERET not followed by ISB")
64
65 # Ensure that at least one instance was found, otherwise the regexes are
66 # probably wrong.
67 if not found_eret:
68 raise Exception("Could not find any ERET instructions")
69
David Brazdila2f45212020-01-20 13:19:31 +000070def check_max_image_size(args):
71 """
72 Check that the ELF's effective image size does not exceed maximum
73 allowed image size, if specified in command-line arguments.
74 """
75
76 if args.max_image_size <= 0:
77 return
78
79 nm_stdout = subprocess\
80 .check_output([ NM, args.input_elf ])\
81 .decode("utf-8")\
82 .splitlines()
83
84 COLUMN_COUNT = 3
85 COLUMN_IDX_VALUE = 0
86 COLUMN_IDX_TYPE = 1
87 COLUMN_IDX_NAME = 2
88
89 image_size = None
90 for line in nm_stdout:
91 line = line.split()
92 if len(line) != COLUMN_COUNT:
93 raise Exception(
94 "Unexpected number of columns in NM output")
95
96 if line[COLUMN_IDX_NAME] == "image_size":
97 if line[COLUMN_IDX_TYPE] != "A":
98 raise Exception(
99 "Unexpected type of image_size symbol")
100 image_size = int(line[COLUMN_IDX_VALUE], 16)
101 break
102
103 if image_size is None:
104 raise Exception("Could not find value of image_size symbol")
105 elif image_size > args.max_image_size:
106 raise Exception(
107 "Image size exceeds maximum allowed image size " +
108 "({}B > {}B)".format(image_size, args.max_image_size))
109
David Brazdild623d312019-12-19 16:04:06 +0000110def Main():
111 parser = argparse.ArgumentParser()
112 parser.add_argument("input_elf",
113 help="ELF file to analyze")
114 parser.add_argument("stamp_file",
115 help="file to be touched if successful")
David Brazdila2f45212020-01-20 13:19:31 +0000116 parser.add_argument("--max-image-size",
117 required=False, type=int, default=0,
118 help="maximum allowed image size in bytes")
David Brazdild623d312019-12-19 16:04:06 +0000119 args = parser.parse_args()
120
David Brazdila2f45212020-01-20 13:19:31 +0000121 check_eret_speculation_barrier(args)
122 check_max_image_size(args)
David Brazdild623d312019-12-19 16:04:06 +0000123
124 # Touch `stamp_file`.
125 with open(args.stamp_file, "w"):
126 pass
127
128 return 0
129
130if __name__ == "__main__":
131 sys.exit(Main())