blob: a19481e905b7e3e48ed68006d26a4a65ec74da02 [file] [log] [blame]
#!/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.'''
import os
import subprocess
import sys
import tempfile
from contextlib import contextmanager
from typing import Iterator, List, Optional
from mako import exceptions # type: ignore
from mako.template import Template # 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_address,
imem_length=mems.imem_size_bytes,
dmem_lma=mems.dmem_address,
dmem_length=mems.dmem_size_bytes,
dmem_bus_length=mems.dmem_bus_size_bytes)
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')
# The --no-check-sections argument tells ld not to complain when we have
# more than one section with the same VMA. Since we have a Harvard
# architecture where data and instructions both start at zero, we expect
# that to happen.
cmd = [ld_name, '--no-check-sections']
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(argv: List[str]) -> 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 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, argv[1:])
else:
return run_ld(None, argv[1:])
if __name__ == '__main__':
sys.exit(main(sys.argv))