Add support for tbf_protected_region_size ELF-file symbol#70
Conversation
That's not right, primarily because elf2tab has to support both pic and non-pic elfs, so always setting the protected region size would generate undesirable pic tbfs. I ran |
Sorry, I should've been more clear: this is for the current state-of-practice for non-PIC apps only. We'd eliminate the need for passing this option with this PR, assuming You're right -- this is not needed for PIC apps, and in practice you can even get non-PIC apps to work, as the 256 byte Lines 476 to 491 in 9e1f707 However, this is brittle and broke for me in the past. To reliably generate working apps at the intended offsets for non-PIC platforms with the current(!) |
|
The underlying argument in this PR is that aligning the start of a tbf to a multiple of 256 bytes (our current practice) is insufficient. Why is that? |
libtock-c and libtock-rs are quite different, and we've (I've) worked extensively on supporting both PIC and non-PIC architectures, and creating TABs which are board independent for libtock-c. This is not the case for libtock-rs, so I think it is worth discussing why libtock-rs cannot just copy libtock-c's lead. libtock-c has never used the --protected-region-size flag: That flag was added to get support for non-pic apps up and working quickly in libtock-rs. We don't need to use 256 for the alignment. We could choose something based on the size of the app binary. Since we add a header in the TBF, once the tbf is generated we know exactly where it needs to be placed to make the app binary start at the correct address. So one question is: why is the app being loaded at the wrong location, and how does adding a start symbol help with that? |
This is a good question. In general, aligning "downward" onto 256 bytes should cover most cases. I don't know with certainty what went wrong in my case, but here's some cases where the above code would break (if I understand it correctly):
|
I think the core of this issue is that applications are linked with the intention of having them be loaded at a certain address. This address is specified in the linker script's The unintuitive / "tricky" part is that the an app must not actually be linked to this exact address. In the non-PIC case, the app must be linked to an address which leaves enough headroom for
One could see this change as a UX adjustment: instead of guessing an offset from within Here's how this could integrate with libtock-rs: tock/libtock-rs@1b69eb0 Notice how the protected size no longer needs to be kept in sync with the linker script. And this shows how it could be integrated into libtock-c: lschuermann/libtock-c@3bfee54 Developers would now actually specify the intended load address for non-PIC apps, and can optionally specify a custom TBF header size. Most importantly, PIC apps are entirely unaffected by these changes. |
But this is only true for libtock-rs. This is not true for libtock-c. For libtock-c, it is exactly as expected: we specify the address the app binary should be compiled for and the linker makes it so.
I think this might another assumption we are making differently. While yes libtock-c sort of has the expected nice round number where it implicitly expects apps to be placed, we don't explicitly have an intended load address. We link the binary several times with different fixed addresses in flash, let elf2tab add a reasonable header, and then expect the loader tool (in my case tockloader) to put the tbf in flash in a location where the kernel will run it.
I think the way forward is to actually forget about libtock-c and libtock-rs and instead think about what we want the interface to elf2tab to be. I generally don't like adding features to elf2tab to support non-pic apps which are not a good fit for the tock linked-list-of-apps model. I also would like to see libtock-rs implement full TAB support (as libtock-c has where one compilation supports [nearly] every board, with multiple apps) before using it as a model for designing this interface. One of the aspects that I do like about The drawback to all command line flags in elf2tab is that they apply to all elfs, How a userland compilation framework generates these symbols is up to it (and outside the scope of this PR).
We should fix this. |
Ah! I mean, this is almost exactly what this change does (it just sets an absolute address instead, and calls it |
We would need to establish what happens if both are set. Probably the elf overrides the command line. |
I would've vouched for the other way around. My guess is that build infrastructures will adopt this option (at least But I don't have strong feelings either way. If you'd prefer the ELF symbol to take precedence, I'd be fine with that too. |
73f8c64 to
65cfa98
Compare
tbf_header symboltbf_protected_region_size ELF-file symbol
|
I have updated the code, commit message and pull request description according to this discussion. I'm happy to change the ELF-symbol to take precedence over the command line, if desired. Tested this to work with |
My thinking being that if an elf goes to the trouble of specifying the protected size then it's probably important to use that. A user might want to set a default for all other TBFs via the command line. |
bradjc
left a comment
There was a problem hiding this comment.
Looks good. Need to update the readme.
I think we shouldn't override the elf setting with the command line. It might not make sense to set all tbfs to the same protected size, but it might be useful to be able to set a default.
65cfa98 to
a8bf3c8
Compare
|
Updated the readme and the ELF symbol / command line parameter precedence. I do now realize why it would make sense for the symbol to take precedence: I missed the fact that elf2tab can consider multiple ELFs simultaneously and pack them into a TAB archive. I agree with the argument that it makes sense to add different protected region sizes for different TBFs in a single TAB. |
This change causes elf2tab determine the TBF protected region size
from a special `tbf_protected_region_size` symbol in the ELF file. If
this symbol is not present, elf2tab falls back to the checking the
`--protected-region-size` command line parameter, and finally onto its
heuristics for determining a reasonable protected region size.
Motivation
----------
For non-PIC apps, it is very inconvenient to need to manually
calculate the apps' actual text-section start address, and then choose
a protected region parameter on elf2tab's command line which matches
the pre-calculated flash address such that the TBF is filled up with
headers and padding to match the expected app load address. While many
times simply specifying an offset in the link address and allowing
elf2tab to fall back onto a reasonable well-aligned address, this
mechanism can break under some configurations. The following outlines
how a PIC and non-PIC target is defined in `libtock-c`:
```
TOCK_TARGETS ?= cortex-m0\
[...]
rv32imac|rv32imac.0x20040060.0x80002800|0x20040060|0x80002800\
```
(Here, the intended load address for the application is
`0x20040000`. To generate correct binaries, `0x60` is added as an
offset to the linker-provided flash address. Deverlopers either rely
on elf2tab automatically falling back onto the next-lower 256-byte
aligned address, or must pass `--protected-region-size 0x60` to
elf2tab.)
`libtock-rs` currently takes a different approach[1]: it reserves a
section for the TBF headers at the start of its flash region. However,
this does not seem to work reliably. For instance, the linker is free
to re-arrange segments in the ELF file, and e.g., have the RAM segment
located before the flash segment. While TBF is compatible with
that (by setting the `init_fn_offset` field properly), having the TBF
header reservation at the front of the `FLASH` segment (which is not
the first segment in the ELF file) would break the assumption that the
TBF header precedes the actual process binary. Furthermore, the
current ELF parsing logic inteprets this `.tbf_header` section as the
start of application flash and prepends TBF headers before that
section.
By introducing support for a `tbf_protected_region_size` symbol (which
can be set to an arbitrary value outside of any ELF segment),
applications can place their `FLASH` segment at an appropriate offset
and hint this offset to `elf2tab`, without placing any constraints on
the ELF segment layout itself.
[1]: https://github.com/tock/libtock-rs/blob/0f7c97627b7d49dd34129d40717eadba9d307a2d/runtime/libtock_layout.ld#L50-L54
a8bf3c8 to
3885ecd
Compare
bradjc
left a comment
There was a problem hiding this comment.
This works with the current libtock-c, correct?
|
Tested that Produced TBFs are bit-by-bit identical with these changes, assuming the symbol not set (the below example is for |
|
Ok great let's merge this. We can always change it before a release if needed. |
476: Use `tbf_protected_region_size` to indicate intended load address r=lschuermann a=lschuermann This removes the special `.tbf_header` section previously created by `libtock-rs`, in favor of setting the `tbf_protected_region_size` symbol and shifting the application's FLASH section by `TBF_HEADER_SIZE`. While adding a `.tbf_header` section is a convenient way to reserve space for elf2tab to insert a TBF header, it causes issues with recent versions of elf2tab and certain Rust toolchains: - depending on factors such as the Rust toolchain version and the precise FLASH address, the linker may choose to place the RAM section before the FLASH section in the generated ELF file. This is perfectly legal for the linker to do: the intended load address for those sections are still maintained and included in the appropriate section headers. Furthermore, internal references in the ELF file (such as the addresses in the `rt_header`) will rely on this ordering of sections -- meaning that elf2tab is not allowed to rearrange them. However, when including a `.tbf_header` section at the start of the FLASH section, this may then not actually be located at the start of the ELF binary itself. - it seems that elf2tab no longer considers the `.tbf_header` region to not be included in the actual TBF binary (I assume this wasn't always the case, otherwise this would have never worked?). Instead, the `.tbf_header` section is included and loaded in the final TBF, and as a result the application is offset by an incorrect amount (twice the `TBF_HEADER_SIZE`) I'm not sure whether this issue is actually an elf2tab bug in that it seemingly considers `NOBITS` sections for loading, but those sections are also included in the ELF file, so I assume that elf2tab works correctly here. This change tries to retain the previous semantics (developers only specify the intended load address in their platform linker scripts), while using a more reliable mechanism for elf2tab to prefix the generated binary with TBF headers and a protected region that, when prepended, aligns to this intended load address. Most importantly, we exclude the TBF header from any of the app's MEMORY sections, giving the linker free reign over their layout. A special symbol, `tbf_protected_region_size`, communicates to elf2tab the intended protected region size, which matches the FLASH-section's ORIGIN of `$INTENDED_LOAD_ADDRESS + TBF_HEADER`. In essence, this adjusts `libtock-rs` to behave like `libtock-c` (where the TBF header is also not reserved in the binary itself), while retaining `libtock-rs`' more elegant semantics. With this change, as long as elf2tab can reliably detect the app's fixed flash address, RAM address and `.start` symbol offset, the ELF's particular layout should now be irrelevant for elf2tab. Relevant elf2tab PR: tock/elf2tab#70 Co-authored-by: Leon Schuermann <leons@opentitan.org>
Pull Request Overview
This change causes elf2tab determine the TBF protected region size from a special
tbf_protected_region_sizesymbol in the ELF file. This can be overriden through the--protected-region-sizecommand line parameter. If neither the parameter nor the symbol are present, elf2tab falls back onto its heuristics for determining a reasonable protected region size.Motivation
For non-PIC apps, it is very inconvenient to need to manually calculate the apps' actual text-section start address, and then choose a protected region parameter on elf2tab's command line which matches the pre-calculated flash address such that the TBF is filled up with headers and padding to match the expected app load address. While many times simply specifying an offset in the link address and allowing elf2tab to fall back onto a reasonable well-aligned address, this mechanism can break under some configurations. The following outlines how a PIC and non-PIC target is defined in
libtock-c:(Here, the intended load address for the application is
0x20040000. To generate correct binaries,0x60is added as an offset to the linker-provided flash address. Deverlopers either rely on elf2tab automatically falling back onto the next-lower 256-byte aligned address, or must pass--protected-region-size 0x60to elf2tab.)libtock-rscurrently takes a different approach1: it reserves a section for the TBF headers at the start of its flash region. However, this does not seem to work reliably. For instance, the linker is free to re-arrange segments in the ELF file, and e.g., have the RAM segment located before the flash segment. While TBF is compatible with that (by setting theinit_fn_offsetfield properly), having the TBF header reservation at the front of theFLASHsegment (which is not the first segment in the ELF file) would break the assumption that the TBF header precedes the actual process binary. Furthermore, the current ELF parsing logic interprets this.tbf_headersection as the start of application flash and prepends TBF headers before that section.By introducing support for a
tbf_protected_region_sizesymbol (which can be set to an arbitrary value outside of any ELF segment), applications can place theirFLASHsegment at an appropriate offset and hint this offset toelf2tab, without placing any constraints on the ELF segment layout itself.Testing Strategy
These changes are tested with a preliminary integration into the linker-scripts of the
libtock-candlibtock-rstoolchains.