blob: a6123b296bb113bda2472418081959faf5022877 [file] [log] [blame]
Rouven Czerwinski84c0da02019-07-02 11:57:32 +02001#!/usr/bin/env python3
Jens Wiklanderb8c97752019-05-23 17:42:09 +02002# 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
Volodymyr Babchuk7ba36df2019-08-01 21:48:30 +030012try:
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
Jens Wiklanderb8c97752019-05-23 17:42:09 +020030
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
Jerome Forissierc706c242020-04-21 15:04:34 +020040 code_size = 0
Jerome Forissierd2fb6902019-07-04 18:34:47 +020041 data_size = 0
Jerome Forissierc706c242020-04-21 15:04:34 +020042 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
Jens Wiklanderb8c97752019-05-23 17:42:09 +020048 n = 0
Jerome Forissierc706c242020-04-21 15:04:34 +020049 # 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:
Jerome Forissier50d680c2020-05-05 14:20:39 +020068 print('RO load segment found after RW one(s) (m={})'.format(n))
Jerome Forissierc706c242020-04-21 15:04:34 +020069 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.
Jerome Forissier50d680c2020-05-05 14:20:39 +020076 print('Warning: suspiciously large padding ({}) after load '
77 'segment {}, please check'.format(pad, n-1))
Jerome Forissierc706c242020-04-21 15:04:34 +020078 pad_size.append(pad)
79 prev_segment = segment
80 n = n + 1
81 pad_size.append(0)
82 n = 0
83 # Compute code_size, data_size and load_size
84 for segment in load_segments:
85 sz = segment['p_filesz'] + pad_size[n]
86 if segment['p_flags'] & P_FLAGS.PF_W:
87 data_size += sz
88 else:
89 code_size += sz
90 load_size += sz
91 n = n + 1
92 n = 0
93 i = 0
94 # Output data to C file
Rouven Czerwinski84c0da02019-07-02 11:57:32 +020095 outf.write(b'const uint8_t ldelf_data[%d]' % round_up(load_size, 4096))
96 outf.write(b' __aligned(4096) = {\n')
Jerome Forissierc706c242020-04-21 15:04:34 +020097 for segment in load_segments:
98 data = segment.data()
99 if pad_size[n]:
100 # Pad with zeros if needed
101 data += bytearray(pad_size[n])
102 for j in range(len(data)):
103 if i % 8 == 0:
104 outf.write(b'\t')
105 outf.write(b'0x' + '{:02x}'.format(data[j]).encode('utf-8')
106 + b',')
107 i = i + 1
108 if i % 8 == 0 or i == load_size:
109 outf.write(b'\n')
110 else:
111 outf.write(b' ')
112 n = n + 1
Rouven Czerwinski84c0da02019-07-02 11:57:32 +0200113 outf.write(b'};\n')
Jens Wiklanderb8c97752019-05-23 17:42:09 +0200114
Rouven Czerwinski84c0da02019-07-02 11:57:32 +0200115 outf.write(b'const unsigned int ldelf_code_size = %d;\n' % code_size)
116 outf.write(b'const unsigned int ldelf_data_size = %d;\n' % data_size)
Jens Wiklanderb8c97752019-05-23 17:42:09 +0200117
118
119def get_args():
120 parser = argparse.ArgumentParser()
121
122 parser.add_argument('--input',
123 required=True, type=argparse.FileType('rb'),
124 help='The input ldelf.elf')
125
126 parser.add_argument('--output',
127 required=True, type=argparse.FileType('wb'),
128 help='The output ldelf_hex.c')
129
130 return parser.parse_args()
131
132
133def main():
134 args = get_args()
135 inf = args.input
136 outf = args.output
137
138 elffile = ELFFile(inf)
139
Rouven Czerwinski84c0da02019-07-02 11:57:32 +0200140 outf.write(b'/* Automatically generated, do no edit */\n')
141 outf.write(b'#include <compiler.h>\n')
142 outf.write(b'#include <stdint.h>\n')
Jens Wiklanderb8c97752019-05-23 17:42:09 +0200143 emit_load_segments(elffile, outf)
Rouven Czerwinski84c0da02019-07-02 11:57:32 +0200144 outf.write(b'const unsigned long ldelf_entry = %lu;\n' %
Jens Wiklanderb8c97752019-05-23 17:42:09 +0200145 elffile.header['e_entry'])
146
147 inf.close()
148 outf.close()
149
150
151if __name__ == "__main__":
152 main()