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_name(obj):
66    # Symbol or section .name might be a byte array or a string, we want a
67    # string
68    try:
69        name = obj.name.decode()
70    except (UnicodeDecodeError, AttributeError):
71        name = obj.name
72    return name
73
74
75def get_symbol(elffile, name):
76    global elffile_symbols
77    global lsyms_def
78    if elffile_symbols is None:
79        elffile_symbols = dict()
80        lsyms_def = dict()
81        symbol_tables = [s for s in elffile.iter_sections()
82                         if isinstance(s, SymbolTableSection)]
83        for section in symbol_tables:
84            for symbol in section.iter_symbols():
85                symbol_name = get_name(symbol)
86                if symbol['st_info']['bind'] == 'STB_GLOBAL':
87                    elffile_symbols[symbol_name] = symbol
88                elif symbol['st_info']['bind'] == 'STB_LOCAL':
89                    if symbol_name not in elffile_symbols.keys():
90                        elffile_symbols[symbol_name] = symbol
91                        if symbol_name not in lsyms_def.keys():
92                            lsyms_def[symbol_name] = 1
93                        else:
94                            lsyms_def[symbol_name] += 1
95
96    if name in lsyms_def.keys() and lsyms_def[name] > 1:
97        eprint("Multiple definitions of local symbol %s" % name)
98        sys.exit(1)
99    if name not in elffile_symbols.keys():
100        eprint("Cannot find symbol %s" % name)
101        sys.exit(1)
102
103    return elffile_symbols[name]
104
105
106def get_sections(elffile, pad_to, dump_names):
107    last_end = 0
108    bin_data = bytearray()
109
110    for section in elffile.iter_sections():
111        section_name = get_name(section)
112        if (section['sh_type'] == 'SHT_NOBITS' or
113                not (section['sh_flags'] & SH_FLAGS.SHF_ALLOC) or
114                not dump_names.match(section_name)):
115            continue
116
117        if last_end == 0:
118            bin_data = section.data()
119        else:
120            if section['sh_addr'] > last_end:
121                bin_data += bytearray(section['sh_addr'] - last_end)
122            bin_data += section.data()
123
124        last_end = section['sh_addr'] + section['sh_size']
125
126    if pad_to > last_end:
127        bin_data += bytearray(pad_to - last_end)
128        last_end = pad_to
129
130    return bin_data
131
132
133def get_pageable_bin(elffile):
134    global tee_pageable_bin
135    if tee_pageable_bin is None:
136        pad_to = 0
137        dump_names = re.compile(r'^\..*_(pageable|init)$')
138        tee_pageable_bin = get_sections(elffile, pad_to, dump_names)
139    return tee_pageable_bin
140
141
142def get_pager_bin(elffile):
143    global tee_pager_bin
144    if tee_pager_bin is None:
145        pad_to = get_symbol(elffile, '__data_end')['st_value']
146        dump_names = re.compile(
147            r'^\.(text|nex_data|rodata|got|data|ARM\.exidx|ARM\.extab)$')
148        tee_pager_bin = get_sections(elffile, pad_to, dump_names)
149
150    return tee_pager_bin
151
152
153def get_reloc_bin(elffile):
154    if get_arch_id(elffile) == 0:
155        exp_rel_type = ENUM_RELOC_TYPE_ARM['R_ARM_RELATIVE']
156    else:
157        exp_rel_type = ENUM_RELOC_TYPE_AARCH64['R_AARCH64_RELATIVE']
158
159    link_address = get_symbol(elffile, '__text_start')['st_value']
160
161    addrs = []
162    for section in elffile.iter_sections():
163        if not isinstance(section, RelocationSection):
164            continue
165        for rel in section.iter_relocations():
166            if rel['r_info_type'] == 0:
167                continue
168            if rel['r_info_type'] != exp_rel_type:
169                eprint("Unexpected relocation type 0x%x" %
170                       rel['r_info_type'])
171                sys.exit(1)
172            addrs.append(rel['r_offset'] - link_address)
173
174    addrs.sort()
175    data = bytearray()
176    for a in addrs:
177        data += struct.pack('<I', a)
178
179    # Relocations has been reduced to only become the relative type with
180    # addend at the address (r_offset) of relocation, that is, increase by
181    # load_offset. The addresses (r_offset) are also sorted. The format is
182    # then:
183    # uint32_t: relocation #1
184    # uint32_t: relocation #2
185    # ...
186    # uint32_t: relocation #n
187
188    return data
189
190
191def get_hashes_bin(elffile):
192    pageable_bin = get_pageable_bin(elffile)
193    if len(pageable_bin) % small_page_size != 0:
194        eprint("pageable size not a multiple of 4K: "
195               "{}".format(paged_area_size))
196        sys.exit(1)
197
198    data = bytearray()
199    for n in range(0, len(pageable_bin), small_page_size):
200        page = pageable_bin[n:n + small_page_size]
201        data += hashlib.sha256(page).digest()
202
203    return data
204
205
206def get_embdata_bin(elffile):
207    global tee_embdata_bin
208    if tee_embdata_bin is None:
209        hashes_bin = get_hashes_bin(elffile)
210        reloc_bin = get_reloc_bin(elffile)
211
212        num_entries = 2
213        hash_offs = 2 * 4 + num_entries * (2 * 4)
214        hash_pad = round_up(len(hashes_bin), 8) - len(hashes_bin)
215        reloc_offs = hash_offs + len(hashes_bin) + hash_pad
216        reloc_pad = round_up(len(reloc_bin), 8) - len(reloc_bin)
217        total_len = reloc_offs + len(reloc_bin) + reloc_pad
218
219        tee_embdata_bin = struct.pack('<IIIIII', total_len, num_entries,
220                                      hash_offs, len(hashes_bin),
221                                      reloc_offs, len(reloc_bin))
222        tee_embdata_bin += hashes_bin + bytearray(hash_pad)
223        tee_embdata_bin += reloc_bin + bytearray(reloc_pad)
224
225    # The embedded data region is designed to be easy to extend when
226    # needed, it's formatted as:
227    # +---------------------------------------------------------+
228    # | uint32_t: Length of entire area including this field    |
229    # +---------------------------------------------------------+
230    # | uint32_t: Number of entries "2"                         |
231    # +---------------------------------------------------------+
232    # | uint32_t: Offset of hashes from beginning of table      |
233    # +---------------------------------------------------------+
234    # | uint32_t: Length of hashes                              |
235    # +---------------------------------------------------------+
236    # | uint32_t: Offset of relocations from beginning of table |
237    # +---------------------------------------------------------+
238    # | uint32_t: Length of relocations                         |
239    # +---------------------------------------------------------+
240    # | Data of hashes + eventual padding                       |
241    # +---------------------------------------------------------+
242    # | Data of relocations + eventual padding                  |
243    # +---------------------------------------------------------+
244
245    return tee_embdata_bin
246
247
248def output_pager_bin(elffile, outf):
249    outf.write(get_pager_bin(elffile))
250
251
252def output_pageable_bin(elffile, outf):
253    outf.write(get_pageable_bin(elffile))
254
255
256def get_init_load_addr(elffile):
257    init_load_addr = get_symbol(elffile, '_start')['st_value']
258    init_load_addr_hi = init_load_addr >> 32
259    init_load_addr_lo = init_load_addr & 0xffffffff
260    return init_load_addr_hi, init_load_addr_lo
261
262
263def output_raw_bin(elffile, outf):
264    pager_bin = get_pager_bin(elffile)
265    pageable_bin = get_pageable_bin(elffile)
266    embdata_bin = get_embdata_bin(elffile)
267    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
268
269    outf.write(pager_bin)
270    outf.write(pageable_bin[:init_bin_size])
271    outf.write(embdata_bin)
272    outf.write(pageable_bin[init_bin_size:])
273
274
275def output_header_v1(elffile, outf):
276    arch_id = get_arch_id(elffile)
277    pager_bin = get_pager_bin(elffile)
278    pageable_bin = get_pageable_bin(elffile)
279    embdata_bin = get_embdata_bin(elffile)
280    init_load_addr = get_init_load_addr(elffile)
281    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
282    pager_bin_size = len(pager_bin)
283    paged_area_size = len(pageable_bin)
284
285    init_mem_usage = (get_symbol(elffile, '__get_tee_init_end')['st_value'] -
286                      get_symbol(elffile, '__text_start')['st_value'] +
287                      len(embdata_bin))
288
289    init_size = (pager_bin_size + min(init_bin_size, paged_area_size) +
290                 len(embdata_bin))
291    paged_size = paged_area_size - min(init_bin_size, paged_area_size)
292
293    magic = 0x4554504f  # 'OPTE'
294    version = 1
295    flags = 0
296    outf.write(struct.pack('<IBBHIIIII', magic, version, arch_id, flags,
297                           init_size, init_load_addr[0], init_load_addr[1],
298                           init_mem_usage, paged_size))
299    outf.write(pager_bin)
300    outf.write(pageable_bin[:init_bin_size])
301    outf.write(embdata_bin)
302    outf.write(pageable_bin[init_bin_size:])
303
304
305def output_header_v2(elffile, outf):
306    arch_id = get_arch_id(elffile)
307    init_load_addr = get_init_load_addr(elffile)
308    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
309    pager_bin_size = len(get_pager_bin(elffile))
310    paged_area_size = len(get_pageable_bin(elffile))
311    embdata_bin_size = len(get_embdata_bin(elffile))
312
313    init_size = (pager_bin_size + min(init_bin_size, paged_area_size) +
314                 embdata_bin_size)
315    paged_size = paged_area_size - min(init_bin_size, paged_area_size)
316
317    magic = 0x4554504f  # 'OPTE'
318    version = 2
319    flags = 0
320    nb_images = 1 if paged_size == 0 else 2
321    outf.write(struct.pack('<IBBHI', magic, version, arch_id, flags,
322                           nb_images))
323    outf.write(struct.pack('<IIII', init_load_addr[0], init_load_addr[1],
324                           0, init_size))
325    if nb_images == 2:
326        outf.write(struct.pack('<IIII', 0xffffffff, 0xffffffff, 1, paged_size))
327
328
329def output_pager_v2(elffile, outf):
330    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
331    pager_bin = get_pager_bin(elffile)
332    pageable_bin = get_pageable_bin(elffile)
333    embdata_bin = get_embdata_bin(elffile)
334
335    outf.write(pager_bin)
336    outf.write(pageable_bin[:init_bin_size])
337    outf.write(embdata_bin)
338
339
340def output_pageable_v2(elffile, outf):
341    init_bin_size = get_symbol(elffile, '__init_size')['st_value']
342    outf.write(get_pageable_bin(elffile)[init_bin_size:])
343
344
345def get_args():
346    parser = argparse.ArgumentParser()
347
348    parser.add_argument('--input',
349                        required=True, type=argparse.FileType('rb'),
350                        help='The input tee.elf')
351
352    parser.add_argument('--out_tee_bin',
353                        required=False, type=argparse.FileType('wb'),
354                        help='The output tee.bin')
355
356    parser.add_argument('--out_tee_raw_bin',
357                        required=False, type=argparse.FileType('wb'),
358                        help='The output tee_raw.bin')
359
360    parser.add_argument('--out_tee_pager_bin',
361                        required=False, type=argparse.FileType('wb'),
362                        help='The output tee_pager.bin')
363
364    parser.add_argument('--out_tee_pageable_bin',
365                        required=False, type=argparse.FileType('wb'),
366                        help='The output tee_pageable.bin')
367
368    parser.add_argument('--out_header_v2',
369                        required=False, type=argparse.FileType('wb'),
370                        help='The output tee_header_v2.bin')
371
372    parser.add_argument('--out_pager_v2',
373                        required=False, type=argparse.FileType('wb'),
374                        help='The output tee_pager_v2.bin')
375
376    parser.add_argument('--out_pageable_v2',
377                        required=False, type=argparse.FileType('wb'),
378                        help='The output tee_pageable_v2.bin')
379
380    return parser.parse_args()
381
382
383def main():
384    args = get_args()
385
386    elffile = ELFFile(args.input)
387
388    if args.out_tee_raw_bin:
389        output_raw_bin(elffile, args.out_tee_raw_bin)
390
391    if args.out_tee_bin:
392        output_header_v1(elffile, args.out_tee_bin)
393
394    if args.out_tee_pager_bin:
395        output_pager_bin(elffile, args.out_tee_pager_bin)
396
397    if args.out_tee_pageable_bin:
398        output_pageable_bin(elffile, args.out_tee_pageable_bin)
399
400    if args.out_header_v2:
401        output_header_v2(elffile, args.out_header_v2)
402
403    if args.out_pager_v2:
404        output_pager_v2(elffile, args.out_pager_v2)
405
406    if args.out_pageable_v2:
407        output_pageable_v2(elffile, args.out_pageable_v2)
408
409
410if __name__ == "__main__":
411    main()
412