|  | #!/usr/bin/env python3 | 
|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | '''A wrapper around riscv32-unknown-elf-ld for OTBN | 
|  |  | 
|  | This just adds the OTBN linker script and calls the underlying | 
|  | linker.''' | 
|  |  | 
|  | from contextlib import contextmanager | 
|  | import os | 
|  | import subprocess | 
|  | import sys | 
|  | import tempfile | 
|  | from typing import Iterator, List, Optional | 
|  |  | 
|  | from mako.template import Template  # type: ignore | 
|  | from mako import exceptions  # type: ignore | 
|  |  | 
|  | from shared.mem_layout import get_memory_layout | 
|  | from shared.toolchain import find_tool | 
|  |  | 
|  |  | 
|  | def interpolate_linker_script(in_path: str, out_path: str) -> None: | 
|  | mems = get_memory_layout() | 
|  |  | 
|  | try: | 
|  | template = Template(filename=in_path) | 
|  | rendered = template.render(imem_lma = mems['IMEM'][0], | 
|  | imem_length = mems['IMEM'][1], | 
|  | dmem_lma = mems['DMEM'][0], | 
|  | dmem_length = mems['DMEM'][1]) | 
|  | except OSError as err: | 
|  | raise RuntimeError(str(err)) from None | 
|  | except:  # noqa: 722 | 
|  | raise RuntimeError(exceptions.text_error_template().render()) from None | 
|  |  | 
|  | try: | 
|  | with open(out_path, 'w') as out_file: | 
|  | out_file.write(rendered) | 
|  | except FileNotFoundError: | 
|  | raise RuntimeError('Failed to open output file at {!r}.' | 
|  | .format(out_path)) from None | 
|  |  | 
|  |  | 
|  | @contextmanager | 
|  | def mk_linker_script() -> Iterator[str]: | 
|  | ld_in = os.path.abspath(os.path.join(os.path.dirname(__file__), | 
|  | '..', 'data', 'otbn.ld.tpl')) | 
|  | with tempfile.TemporaryDirectory(prefix='otbn-ld-') as tmpdir: | 
|  | ld_out = os.path.join(tmpdir, 'otbn.ld') | 
|  | try: | 
|  | interpolate_linker_script(ld_in, ld_out) | 
|  | except RuntimeError as err: | 
|  | sys.stderr.write('Failed to interpolate linker script: {}\n' | 
|  | .format(err)) | 
|  | return 1 | 
|  |  | 
|  | yield ld_out | 
|  |  | 
|  |  | 
|  | def run_ld(ld_script: Optional[str], args: List[str]) -> int: | 
|  | '''Run the underlying linker and return the status code''' | 
|  | ld_name = find_tool('ld') | 
|  | cmd = [ld_name] | 
|  | if ld_script is not None: | 
|  | cmd.append('--script={}'.format(ld_script)) | 
|  | cmd += args | 
|  |  | 
|  | try: | 
|  | return subprocess.run(cmd).returncode | 
|  | except FileNotFoundError: | 
|  | sys.stderr.write('Unknown command: {!r}. ' | 
|  | '(is it installed and on your PATH?)\n' | 
|  | .format(ld_name)) | 
|  | return 127 | 
|  |  | 
|  |  | 
|  | def main() -> int: | 
|  | # Only add the --script argument if the caller isn't supplying one | 
|  | # themselves. This argument accumulates (so -T foo -T bar is like | 
|  | # concatenating foo and bar), so we mustn't supply our own if the user | 
|  | # has one. | 
|  | needs_script = True | 
|  | for arg in sys.argv[1:]: | 
|  | if arg == '-T' or arg.startswith('--script='): | 
|  | needs_script = False | 
|  | break | 
|  |  | 
|  | if needs_script: | 
|  | with mk_linker_script() as script_path: | 
|  | return run_ld(script_path, sys.argv[1:]) | 
|  | else: | 
|  | return run_ld(None, sys.argv[1:]) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |