[RFC][PATCH] Binutils changes for RISC-V overlay system
Ed Jones
ed.jones@embecosm.com
Mon Apr 25 09:42:00 GMT 2022
Hi all,
Some colleagues (Craig Blackmore and Simon Cook @ Embecosm) and I have
been working on changes to implement the proposed overlay system for
RISC-V (https://github.com/riscv-software-src/riscv-overlay/). We're
at a stage where it would be useful to get community feedback.
Below is a high level description of the system and how it's
implemented. A patch is attached which implements the binutils
changes in its entirety.
## Introduction
The RISC-V overlay system allows functions and data to exist in
one or more overlay groups which are swapped in and out of active
memory on demand by the runtime engine. The proposed changes here
cover the binutils changes necessary to link objects which use
this system.
A high level description for the overlay can be found here:
- [overlay-hld.adoc on
GitHub](https://github.com/riscv-software-src/riscv-overlay/blob/master/docs/overlay-hld.adoc)
There are also accompanying changes for Clang/LLVM:
- [LLVM patch on reviews.llvm.org](https://reviews.llvm.org/D109371)
- [Clang patch on reviews.llvm.org](https://reviews.llvm.org/D109372)
The goal of this RFC is to get feedback on the design and current
binutils changes with the aim of eventually merging the changes
with the main source tree.
## Glossary
- resident memory
The data and code which is currently loaded and in active
memory. Includes both memory not managed by the overlay runtime
which is always 'resident' as well as the memory of the overlay
group(s) which are currently loaded in.
- overlay runtime engine
Runtime routines linked into the final executable. Tracks the
currently loaded overlay group(s) and handles the swapping of
groups into resident memory as appropriate.
- token
A value which represents where a symbol can be found by the
overlay runtime engine. Used in place of an address when
making a call or otherwise referencing an overlay symbol.
- overlay PLT entry
A stub function representing an overlay symbol. The stub
functions materializes the token for the symbol and then
calls into the overlay runtime engine. These entries are
generated when the address of a overlay function is taken
and used out of its original context - for example when it
is stored in a function pointer.
- grouping file
A `.csv` file containing a one-to-many mapping of overlay
symbol names to the number of each group which they should
exist in.
- grouping tool
An optional user-provided executable that can be used to
generate a grouping file at link time.
- grouping table
A table generated by the linker and consumed by the overlay
runtime engine. There is an entry for every group which
gives the last page number for that group. By multiplying
this by the page size the runtime engine can work out
where the start and end of a given group is in the overlay
memory and load it.
- multigroup table
Holds a list for each symbol which exists in multiple groups.
The list contains token values for each group that the
given symbol exists in. The token value representing the
symbol for relocations will then reference the multigroup
table instead of directly encoding the group and offset.
- `.ovlgrps`
The single monolithic output section which contains both
the grouping table and multigroup table as well as the
contents of all of the overlay groups.
## The RISC-V Overlay System
Most of the details about the overlay system can be found in the
High Level Document, however below is a brief summary leading in
to the binutils changes.
There are two primary ways a user interacts with the overlay
system.
1. By marking a function declaration with a
`__attribute__((overlay))` attribute to signify that the
function will be handled by the overlay system.
2. By providing a list of groups which each 'overlay' function
exists in.
The role of the compiler and linker are to take the above
user inputs and produce an executable that can be managed by the
overlay runtime engine also linked into that executable. In more
detail the roles of the compiler and linker are as follows:
Compiler:
1. Place each symbol marked as `__attribute__((overlay))` in
its own section identifiable by the linker. Each symbol ends
up in a section named `.ovlinput.<symbol>`.
2. Reserve registers used exclusively by the overlay runtime
engine.
3. Update calls to `__attribute__((overlay))` functions so that
they are redirected through the overlay runtime engine.
Linker:
1. Read in the group assignments for each symbol from a
user-provided grouping file, or assign groups automatically.
2. Build the grouping table which maps from a group to the
page immediate after the end of that group.
3. Build the multigroup table which holds a one-to-many mapping
from a symbol to a list of tokens for the multiple groups it
exists in.
4. Assign all 'overlay' symbols into a special output section
called `.ovlgrps`. Create duplicate input sections for symbols
assigned to multiple groups.
5. Sort all of the 'overlay' symbol input sections within the
`.ovlgrps` output section by their group number, with a
special padding section between each group.
6. Fill in overlay relocations
7. Build overlay 'PLT' entries to handle indirect calls.
## Implementation details
All functions marked by the 'overlay' attribute eventually end
up in a single monolithic output section called `.ovlgrps`.
This section contains both the loadable contents of each overlay
group as well as the metadata tables consumed by the runtime
engine to locate and load groups on demand and call overlay
functions (the metadata table is also consumed by GDB when
debugging overlay executables).
The `.ovlgrps` output section is itself built from the
user-provided sections with the prefix `.ovlinput.`, and a
number of internal sections generated by the linker with the
prefix `.ovlinput.__internal.`. These linker-generated sections
are as follows:
- `.ovlinput.__internal.grouptables`
Section containing the contents of the grouping table and the
multigroup table. This section occupies the area allocated
for the first group (group 0) `.ovlgrps`.
- `.ovlinput.__internal.padding.<group>`
A special padding section which is placed after the last
`.ovlinput.<symbol>` section assigned to a given group. This
pads the group size up to the next `OVL_GROUP_PAGE_SIZE`.
This section is filled with the 16-bit group number as
padding and the final 32-bits contains a CRC of the
entire group contents.
- `.ovlinput.__internal.duplicate.<group>.<symbol>`
When a symbol exists in multiple overlay groups the contents
need to be duplicated into each output group. The
duplicate sections fulfill this role. Note that the
original symbol and its debug information remains associated
with the first group it is assigned to.
In addition to the above input sections there is also a linker
generated `.ovlplt` section, and unlike the other linker
generated sections this section is always held in resident
memory. The `.ovlplt` section is used to hold stub functions which
materialize an overlay token value and jump into the overlay
engine. The address of the stub function can then be used in a
normal indirect call sequence. The `.ovlplt` entries are
generated for symbols referred to by the `R_RISCV_OVLPLT_LO12_I`,
`R_RISCV_OVLPLT_HI20` and `R_RISCV_OVLPLT32` relocations.
When the linker maps the `.ovlinput` groups into `.ovlgrps`,
it does a custom sort which lays them out in the correct order.
Pseudo-code for this layout is as follows:
```
Layout .ovlinput.__internal.grouptables
Layout .ovlinput.__internal.padding.0
for each group in 1 to ${max_group}
for each symbol in ${group}
if .ovlinput.${symbols} exists
Layout .ovlinput.${symbol}
else if .ovlinput.__internal.duplicate.${group}.${symbol} exists
Layout .ovlinput.__internal.duplicate.${group}.${symbol}
Layout .ovlinput.__internal.padding.${group}
```
Essentially all of the sections which make up the contents of a
given overlay group are then followed by the padding section which
contains the padding, crc, and rounds the size up to the next
page boundary.
The group and offset of each `.ovlinput.<symbol>` section is
tracked, and this is used to populate the grouping table in
`.ovlinput.__internal.grouptables` and also when materializing the
token values to fill in `R_RISCV_OVLTOK_LO12_I`,
`R_RISCV_OVLTOK_HI20` and `R_RISCV_OVLTOK32` relocations.
Symbols which are assigned to multiple groups also generate
entries in the multigroup table in `.ovlinput.__internal.grouptables`,
and affect the token values which are materialized - the tokens
refer into the multigroup table instead of the grouping table.
To achieve all of the above, the entry points below are called
by the linker in approximately their listed order:
### riscv_elf_overlay_check_relocation
- Marks symbols as requiring a group assignment if they
are referred to by any overlay relocations.
- Creates and allocates space in the `.ovlplt` section for any
symbols referenced by overlay PLT relocations.
### riscv_elf_overlay_generate_tables
- Initializes internal data structures to track group
assignments.
- Populates data structures with symbols that are pending
a group assignment.
- Assigns groups using an external tool, a provided
`.csv` grouping file, or automatically.
### riscv_elf_overlay_create_ovlgrps
- Creates the `.ovlinput.__internal.grouptables` section.
### riscv_elf_overlay_duplicate_multigroup_sections
- Creates `.ovlinput.__internal.duplicate.<group>.<symbol>`
sections for any symbol assigned to multiple groups.
### riscv_elf_overlay_pad_groups
- Creates the padding section for each group.
### riscv_elf_overlay_sort_value
- Correctly lays out all of the user-provided and linker-generated
input sections assigned to `.ovlgrps`
### riscv_overlay_relax_delete_bytes
- Resizes the padding section for a group when any of the
input sections are resized due to relaxation. Ensures
the group ends on an overlay page boundary.
- Resizes any overlay duplicate sections to match the size
of the original.
### riscv_overlay_handle_relocation
- Materializes token values and uses them to fill in overlay
relocations.
### riscv_overlay_finish_sections
- Fills in the `.ovlplt` entries and the grouping table and
multigroup table in `.ovlinput.__internal.grouptables`.
- Duplicates the contents for multigroup symbols in `.ovlgrps`
to all of their assigned groups.
- Emits the padding and crc at the end of each group.
## Open issues
This is a (probably incomplete) list of issues and questions
which need to be resolved.
- It has not yet been settled about how to avoid overlay and
non-overlay code from being linked. This is important as the
overlay system requires user code to avoid using a number of
registers. We have a work in progress patch to add a new
attribute tag `TAG_reserved_regs` which could achieve this.
Alternatively it could be achieved through a new ABI variant.
- Currently the linker changes work by creating a bunch of input
sections and then using a custom sort to arrange them
appropriately in the output section along with special padding
sections. Is there a simpler design to acheive this?
- There's a significant amount of code added to the riscv
emulation file to parse new options, and there are also some
changes to generic code (addition of `by_overlay` sorting)
which are clumsy. Can these changes be isolated or made more
generic?
- Currently the changes use custom relocation numbers. Final
relocation numbers are awaiting allocation.
- Given the scale of the changes, how can this be maintained
and bitrot avoided?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: binutils.patch
Type: text/x-patch
Size: 209793 bytes
Desc: not available
URL: <https://sourceware.org/pipermail/binutils/attachments/20220425/2a83a74a/attachment-0001.bin>
More information about the Binutils
mailing list