xref: /optee_os/scripts/gen_ldelf_hex.py (revision 15e14f8ff549038cea7fb0b9ea2cd1602d0292c4)
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
12try:
13    from elftools.elf.elffile import ELFFile
14    from elftools.elf.sections import SymbolTableSection
15    from elftools.elf.constants import P_FLAGS
16except ImportError:
17    print("""
18***
19Can't find elftools module. Probably it is not installed on your system.
20You can install this module with
21
22$ apt install python3-pyelftools
23
24if you are using Ubuntu. Or try to search for "pyelftools" or "elftools" in
25your package manager if you are using some other distribution.
26***
27""")
28    raise
29
30
31def round_up(n, m):
32    if n == 0:
33        return 0
34    else:
35        return (((n - 1) // m) + 1) * m
36
37
38def emit_load_segments(elffile, outf):
39    load_size = 0
40    code_size = 0
41    data_size = 0
42    load_segments = [s for s in elffile.iter_segments()
43                     if s['p_type'] == 'PT_LOAD']
44    prev_segment = None
45    pad = 0
46    pad_size = []
47    w_found = False
48    n = 0
49    # Check that load segments ordered by VA have the expected layout:
50    # read only first, then read-write. Compute padding at end of each segment,
51    # 0 if none is required.
52    for segment in load_segments:
53        if prev_segment:
54            pad = segment['p_vaddr'] - (prev_segment['p_vaddr'] +
55                                        prev_segment['p_filesz'])
56        else:
57            if segment['p_flags'] & P_FLAGS.PF_W:
58                print('Expected RO load segment(s) first')
59                sys.exit(1)
60        if segment['p_flags'] & P_FLAGS.PF_W:
61            if not w_found:
62                # End of RO segments, discard padding for the last one (it
63                # would just take up space in the generated C file)
64                pad = 0
65                w_found = True
66        else:
67            if w_found:
68                print('RO load segment found after RW one(s) (m={})'.format(n))
69                sys.exit(1)
70        if prev_segment:
71            if pad > 31:
72                # We expect segments to be tightly packed together for memory
73                # efficiency. 31 is an arbitrary, "sounds reasonable" value
74                # which might need to be adjusted -- who knows what the
75                # compiler/linker can do.
76                print('Warning: suspiciously large padding ({}) after load '
77                      'segment {}, please check'.format(pad, n-1))
78            pad_size.append(pad)
79        prev_segment = segment
80        n = n + 1
81    # Last padding is actual memsz and filesz difference
82    # The .bss section may typically be here
83    last_pad = segment['p_memsz'] - segment['p_filesz']
84    pad_size.append(last_pad)
85    n = 0
86    # Compute code_size, data_size and load_size
87    for segment in load_segments:
88        sz = segment['p_filesz'] + pad_size[n]
89        if segment['p_flags'] & P_FLAGS.PF_W:
90            data_size += sz
91        else:
92            code_size += sz
93        load_size += sz
94        n = n + 1
95    n = 0
96    i = 0
97    # Output data to C file
98    outf.write(b'const uint8_t ldelf_data[%d]' % round_up(load_size, 4096))
99    outf.write(b' __aligned(4096) = {\n')
100    for segment in load_segments:
101        data = segment.data()
102        if pad_size[n]:
103            # Pad with zeros if needed
104            data += bytearray(pad_size[n])
105        for j in range(len(data)):
106            if i % 8 == 0:
107                outf.write(b'\t')
108            outf.write(b'0x' + '{:02x}'.format(data[j]).encode('utf-8')
109                       + b',')
110            i = i + 1
111            if i % 8 == 0 or i == load_size:
112                outf.write(b'\n')
113            else:
114                outf.write(b' ')
115        n = n + 1
116    outf.write(b'};\n')
117
118    outf.write(b'const unsigned int ldelf_code_size = %d;\n' % code_size)
119    outf.write(b'const unsigned int ldelf_data_size = %d;\n' % data_size)
120
121
122def get_args():
123    parser = argparse.ArgumentParser()
124
125    parser.add_argument('--input',
126                        required=True, type=argparse.FileType('rb'),
127                        help='The input ldelf.elf')
128
129    parser.add_argument('--output',
130                        required=True, type=argparse.FileType('wb'),
131                        help='The output ldelf_hex.c')
132
133    return parser.parse_args()
134
135
136def main():
137    args = get_args()
138    inf = args.input
139    outf = args.output
140
141    elffile = ELFFile(inf)
142
143    outf.write(b'/* Automatically generated, do no edit */\n')
144    outf.write(b'#include <compiler.h>\n')
145    outf.write(b'#include <stdint.h>\n')
146    emit_load_segments(elffile, outf)
147    outf.write(b'const unsigned long ldelf_entry = %lu;\n' %
148               elffile.header['e_entry'])
149
150    inf.close()
151    outf.close()
152
153
154if __name__ == "__main__":
155    main()
156