xref: /optee_os/scripts/gen_tee_bin.py (revision f77987ae8c99f93a42f2085cf44c4a3116386b57)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2019, Linaro Limited
5#
6
7from __future__ import print_function
8from __future__ import division
9
10import argparse
11import sys
12import struct
13import re
14import hashlib
15try:
16    from elftools.elf.elffile import ELFFile
17    from elftools.elf.constants import SH_FLAGS
18    from elftools.elf.enums import ENUM_RELOC_TYPE_ARM
19    from elftools.elf.enums import ENUM_RELOC_TYPE_AARCH64
20    from elftools.elf.sections import SymbolTableSection
21    from elftools.elf.relocation import RelocationSection
22
23except ImportError:
24    print("""
25***
26Can't find elftools module. Probably it is not installed on your system.
27You can install this module with
28
29$ apt install python3-pyelftools
30
31if you are using Ubuntu. Or try to search for "pyelftools" or "elftools" in
32your package manager if you are using some other distribution.
33***
34""")
35    raise
36
37small_page_size = 4 * 1024
38elffile_symbols = None
39tee_pageable_bin = None
40tee_pager_bin = None
41tee_embdata_bin = None
42
43
44def eprint(*args, **kwargs):
45    print(*args, file=sys.stderr, **kwargs)
46
47
48def round_up(n, m):
49    if n == 0:
50        return 0
51    else:
52        return (((n - 1) // m) + 1) * m
53
54
55def get_arch_id(elffile):
56    e_machine = elffile.header['e_machine']
57    if e_machine == 'EM_ARM':
58        return 0
59    if e_machine == 'EM_AARCH64':
60        return 1
61    eprint('Unknown e_machine "%s"' % e_machine)
62    sys.exit(1)
63
64
65def get_symbol(elffile, name):
66    global elffile_symbols
67    global lsyms_def
68    if elffile_symbols is None:
69        elffile_symbols = dict()
70        lsyms_def = dict()
71        symbol_tables = [s for s in elffile.iter_sections()
72                         if isinstance(s, SymbolTableSection)]
73        for section in symbol_tables:
74            for symbol in section.iter_symbols():
75                if symbol['st_info']['bind'] == 'STB_GLOBAL':
76                    elffile_symbols[symbol.name] = symbol
77                elif symbol['st_info']['bind'] == 'STB_LOCAL':
78                    if symbol.name not in elffile_symbols.keys():
79                        elffile_symbols[symbol.name] = symbol
80                        if symbol.name not in lsyms_def.keys():
81                            lsyms_def[symbol.name] = 1
82                        else:
83                            lsyms_def[symbol.name] += 1
84
85    if name in lsyms_def.keys() and lsyms_def[name] > 1:
86        eprint("Multiple definitions of local symbol %s" % name)
87        sys.exit(1)
88    if name not in elffile_symbols.keys():
89        eprint("Cannot find symbol %s" % name)
90        sys.exit(1)
91
92    return elffile_symbols[name]
93
94
95def get_sections(elffile, pad_to, dump_names):
96    last_end = 0
97    bin_data = bytearray()
98
99    for section in elffile.iter_sections():
100        if (section['sh_type'] == 'SHT_NOBITS' or
101                not (section['sh_flags'] & SH_FLAGS.SHF_ALLOC) or
102                not dump_names.match(section.name)):
103            continue
104
105        if last_end == 0:
106            bin_data = section.data()
107        else:
108            if section['sh_addr'] > last_end:
109                bin_data += bytearray(section['sh_addr'] - last_end)
110            bin_data += section.data()
111
112        last_end = section['sh_addr'] + section['sh_size']
113
114    if pad_to > last_end:
115        bin_data += bytearray(pad_to - last_end)
116        last_end = pad_to
117
118    return bin_data
119
120
121def get_pageable_bin(elffile):
122    global tee_pageable_bin
123    if tee_pageable_bin is None:
124        pad_to = 0
125        dump_names = re.compile(r'^\..*_(pageable|init)$')
126        tee_pageable_bin = get_sections(elffile, pad_to, dump_names)
127    return tee_pageable_bin
128
129
130def get_pager_bin(elffile):
131    global tee_pager_bin
132    if tee_pager_bin is None:
133        pad_to = get_symbol(elffile, '__data_end')['st_value']
134        dump_names = re.compile(
135            r'^\.(text|rodata|got|data|ARM\.exidx|ARM\.extab)$')
136        tee_pager_bin = get_sections(elffile, pad_to, dump_names)
137
138    return tee_pager_bin
139
140
141def get_reloc_bin(elffile):
142    if get_arch_id(elffile) == 0:
143        exp_rel_type = ENUM_RELOC_TYPE_ARM['R_ARM_RELATIVE']
144    else:
145        exp_rel_type = ENUM_RELOC_TYPE_AARCH64['R_AARCH64_RELATIVE']
146
147    link_address = get_symbol(elffile, '__text_start')['st_value']
148
149    addrs = []
150    for section in elffile.iter_sections():
151        if not isinstance(section, RelocationSection):
152            continue
153        for rel in section.iter_relocations():
154            if rel['r_info_type'] == 0:
155                continue
156            if rel['r_info_type'] != exp_rel_type:
157                eprint("Unexpected relocation type 0x%x" %
158                       rel['r_info_type'])
159                sys.exit(1)
160            addrs.append(rel['r_offset'] - link_address)
161
162    addrs.sort()
163    data = bytearray()
164    for a in addrs:
165        data += struct.pack('<I', a)
166
167    # Relocations has been reduced to only become the relative type with
168    # addend at the address (r_offset) of relocation, that is, increase by
169    # load_offset. The addresses (r_offset) are also sorted. The format is
170    # then:
171    # uint32_t: relocation #1
172    # uint32_t: relocation #2
173    # ...
174    # uint32_t: relocation #n
175
176    return data
177
178
179def get_hashes_bin(elffile):
180    pageable_bin = get_pageable_bin(elffile)
181    if len(pageable_bin) % small_page_size != 0:
182        eprint("pageable size not a multiple of 4K: "
183               "{}".format(paged_area_size))
184        sys.exit(1)
185
186    data = bytearray()
187    for n in range(0, len(pageable_bin), small_page_size):
188        page = pageable_bin[n:n + small_page_size]
189        data += hashlib.sha256(page).digest()
190
191    return data
192
193
194def get_embdata_bin(elffile):
195    global tee_embdata_bin
196    if tee_embdata_bin is None:
197        hashes_bin = get_hashes_bin(elffile)
198        reloc_bin = get_reloc_bin(elffile)
199
200        num_entries = 2
201        hash_offs = 2 * 4 + num_entries * (2 * 4)
202        hash_pad = round_up(len(hashes_bin), 8) - len(hashes_bin)
203        reloc_offs = hash_offs + len(hashes_bin) + hash_pad
204        reloc_pad = round_up(len(reloc_bin), 8) - len(reloc_bin)
205        total_len = reloc_offs + len(reloc_bin) + reloc_pad
206
207        tee_embdata_bin = struct.pack('<IIIIII', total_len, num_entries,
208                                      hash_offs, len(hashes_bin),
209                                      reloc_offs, len(reloc_bin))
210        tee_embdata_bin += hashes_bin + bytearray(hash_pad)
211        tee_embdata_bin += reloc_bin + bytearray(reloc_pad)
212
213    # The embedded data region is designed to be easy to extend when
214    # needed, it's formatted as:
215    # +---------------------------------------------------------+
216    # | uint32_t: Length of entire area including this field    |
217    # +---------------------------------------------------------+
218    # | uint32_t: Number of entries "2"                         |
219    # +---------------------------------------------------------+
220    # | uint32_t: Offset of hashes from beginning of table      |
221    # +---------------------------------------------------------+
222    # | uint32_t: Length of hashes                              |
223    # +---------------------------------------------------------+
224    # | uint32_t: Offset of relocations from beginning of table |
225    # +---------------------------------------------------------+
226    # | uint32_t: Length of relocations                         |
227    # +---------------------------------------------------------+
228    # | Data of hashes + eventual padding                       |
229    # +---------------------------------------------------------+
230    # | Data of relocations + eventual padding                  |
231    # +---------------------------------------------------------+
232
233    return tee_embdata_bin
234
235
236def output_pager_bin(elffile, outf):
237    outf.write(get_pager_bin(elffile))
238
239
240def output_pageable_bin(elffile, outf):
241    outf.write(get_pageable_bin(elffile))
242
243
244def get_init_load_addr(elffile):
245    init_load_addr = get_symbol(elffile, '_start')['st_value']
246    init_load_addr_hi = init_load_addr >> 32
247    init_load_addr_lo = init_load_addr & 0xffffffff
248    return init_load_addr_hi, init_load_addr_lo
249
250
251def output_header_v1(elffile, outf):
252    arch_id = get_arch_id(elffile)
253    pager_bin = get_pager_bin(elffile)
254    pageable_bin = get_pageable_bin(elffile)
255    embdata_bin = get_embdata_bin(elffile)
256    init_load_addr = get_init_load_addr(elffile)
257    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
258    pager_bin_size = len(pager_bin)
259    paged_area_size = len(pageable_bin)
260
261    init_mem_usage = (get_symbol(elffile, '__init_end')['st_value'] -
262                      get_symbol(elffile, '__text_start')['st_value'] +
263                      len(embdata_bin))
264
265    init_size = (pager_bin_size + min(init_bin_size, paged_area_size) +
266                 len(embdata_bin))
267    paged_size = paged_area_size - min(init_bin_size, paged_area_size)
268
269    magic = 0x4554504f  # 'OPTE'
270    version = 1
271    flags = 0
272    outf.write(struct.pack('<IBBHIIIII', magic, version, arch_id, flags,
273                           init_size, init_load_addr[0], init_load_addr[1],
274                           init_mem_usage, paged_size))
275    outf.write(pager_bin)
276    outf.write(pageable_bin[:init_bin_size])
277    outf.write(embdata_bin)
278    outf.write(pageable_bin[init_bin_size:])
279
280
281def output_header_v2(elffile, outf):
282    arch_id = get_arch_id(elffile)
283    init_load_addr = get_init_load_addr(elffile)
284    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
285    pager_bin_size = len(get_pager_bin(elffile))
286    paged_area_size = len(get_pageable_bin(elffile))
287    embdata_bin_size = len(get_embdata_bin(elffile))
288
289    init_size = (pager_bin_size + min(init_bin_size, paged_area_size) +
290                 embdata_bin_size)
291    paged_size = paged_area_size - min(init_bin_size, paged_area_size)
292
293    magic = 0x4554504f  # 'OPTE'
294    version = 2
295    flags = 0
296    nb_images = 1 if paged_size == 0 else 2
297    outf.write(struct.pack('<IBBHI', magic, version, arch_id, flags,
298                           nb_images))
299    outf.write(struct.pack('<IIII', init_load_addr[0], init_load_addr[1],
300                           0, init_size))
301    if nb_images == 2:
302        outf.write(struct.pack('<IIII', 0xffffffff, 0xffffffff, 1, paged_size))
303
304
305def output_pager_v2(elffile, outf):
306    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
307    pager_bin = get_pager_bin(elffile)
308    pageable_bin = get_pageable_bin(elffile)
309    embdata_bin = get_embdata_bin(elffile)
310
311    outf.write(pager_bin)
312    outf.write(pageable_bin[:init_bin_size])
313    outf.write(embdata_bin)
314
315
316def output_pageable_v2(elffile, outf):
317    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
318    outf.write(get_pageable_bin(elffile)[init_bin_size:])
319
320
321def get_args():
322    parser = argparse.ArgumentParser()
323
324    parser.add_argument('--input',
325                        required=True, type=argparse.FileType('rb'),
326                        help='The input tee.elf')
327
328    parser.add_argument('--out_tee_bin',
329                        required=False, type=argparse.FileType('wb'),
330                        help='The output tee.bin')
331
332    parser.add_argument('--out_tee_pager_bin',
333                        required=False, type=argparse.FileType('wb'),
334                        help='The output tee_pager.bin')
335
336    parser.add_argument('--out_tee_pageable_bin',
337                        required=False, type=argparse.FileType('wb'),
338                        help='The output tee_pageable.bin')
339
340    parser.add_argument('--out_header_v2',
341                        required=False, type=argparse.FileType('wb'),
342                        help='The output tee_header_v2.bin')
343
344    parser.add_argument('--out_pager_v2',
345                        required=False, type=argparse.FileType('wb'),
346                        help='The output tee_pager_v2.bin')
347
348    parser.add_argument('--out_pageable_v2',
349                        required=False, type=argparse.FileType('wb'),
350                        help='The output tee_pageable_v2.bin')
351
352    return parser.parse_args()
353
354
355def main():
356    args = get_args()
357
358    elffile = ELFFile(args.input)
359
360    if args.out_tee_bin:
361        output_header_v1(elffile, args.out_tee_bin)
362
363    if args.out_tee_pager_bin:
364        output_pager_bin(elffile, args.out_tee_pager_bin)
365
366    if args.out_tee_pageable_bin:
367        output_pageable_bin(elffile, args.out_tee_pageable_bin)
368
369    if args.out_header_v2:
370        output_header_v2(elffile, args.out_header_v2)
371
372    if args.out_pager_v2:
373        output_pager_v2(elffile, args.out_pager_v2)
374
375    if args.out_pageable_v2:
376        output_pageable_v2(elffile, args.out_pageable_v2)
377
378
379if __name__ == "__main__":
380    main()
381