blob: fe48880d6bc6b4834276dae949999372e3a1b64e [file] [log] [blame] [edit]
#!/usr/bin/env python3
#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: GPL-2.0-only
#
"""
Extract information of interest to the seL4 image build process from ELF files.
THIS IS NOT A STABLE API. Use as a script, not a module.
"""
import argparse
import elftools.elf.elffile
import sys
from typing import BinaryIO
def get_aligned_size(n: int) -> int:
"""
Return the smallest multiple of 4KiB not less than the total of the loadable
segments. (In other words, the size will be returned as-is if it is an
exact multiple of 4KiB, otherwise it is rounded up to the next higher
multiple of 4KiB.)
"""
return n if n % 4096 == 0 else ((n // 4096) + 1) * 4096
def get_memory_usage(elf_file: BinaryIO, align: bool) -> int:
"""
Return the size in bytes occuped in memory of the loadable ELF segments from
the ELF object file `elf_file`.
"""
elf = elftools.elf.elffile.ELFFile(elf_file)
# We only care about loadable segments (p_type is "PT_LOAD"), and we
# want the size in memory of those segments (p_memsz), which can be
# greater than the size in the file (p_filesz). This is especially
# important for the BSS section. See elf(5).
# There may be gaps between segments; use the min+max vaddr of
# the loaded segments to calculate total usage.
min_vaddr = None
max_vaddr: int = 0
for seg in elf.iter_segments():
if seg['p_type'] == 'PT_LOAD':
if min_vaddr is None:
min_vaddr = seg['p_vaddr']
else:
min_vaddr = min(seg['p_vaddr'], min_vaddr)
max_vaddr = max(seg['p_vaddr'] + seg['p_memsz'], max_vaddr)
total: int = max_vaddr - min_vaddr
return get_aligned_size(total) if align else total
def get_memory_usage_from_file(filename: str, align: bool) -> int:
"""
Return the size in bytes occuped in memory of the loadable ELF segments from
the ELF object file `filename`.
"""
with open(filename, 'rb') as f:
return get_memory_size(f)
def main() -> int:
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""
Extract information of interest to the seL4 image build process from ELF files.
We extract the sizes of loadable ELF segments from the ELF files given as
operands and print their sum.
If the "--align" flag is specified, the space "after" each ELF file is aligned
to the next 4KiB boundary, increasing the total.
""")
parser.add_argument('elf_file', nargs='+', type=str,
help='ELF object file to examine')
parser.add_argument('--align', action='store_true',
help='align to 4KiB between files')
parser.add_argument('--reserve', metavar='BYTES', type=int, action='store',
default=0, help='number of additional bytes to reserve')
args = parser.parse_args()
regions = [get_memory_usage_from_file(elf, args.align)
for elf in args.elf_file]
regions.append(args.reserve)
total = sum(regions)
if args.align:
total = get_aligned_size(total)
print(total)
return 0
if __name__ == '__main__':
sys.exit(main())