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