public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [RFC][PATCH] Binutils changes for RISC-V overlay system
@ 2022-04-25  9:42 Ed Jones
  0 siblings, 0 replies; only message in thread
From: Ed Jones @ 2022-04-25  9:42 UTC (permalink / raw)
  To: Binutils; +Cc: Craig Blackmore, Simon Cook, Ed Jones, Ofer Shinaar

[-- Attachment #1: Type: text/plain, Size: 11749 bytes --]

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?

[-- Attachment #2: binutils.patch --]
[-- Type: text/x-patch, Size: 209793 bytes --]

From 864a29346d50b293d1526f60a6b72392c14119fe Mon Sep 17 00:00:00 2001
From: Edward Jones <ed.jones@embecosm.com>
Date: Tue, 22 Feb 2022 12:20:59 +0000
Subject: [PATCH] Add support for RISC-V overlay system

This adds changes to allow objects which use the RISC-V
overlay system to be linked together. It manages the creation
and layout of overlay sections for use by the overlay runtime,
and handles overlay specific relocations.

bfd/

	* Makefile.am: Add elfxx-riscv-overlay.c and
	elfxx-riscv-overlay.h to files included in the
	build.
	* Makefile.in: Regenerated.
	* bfd-in2.h: Likewise.
	* bfd.c (bfd_get_section_overlay_sort_data): Define.
	* binary.c (binary_bfd_get_section_overlay_sort_data):
	Likewise.
	* configure: Regenerated.
	* configure.ac: Add elfxx-riscv-overlay.lo to riscv
	backend .o files.
	* elfnn-riscv.c (struct riscv_elf_link_hash_entry):
	(struct riscv_elf_link_hash_table)
	(riscv_elf_hash_entry riscv_elf_hash_table is_riscv_elf)
	(sec_addr): Move to elfxx-riscv.h.
	(link_hash_newfunc): Initialize overlay members of
	struct riscv_elf_link_hash_entry.
	(riscv_elf_link_hash_table_create): Call out to
	riscv_elf_overlay_link_hash_table_init to initialization
	overlay data structure.
	(riscv_elf_check_relocs): Add handling of overlay
	relocations, call out to riscv_elf_overlay_check_relocation.
	Update handling of R_RISCV_CALL. Mark symbols referred
	through non-overlay relocations and error for both
	overlay and non-overlay relocations to the same symbol.
	(riscv_elf_check_sections): New function.
	(riscv_elf_gc_mark_hook): Call out to
	riscv_overlay_set_use_gcmark.
	(perform_relocation): Add handling of overlay relocations.
	(riscv_elf_relocate_section): Add handling of new overlay
	relocations. Call out to riscv_overlay_handle_relocation.
	(riscv_elf_finish_dynamic_sections): Call out to
	riscv_overlay_finish_sections.
	(riscv_relax_delete_bytes): Add call out to
	riscv_overlay_relax_delete_bytes.
	(riscv_elf_overlay_hook_elfNNlriscv)
	(riscv_elf_overlay_hook_elfNNbriscv): New functions.
	(bfd_elfNN_bfd_get_section_overlay_sort_data): New define.
	(elf_backend_check_directives): Likewise.
	* elfxx-riscv-overlay.c: New file.
	* elfxx-riscv-overlay.h: Likewise.
	* elfxx-riscv.c (howto_table): Add HOWTO entries for
	R_RISCV_OVLTOK_HI20, R_RISCV_OVLTOK_LO12_I,
	R_RISCV_OVLTOK32, R_RISCV_OVLPLT_HI20,
	R_RISCV_OVLPLT_LO12_I, and R_RISCV_OVLPLT32.
	(riscv_reloc_map): Add mappings for overlay
	relocations.
	(riscv_reloc_type_lookup): Add workaround to
	correctly map from overlay relocation numbers to
	howto table entries.
	(riscv_elf_rtype_to_howto): Correctly map relocation
	types to howto entries.
	* elfxx-riscv.h (struct riscv_elf_link_hash_entry):
	(struct riscv_elf_link_hash_table)
	(riscv_elf_hash_entry riscv_elf_hash_table is_riscv_elf)
	(sec_addr): Move definitions from elfnn-riscv.c.
	* elfxx-target.h (bfd_elfNN_bfd_get_section_overlay_sort_data):
	Add define.
	* ihex.c (ihex_bfd_get_section_overlay_sort_data): Add define.
	* libbfd-in.h (_bfd_nolink_bfd_get_section_overlay_sort_data):
	Add define.
	* libbfd.h: Regenerated.
	* plugin.c (bfd_plugin_bfd_get_section_overlay_sort_data):
	Add define.
	* reloc.c: Add ENUMX entries for BFD_RELOC_RISCV_OVLTOK_HI20,
	BFD_RELOC_RISCV_OVLTOK_LO12_I, BFD_RELOC_RISCV_OVLTOK32,
	BFD_RELOC_RISCV_OVLPLT_HI20, BFD_RELOC_RISCV_OVLPLT_LO12_I,
	and BFD_RELOC_RISCV_OVLPLT32.
	* section.c (bfd_generic_get_section_overlay_sort_data):
	Add function.
	* srec.c (srec_bfd_get_section_overlay_sort_data): Add define.
	* target.c (BFD_JUMP_TABLE_LINK): Add
	_bfd_get_section_overlay_sort_data entry.
	(bfd_copy_link_hash_symbol_type): Likewise.
	* tekhex.c (tekhex_bfd_get_section_overlay_sort_data):
	Add define.

gas/

	* config/tc-riscv.c (percent_op_utype):
	Add mappings for BFD_RELOC_RISCV_OVLTOK_HI20
	and BFD_RELOC_RISCV_OVLPLT_HI20 relocs.
	(percent_op_itype):
	Add mappings for BFD_RELOC_RISCV_OVLTOK_LO12_I
	and BFD_RELOC_RISCV_OVLPLT_LO12_I relocs.
	(md_apply_fix): Add handling of overlay
	relocations.

include/

	* elf/riscv.h: Add R_RISCV_OVLTOK_HI20,
	R_RISCV_OVLTOK_LO12_I, R_RISCV_OVLTOK32,
	R_RISCV_OVLPLT_HI20, R_RISCV_OVLPLT_LO12_I,
	R_RISCV_OVLPLT32 for reloc numbers from 220
	to 225.
	* opcode/riscv.h (X_ZERO X_T5 X_T6): Add
	defines.

ld/

	* emultempl/emulation.em (LDEMUL_EXTRA_EARLY_MAP_FILE_TEST):
	Default to NULL.
	* emultempl/riscvelf.em (riscv_grouping_file): Forward declare.
	(DEFAULT_CRC_INIT DEFAULT_CRC_POLY DEFAULT_CRC_XOROUT)
	(DEFAULT_CRC_REFIN DEFAULT_CRC_REFOUT): New defines.
	(elf_riscv_before_parse): New function.
	(riscv_elf_before_allocation): Add error for .ovlinput
	prefixed input sections not assigned to .ovlgrps output
	section.
	(riscv_elf_after_check_relocs): New function.
	(get_type_from_name_and_flags): Likewise.
	(riscv_ovl_additional_link_map_text): Likewise.
	(PARSE_AND_LIST_PROLOGUE): Set to add various
	defines to the standard elf argument parsing.
	(PARSE_AND_LIST_LONGOPTS): Add various overlay options
	to standard option list.
	(PARSE_AND_LIST_OPTIONS): Add various overlay short
	options to standard elf option list.
	(PARSE_AND_LIST_ARGS_CASES): Set to add cases to
	standard elf argument parsing.
	(LDEMUL_BEFORE_PARSE): Define to elf_riscv_before_parse.
	(LDEMUL_AFTER_CHECK_RELOCS): Define to
	riscv_elf_after_check_relocs.
	(LDEMUL_EXTRA_EARLY_MAP_FILE_TEXT):
	Define to =riscv_ovl_additional_link_map_text
	* ld.h (sort_type): Add by_overlay sort type.
	* ldemul.c (ldemul_extra_early_map_file_text): Add
	new entry point.
	* ldemul.h (ldemul_extra_early_map_file_text): Add
	forward declaration.
	(struct ld_emulation_xfer_struct): Add
	extra_early_map_file_text member.
	* ldlang.c (compare_section): Add handling of
	by_overlay sort type.
	(get_lang_memory_region_list): Add function.
	(lang_map): Call ldemul_extra_early_map_text
	at start of map file printing.
	(set_overlay_sort_default): Add function.
	(update_wild_statements): Force unsorted sections
	assigned to the .ovlgrps output section to use
	by_overlay sorting.
	(print_wild_statement): Handle by_overlay sorting type.
	(size_input_section): Force alignment of overlay
	padding sections.
	* ldlang.h (get_lang_memory_region_list): Add forward
	declaration.
	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Add
	run_dump_test calls for overlay tests.
	(riscv_get_testdir): Add procedure.
	* testsuite/ld-riscv-elf/overlay-assign-dead.csv:
	Added file.
	* testsuite/ld-riscv-elf/overlay-assing-dead.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-assign-group0.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-assign-group0.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-assign-missing.csv:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-assign-missing.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-assign-non-contiguous.csv:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-assign-non-contiguous.d:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-autoassign-01.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-autoassign-02.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-autoassign-dead.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-big-funcs.s: Likewise.
	* testsuite/ld-riscv-elf/overlay-dead-symbol.s: Likewise.
	* testsuite/ld-riscv-elf/overlay-invalid-group-number.csv:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-invalid-group-number.d:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-many-func-group.csv:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-many-func-group.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-many-funcs.s: Likewise.
	* testsuite/ld-riscv-elf/overlay-max-group-number.csv:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-max-group-number.d:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-max-size-group.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-max-size-group.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-multigroup-01.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-multigroup-01.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-multigroup-02.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-multigroup-02.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-no-grouping-file.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-partial-assign.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-partial-assign.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-plt.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-plt.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-plt.s: Likewise.
	* testsuite/ld-riscv-elf/overlay-relax-01.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-relax-02.csv: Likewise.
	* testsuite/ld-riscv-elf/overlay-relax-02.d: Likewise.
	* testsuite/ld-riscv-elf/overlay-relax-multigroup.csv:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-relax-multigroup.d:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-relax.s: Likewise.
	* testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.d:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.s:
	Likewise.
	* testsuite/ld-riscv-elf/overlay-trivial.s: Likewise.
	* testsuite/ld-riscv-elf/overlay.ld: Likewise.
---
 bfd/ChangeLog                                 |   79 +
 bfd/Makefile.am                               |    5 +-
 bfd/Makefile.in                               |    6 +-
 bfd/bfd-in2.h                                 |   19 +-
 bfd/bfd.c                                     |    3 +
 bfd/binary.c                                  |    1 +
 bfd/configure                                 |    8 +-
 bfd/configure.ac                              |    8 +-
 bfd/elfnn-riscv.c                             |  204 +-
 bfd/elfxx-riscv-overlay.c                     | 2563 +++++++++++++++++
 bfd/elfxx-riscv-overlay.h                     |  205 ++
 bfd/elfxx-riscv.c                             |  129 +-
 bfd/elfxx-riscv.h                             |   66 +
 bfd/elfxx-target.h                            |    4 +
 bfd/ihex.c                                    |    1 +
 bfd/libbfd-in.h                               |    2 +
 bfd/libbfd.h                                  |    8 +
 bfd/plugin.c                                  |    1 +
 bfd/reloc.c                                   |   12 +
 bfd/section.c                                 |   19 +
 bfd/srec.c                                    |    1 +
 bfd/targets.c                                 |    7 +-
 bfd/tekhex.c                                  |    1 +
 gas/ChangeLog                                 |   11 +
 gas/config/tc-riscv.c                         |   12 +
 include/ChangeLog                             |   10 +
 include/elf/riscv.h                           |    6 +
 include/opcode/riscv.h                        |    3 +
 ld/ChangeLog                                  |  107 +
 ld/emultempl/emulation.em                     |    1 +
 ld/emultempl/riscvelf.em                      |  333 +++
 ld/ld.h                                       |    2 +-
 ld/ldemul.c                                   |    8 +
 ld/ldemul.h                                   |    8 +
 ld/ldlang.c                                   |   48 +
 ld/ldlang.h                                   |    2 +
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    |   27 +
 .../ld-riscv-elf/overlay-assign-dead.csv      |    1 +
 .../ld-riscv-elf/overlay-assign-dead.d        |   12 +
 .../ld-riscv-elf/overlay-assign-group0.csv    |    1 +
 .../ld-riscv-elf/overlay-assign-group0.d      |    4 +
 .../ld-riscv-elf/overlay-assign-missing.csv   |    1 +
 .../ld-riscv-elf/overlay-assign-missing.d     |   31 +
 .../overlay-assign-non-contiguous.csv         |    6 +
 .../overlay-assign-non-contiguous.d           |   89 +
 .../ld-riscv-elf/overlay-autoassign-01.d      |   31 +
 .../ld-riscv-elf/overlay-autoassign-02.d      |   85 +
 .../ld-riscv-elf/overlay-autoassign-dead.d    |   12 +
 ld/testsuite/ld-riscv-elf/overlay-big-funcs.s |   64 +
 .../ld-riscv-elf/overlay-dead-symbol.s        |   12 +
 .../overlay-invalid-group-number.csv          |    1 +
 .../overlay-invalid-group-number.d            |    4 +
 .../ld-riscv-elf/overlay-many-func-group.csv  |    6 +
 .../ld-riscv-elf/overlay-many-func-group.d    |   68 +
 .../ld-riscv-elf/overlay-many-funcs.s         |   58 +
 .../ld-riscv-elf/overlay-max-group-number.csv |    1 +
 .../ld-riscv-elf/overlay-max-group-number.d   |   33 +
 .../ld-riscv-elf/overlay-max-size-group.csv   |    6 +
 .../ld-riscv-elf/overlay-max-size-group.d     |    4 +
 .../ld-riscv-elf/overlay-multigroup-01.csv    |    1 +
 .../ld-riscv-elf/overlay-multigroup-01.d      |  114 +
 .../ld-riscv-elf/overlay-multigroup-02.csv    |    6 +
 .../ld-riscv-elf/overlay-multigroup-02.d      |  154 +
 .../ld-riscv-elf/overlay-no-grouping-file.d   |    4 +
 .../ld-riscv-elf/overlay-partial-assign.csv   |    3 +
 .../ld-riscv-elf/overlay-partial-assign.d     |   85 +
 ld/testsuite/ld-riscv-elf/overlay-plt.csv     |    4 +
 ld/testsuite/ld-riscv-elf/overlay-plt.d       |   72 +
 ld/testsuite/ld-riscv-elf/overlay-plt.s       |   40 +
 ld/testsuite/ld-riscv-elf/overlay-relax-01.d  |   19 +
 .../ld-riscv-elf/overlay-relax-02.csv         |    3 +
 ld/testsuite/ld-riscv-elf/overlay-relax-02.d  |   19 +
 .../ld-riscv-elf/overlay-relax-multigroup.csv |    3 +
 .../ld-riscv-elf/overlay-relax-multigroup.d   |   19 +
 ld/testsuite/ld-riscv-elf/overlay-relax.s     |   55 +
 .../overlay-symbol-not-in-ovlinput.d          |    4 +
 .../overlay-symbol-not-in-ovlinput.s          |   10 +
 ld/testsuite/ld-riscv-elf/overlay-trivial.s   |   12 +
 ld/testsuite/ld-riscv-elf/overlay.ld          |   17 +
 79 files changed, 5027 insertions(+), 77 deletions(-)
 create mode 100644 bfd/elfxx-riscv-overlay.c
 create mode 100644 bfd/elfxx-riscv-overlay.h
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-dead.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-dead.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-group0.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-group0.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-missing.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-missing.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-autoassign-01.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-autoassign-02.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-autoassign-dead.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-big-funcs.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-dead-symbol.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-many-func-group.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-many-func-group.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-many-funcs.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-max-group-number.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-max-group-number.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-max-size-group.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-max-size-group.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-multigroup-01.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-multigroup-01.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-multigroup-02.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-multigroup-02.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-no-grouping-file.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-partial-assign.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-partial-assign.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-plt.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-plt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-plt.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-relax-01.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-relax-02.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-relax-02.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.csv
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-relax.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.d
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay-trivial.s
 create mode 100644 ld/testsuite/ld-riscv-elf/overlay.ld

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index d64a705abf6..a9e6546f6df 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,82 @@
+2022-02-28  Edward Jones  <ed.jones@embecosm.com>
+
+	* Makefile.am: Add elfxx-riscv-overlay.c and
+	elfxx-riscv-overlay.h to files included in the
+	build.
+	* Makefile.in: Regenerated.
+	* bfd-in2.h: Likewise.
+	* bfd.c (bfd_get_section_overlay_sort_data): Define.
+	* binary.c (binary_bfd_get_section_overlay_sort_data):
+	Likewise.
+	* configure: Regenerated.
+	* configure.ac: Add elfxx-riscv-overlay.lo to riscv
+	backend .o files.
+	* elfnn-riscv.c (struct riscv_elf_link_hash_entry):
+	(struct riscv_elf_link_hash_table)
+	(riscv_elf_hash_entry riscv_elf_hash_table is_riscv_elf)
+	(sec_addr): Move to elfxx-riscv.h.
+	(link_hash_newfunc): Initialize overlay members of
+	struct riscv_elf_link_hash_entry.
+	(riscv_elf_link_hash_table_create): Call out to
+	riscv_elf_overlay_link_hash_table_init to initialization
+	overlay data structure.
+	(riscv_elf_check_relocs): Add handling of overlay
+	relocations, call out to riscv_elf_overlay_check_relocation.
+	Update handling of R_RISCV_CALL. Mark symbols referred
+	through non-overlay relocations and error for both
+	overlay and non-overlay relocations to the same symbol.
+	(riscv_elf_check_sections): New function.
+	(riscv_elf_gc_mark_hook): Call out to
+	riscv_overlay_set_use_gcmark.
+	(perform_relocation): Add handling of overlay relocations.
+	(riscv_elf_relocate_section): Add handling of new overlay
+	relocations. Call out to riscv_overlay_handle_relocation.
+	(riscv_elf_finish_dynamic_sections): Call out to
+	riscv_overlay_finish_sections.
+	(riscv_relax_delete_bytes): Add call out to
+	riscv_overlay_relax_delete_bytes.
+	(riscv_elf_overlay_hook_elfNNlriscv)
+	(riscv_elf_overlay_hook_elfNNbriscv): New functions.
+	(bfd_elfNN_bfd_get_section_overlay_sort_data): New define.
+	(elf_backend_check_directives): Likewise.
+	* elfxx-riscv-overlay.c: New file.
+	* elfxx-riscv-overlay.h: Likewise.
+	* elfxx-riscv.c (howto_table): Add HOWTO entries for
+	R_RISCV_OVLTOK_HI20, R_RISCV_OVLTOK_LO12_I,
+	R_RISCV_OVLTOK32, R_RISCV_OVLPLT_HI20,
+	R_RISCV_OVLPLT_LO12_I, and R_RISCV_OVLPLT32.
+	(riscv_reloc_map): Add mappings for overlay
+	relocations.
+	(riscv_reloc_type_lookup): Add workaround to
+	correctly map from overlay relocation numbers to
+	howto table entries.
+	(riscv_elf_rtype_to_howto): Correctly map relocation
+	types to howto entries.
+	* elfxx-riscv.h (struct riscv_elf_link_hash_entry):
+	(struct riscv_elf_link_hash_table)
+	(riscv_elf_hash_entry riscv_elf_hash_table is_riscv_elf)
+	(sec_addr): Move definitions from elfnn-riscv.c.
+	* elfxx-target.h (bfd_elfNN_bfd_get_section_overlay_sort_data):
+	Add define.
+	* ihex.c (ihex_bfd_get_section_overlay_sort_data): Add define.
+	* libbfd-in.h (_bfd_nolink_bfd_get_section_overlay_sort_data):
+	Add define.
+	* libbfd.h: Regenerated.
+	* plugin.c (bfd_plugin_bfd_get_section_overlay_sort_data):
+	Add define.
+	* reloc.c: Add ENUMX entries for BFD_RELOC_RISCV_OVLTOK_HI20,
+	BFD_RELOC_RISCV_OVLTOK_LO12_I, BFD_RELOC_RISCV_OVLTOK32,
+	BFD_RELOC_RISCV_OVLPLT_HI20, BFD_RELOC_RISCV_OVLPLT_LO12_I,
+	and BFD_RELOC_RISCV_OVLPLT32.
+	* section.c (bfd_generic_get_section_overlay_sort_data):
+	Add function.
+	* srec.c (srec_bfd_get_section_overlay_sort_data): Add define.
+	* target.c (BFD_JUMP_TABLE_LINK): Add
+	_bfd_get_section_overlay_sort_data entry.
+	(bfd_copy_link_hash_symbol_type): Likewise.
+	* tekhex.c (tekhex_bfd_get_section_overlay_sort_data):
+	Add define.
+
 2022-02-17  Nick Clifton  <nickc@redhat.com>
 
 	* po/sr.po: Updated Serbian translation.
diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index 0f0138408ce..a42b87c4de7 100644
--- a/bfd/Makefile.am
+++ b/bfd/Makefile.am
@@ -567,6 +567,7 @@ BFD64_BACKENDS = \
 	elf32-riscv.lo \
 	elf64-riscv.lo \
 	elfxx-riscv.lo \
+	elfxx-riscv-overlay.lo \
 	elf64-s390.lo \
 	elf64-sparc.lo \
 	elf64-tilegx.lo \
@@ -616,6 +617,7 @@ BFD64_BACKENDS_CFILES = \
 	elfxx-loongarch.c \
 	elfxx-mips.c \
 	elfxx-riscv.c \
+	elfxx-riscv-overlay.c \
 	mach-o-aarch64.c \
 	mach-o-x86-64.c \
 	mmo.c \
@@ -701,7 +703,8 @@ SOURCE_HFILES = \
 	elf-bfd.h elfcode.h elfcore.h elf-hppa.h elf-linker-x86.h \
 	elf-linux-core.h elf-nacl.h elf-s390.h elf-vxworks.h \
 	elfxx-aarch64.h elfxx-ia64.h elfxx-mips.h elfxx-riscv.h \
-	elfxx-sparc.h elfxx-tilegx.h elfxx-x86.h elfxx-loongarch.h \
+	elfxx-riscv.overlay.h elfxx-sparc.h elfxx-tilegx.h elfxx-x86.h \
+	elfxx-loongarch.h \
 	genlink.h go32stub.h \
 	libaout.h libbfd.h libcoff.h libecoff.h libhppa.h \
 	libpei.h libxcoff.h \
diff --git a/bfd/Makefile.in b/bfd/Makefile.in
index b8e5ea0153f..00dffb8bda8 100644
--- a/bfd/Makefile.in
+++ b/bfd/Makefile.in
@@ -1036,6 +1036,7 @@ BFD64_BACKENDS = \
 	elf32-riscv.lo \
 	elf64-riscv.lo \
 	elfxx-riscv.lo \
+	elfxx-riscv-overlay.lo \
 	elf64-s390.lo \
 	elf64-sparc.lo \
 	elf64-tilegx.lo \
@@ -1085,6 +1086,7 @@ BFD64_BACKENDS_CFILES = \
 	elfxx-loongarch.c \
 	elfxx-mips.c \
 	elfxx-riscv.c \
+	elfxx-riscv-overlay.c \
 	mach-o-aarch64.c \
 	mach-o-x86-64.c \
 	mmo.c \
@@ -1166,7 +1168,8 @@ SOURCE_HFILES = \
 	elf-bfd.h elfcode.h elfcore.h elf-hppa.h elf-linker-x86.h \
 	elf-linux-core.h elf-nacl.h elf-s390.h elf-vxworks.h \
 	elfxx-aarch64.h elfxx-ia64.h elfxx-mips.h elfxx-riscv.h \
-	elfxx-sparc.h elfxx-tilegx.h elfxx-x86.h elfxx-loongarch.h \
+	elfxx-riscv.overlay.h elfxx-sparc.h elfxx-tilegx.h elfxx-x86.h \
+	elfxx-loongarch.h \
 	genlink.h go32stub.h \
 	libaout.h libbfd.h libcoff.h libecoff.h libhppa.h \
 	libpei.h libxcoff.h \
@@ -1668,6 +1671,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-ia64.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-loongarch.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-mips.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-riscv-overlay.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-riscv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-sparc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-tilegx.Plo@am__quote@
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 78a0a1dea42..827cec436c5 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1441,6 +1441,9 @@ const char *bfd_generic_group_name (bfd *, const asection *sec);
 
 bool bfd_generic_discard_group (bfd *abfd, asection *group);
 
+int bfd_generic_get_section_overlay_sort_data
+   (asection *sec, struct bfd_link_info *info);
+
 /* Extracted from archures.c.  */
 enum bfd_architecture
 {
@@ -4403,6 +4406,12 @@ number for the SBIC, SBIS, SBI and CBI instructions  */
   BFD_RELOC_RISCV_SET16,
   BFD_RELOC_RISCV_SET32,
   BFD_RELOC_RISCV_32_PCREL,
+  BFD_RELOC_RISCV_OVLTOK_HI20,
+  BFD_RELOC_RISCV_OVLTOK_LO12_I,
+  BFD_RELOC_RISCV_OVLTOK32,
+  BFD_RELOC_RISCV_OVLPLT_HI20,
+  BFD_RELOC_RISCV_OVLPLT_LO12_I,
+  BFD_RELOC_RISCV_OVLPLT32,
 
 /* Renesas RL78 Relocations.  */
   BFD_RELOC_RL78_NEG8,
@@ -7272,6 +7281,9 @@ extern bfd_byte *bfd_get_relocated_section_contents
   (bfd *, struct bfd_link_info *, struct bfd_link_order *, bfd_byte *,
    bool, asymbol **);
 
+/* Hook for getting BFD specific sort data for a section.  */
+#define bfd_get_section_overlay_sort_data(abfd, sec, info) \
+ BFD_SEND (abfd, _bfd_get_section_overlay_sort_data, (sec, info))
 bool bfd_alt_mach_code (bfd *abfd, int alternative);
 
 bfd_vma bfd_emul_get_maxpagesize (const char *);
@@ -7670,7 +7682,8 @@ typedef struct bfd_target
   NAME##_section_already_linked, \
   NAME##_bfd_define_common_symbol, \
   NAME##_bfd_link_hide_symbol, \
-  NAME##_bfd_define_start_stop
+  NAME##_bfd_define_start_stop, \
+  NAME##_bfd_get_section_overlay_sort_data
 
   int  (*_bfd_sizeof_headers) (bfd *, struct bfd_link_info *);
   bfd_byte *
@@ -7749,6 +7762,10 @@ typedef struct bfd_target
        (*_bfd_define_start_stop) (struct bfd_link_info *, const char *,
                                   asection *);
 
+  /* Get section specific sort data.  */
+  int (*_bfd_get_section_overlay_sort_data) (asection *,
+                                             struct bfd_link_info *);
+
   /* Routines to handle dynamic symbols and relocs.  */
 #define BFD_JUMP_TABLE_DYNAMIC(NAME) \
   NAME##_get_dynamic_symtab_upper_bound, \
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 913ce2d6abe..c5345e63a37 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -2138,6 +2138,9 @@ DESCRIPTION
 .  (bfd *, struct bfd_link_info *, struct bfd_link_order *, bfd_byte *,
 .   bool, asymbol **);
 .
+.{* Hook for getting BFD specific sort data for a section.  *}
+.#define bfd_get_section_overlay_sort_data(abfd, sec, info) \
+. BFD_SEND (abfd, _bfd_get_section_overlay_sort_data, (sec, info))
 
 */
 
diff --git a/bfd/binary.c b/bfd/binary.c
index 999d41c0611..edb1197a7f1 100644
--- a/bfd/binary.c
+++ b/bfd/binary.c
@@ -321,6 +321,7 @@ binary_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define binary_bfd_link_split_section		  _bfd_generic_link_split_section
 #define binary_get_section_contents_in_window	  _bfd_generic_get_section_contents_in_window
 #define binary_bfd_link_check_relocs		  _bfd_generic_link_check_relocs
+#define binary_bfd_get_section_overlay_sort_data  bfd_generic_get_section_overlay_sort_data
 
 const bfd_target binary_vec =
 {
diff --git a/bfd/configure b/bfd/configure
index 4502e52decb..b0447686fbb 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -13532,10 +13532,10 @@ do
     powerpc_elf64_fbsd_le_vec)	 tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
     powerpc_xcoff_vec)		 tb="$tb coff-rs6000.lo $xcoff" ;;
     pru_elf32_vec)		 tb="$tb elf32-pru.lo elf32.lo $elf" ;;
-    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf" ;;
-    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
-    riscv_elf32_be_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf" ;;
-    riscv_elf64_be_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
+    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf" ;;
+    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
+    riscv_elf32_be_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf" ;;
+    riscv_elf64_be_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
     rl78_elf32_vec)		 tb="$tb elf32-rl78.lo elf32.lo $elf" ;;
     rs6000_xcoff64_vec)		 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
     rs6000_xcoff64_aix_vec)	 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
diff --git a/bfd/configure.ac b/bfd/configure.ac
index 07f2074770f..f1553402d7c 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -611,10 +611,10 @@ do
     powerpc_elf64_fbsd_le_vec)	 tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
     powerpc_xcoff_vec)		 tb="$tb coff-rs6000.lo $xcoff" ;;
     pru_elf32_vec)		 tb="$tb elf32-pru.lo elf32.lo $elf" ;;
-    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf" ;;
-    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
-    riscv_elf32_be_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf" ;;
-    riscv_elf64_be_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
+    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf" ;;
+    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
+    riscv_elf32_be_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf" ;;
+    riscv_elf64_be_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elfxx-riscv-overlay.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
     rl78_elf32_vec)		 tb="$tb elf32-rl78.lo elf32.lo $elf" ;;
     rs6000_xcoff64_vec)		 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
     rs6000_xcoff64_aix_vec)	 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 8f9f0d8a86a..f5050d00fb2 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -63,23 +63,6 @@
 
 #define RISCV_ATTRIBUTES_SECTION_NAME ".riscv.attributes"
 
-/* RISC-V ELF linker hash entry.  */
-
-struct riscv_elf_link_hash_entry
-{
-  struct elf_link_hash_entry elf;
-
-#define GOT_UNKNOWN	0
-#define GOT_NORMAL	1
-#define GOT_TLS_GD	2
-#define GOT_TLS_IE	4
-#define GOT_TLS_LE	8
-  char tls_type;
-};
-
-#define riscv_elf_hash_entry(ent) \
-  ((struct riscv_elf_link_hash_entry *) (ent))
-
 struct _bfd_riscv_elf_obj_tdata
 {
   struct elf_obj_tdata root;
@@ -98,11 +81,6 @@ struct _bfd_riscv_elf_obj_tdata
   (*((h) != NULL ? &riscv_elf_hash_entry (h)->tls_type		\
      : &_bfd_riscv_elf_local_got_tls_type (abfd) [symndx]))
 
-#define is_riscv_elf(bfd)				\
-  (bfd_get_flavour (bfd) == bfd_target_elf_flavour	\
-   && elf_tdata (bfd) != NULL				\
-   && elf_object_id (bfd) == RISCV_ELF_DATA)
-
 static bool
 elfNN_riscv_mkobject (bfd *abfd)
 {
@@ -114,31 +92,6 @@ elfNN_riscv_mkobject (bfd *abfd)
 #include "elf/common.h"
 #include "elf/internal.h"
 
-struct riscv_elf_link_hash_table
-{
-  struct elf_link_hash_table elf;
-
-  /* Short-cuts to get to dynamic linker sections.  */
-  asection *sdyntdata;
-
-  /* The max alignment of output sections.  */
-  bfd_vma max_alignment;
-
-  /* Used by local STT_GNU_IFUNC symbols.  */
-  htab_t loc_hash_table;
-  void * loc_hash_memory;
-
-  /* The index of the last unused .rel.iplt slot.  */
-  bfd_vma last_iplt_index;
-
-  /* The data segment phase, don't relax the section
-     when it is exp_seg_relro_adjust.  */
-  int *data_segment_phase;
-
-  /* Relocations for variant CC symbols may be present.  */
-  int variant_cc;
-};
-
 /* Instruction access functions. */
 #define riscv_get_insn(bits, ptr)		\
   ((bits) == 16 ? bfd_getl16 (ptr)		\
@@ -151,12 +104,6 @@ struct riscv_elf_link_hash_table
    : (bits) == 64 ? bfd_putl64 (val, ptr)	\
    : (abort (), (void) 0))
 
-/* Get the RISC-V ELF linker hash table from a link_info structure.  */
-#define riscv_elf_hash_table(p) \
-  ((is_elf_hash_table ((p)->hash)					\
-    && elf_hash_table_id (elf_hash_table (p)) == RISCV_ELF_DATA)	\
-   ? (struct riscv_elf_link_hash_table *) (p)->hash : NULL)
-
 static bool
 riscv_info_to_howto_rela (bfd *abfd,
 			  arelent *cache_ptr,
@@ -201,8 +148,6 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
    entry used for runtime profile?  */
 #define GOTPLT_HEADER_SIZE (2 * GOT_ENTRY_SIZE)
 
-#define sec_addr(sec) ((sec)->output_section->vma + (sec)->output_offset)
-
 #if ARCH_SIZE == 32
 # define MATCH_LREG MATCH_LW
 #else
@@ -299,6 +244,10 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
 
       eh = (struct riscv_elf_link_hash_entry *) entry;
       eh->tls_type = GOT_UNKNOWN;
+      eh->ovl.needs_ovlplt_entry = false;
+      eh->ovl.needs_overlay_group = false;
+      eh->ovl.non_overlay_reference = false;
+      eh->ovl.overlay_groups_resolved = false;
     }
 
   return entry;
@@ -419,6 +368,9 @@ riscv_elf_link_hash_table_create (bfd *abfd)
     }
   ret->elf.root.hash_table_free = riscv_elf_link_hash_table_free;
 
+  if (!riscv_elf_overlay_link_hash_table_init (&ret->ovl))
+    return NULL;
+
   return &ret->elf.root;
 }
 
@@ -725,6 +677,25 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
       switch (r_type)
 	{
+	case R_RISCV_OVLTOK_HI20:
+	case R_RISCV_OVLTOK_LO12_I:
+	case R_RISCV_OVLTOK32:
+	case R_RISCV_OVLPLT_HI20:
+	case R_RISCV_OVLPLT_LO12_I:
+	case R_RISCV_OVLPLT32:
+	  {
+	    struct riscv_elf_link_hash_entry *eh = NULL;
+	    struct riscv_elf_link_hash_entry_overlay *ovl = NULL;
+	    eh = (struct riscv_elf_link_hash_entry *) h;
+	    if (eh)
+	      ovl = &eh->ovl;
+	    if (!riscv_elf_overlay_check_relocation (htab->elf.dynobj,
+						     info, ovl, &htab->ovl,
+						     r_type))
+	      return false;
+	  }
+	  break;
+
 	case R_RISCV_TLS_GD_HI20:
 	  if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
 	      || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_TLS_GD))
@@ -746,6 +717,15 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  break;
 
 	case R_RISCV_CALL:
+	  if (h != NULL)
+	    {
+	      /* Note this symbol as a non-overlay function.  */
+	      struct riscv_elf_link_hash_entry *eh =
+		(struct riscv_elf_link_hash_entry *)h;
+	      eh->ovl.non_overlay_reference = true;
+	    }
+	  /* fallthrough */
+
 	case R_RISCV_CALL_PLT:
 	  /* These symbol requires a procedure linkage table entry.
 	     We actually build the entry in adjust_dynamic_symbol,
@@ -773,6 +753,11 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		 so we always need the plt when it refers to
 		 ifunc symbol.  */
 	      h->plt.refcount += 1;
+
+	      /* Note this symbol as a non-overlay function.  */
+	      struct riscv_elf_link_hash_entry *eh =
+		(struct riscv_elf_link_hash_entry *)h;
+	      eh->ovl.non_overlay_reference = true;
 	    }
 	  /* Fall through.  */
 
@@ -945,11 +930,45 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	default:
 	  break;
 	}
+
+      if (h)
+	{
+	  struct riscv_elf_link_hash_entry *eh =
+	    (struct riscv_elf_link_hash_entry *)h;
+	  if (eh->ovl.needs_overlay_group && eh->ovl.non_overlay_reference)
+	    {
+	      (*_bfd_error_handler) (_("%pB: Symbol '%s` referred to by both "
+				       "overlay and non-overlay relocations"),
+				     abfd, h->root.root.string);
+	      return false;
+	    }
+	}
     }
 
   return true;
 }
 
+/* Look at all sections in abfd and enable overlay support if any contains
+   overlay references.  */
+
+static bool
+riscv_elf_check_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab;
+
+  if (bfd_link_relocatable (info))
+    return true;
+
+  htab = riscv_elf_hash_table (info);
+
+  if (htab->elf.dynobj == NULL)
+    htab->elf.dynobj = abfd;
+
+  riscv_elf_overlay_check_sections (abfd, info, &htab->ovl);
+
+  return true;
+}
+
 static asection *
 riscv_elf_gc_mark_hook (asection *sec,
 			struct bfd_link_info *info,
@@ -957,6 +976,11 @@ riscv_elf_gc_mark_hook (asection *sec,
 			struct elf_link_hash_entry *h,
 			Elf_Internal_Sym *sym)
 {
+  /* Note that gc-sections has been used, allowing some overlay table
+     generation to be skipped for dead functions (i.e sec->gc_mark is
+     reliable). */
+  riscv_overlay_set_use_gcmark ();
+
   if (h != NULL)
     switch (ELFNN_R_TYPE (rel->r_info))
       {
@@ -1638,6 +1662,8 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_GOT_HI20:
     case R_RISCV_TLS_GOT_HI20:
     case R_RISCV_TLS_GD_HI20:
+    case R_RISCV_OVLTOK_HI20:
+    case R_RISCV_OVLPLT_HI20:
       if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)))
 	return bfd_reloc_overflow;
       value = ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
@@ -1648,6 +1674,8 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_TPREL_LO12_I:
     case R_RISCV_TPREL_I:
     case R_RISCV_PCREL_LO12_I:
+    case R_RISCV_OVLTOK_LO12_I:
+    case R_RISCV_OVLPLT_LO12_I:
       value = ENCODE_ITYPE_IMM (value);
       break;
 
@@ -1727,6 +1755,8 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_32_PCREL:
     case R_RISCV_TLS_DTPREL32:
     case R_RISCV_TLS_DTPREL64:
+    case R_RISCV_OVLTOK32:
+    case R_RISCV_OVLPLT32:
       break;
 
     case R_RISCV_DELETE:
@@ -2771,6 +2801,18 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	  unresolved_reloc = false;
 	  break;
 
+	case R_RISCV_OVLTOK_HI20:
+	case R_RISCV_OVLTOK_LO12_I:
+	case R_RISCV_OVLTOK32:
+	case R_RISCV_OVLPLT_HI20:
+	case R_RISCV_OVLPLT_LO12_I:
+	case R_RISCV_OVLPLT32:
+	  if (riscv_overlay_handle_relocation (&relocation, output_bfd,
+					       info, h, &htab->ovl, sym,
+					       r_type))
+	    unresolved_reloc = false;
+	  break;
+
 	default:
 	  r = bfd_reloc_notsupported;
 	}
@@ -3259,6 +3301,10 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd,
 	}
     }
 
+  if (htab->ovl.overlay_enabled
+      && !riscv_overlay_finish_sections (&htab->elf, &htab->ovl, info))
+      return false;
+
   if (htab->elf.sgotplt)
     {
       asection *output_section = htab->elf.sgotplt->output_section;
@@ -4074,6 +4120,7 @@ riscv_relax_delete_bytes (bfd *abfd,
 
   /* Actually delete the bytes.  */
   sec->size -= count;
+  riscv_overlay_relax_delete_bytes (sec, count, link_info);
   memmove (contents + addr, contents + addr + count, toaddr - addr - count);
 
   /* Adjust the location of all of the relocs.  Note that we need not
@@ -5238,6 +5285,50 @@ riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
     h->other |= STO_RISCV_VARIANT_CC;
 }
 
+void
+riscv_elf_overlay_hook_elfNNlriscv(struct bfd_link_info *info);
+void
+riscv_elf_overlay_hook_elfNNbriscv(struct bfd_link_info *info);
+
+/* Prepare overlay data structures.
+
+   This function is called by the LDEMUL_AFTER_CHECK_RELOCS hook.  */
+
+void 
+riscv_elf_overlay_hook_elfNNlriscv(struct bfd_link_info *info)
+{
+  bool ret;
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  if (!htab->ovl.overlay_enabled || htab->elf.dynobj == NULL)
+    return;
+
+  const struct elf_backend_data *bed = get_elf_backend_data (htab->elf.dynobj);
+
+  ret = riscv_elf_overlay_generate_tables (info->output_bfd, info,
+					   sizeof(ElfNN_External_Sym));
+  if (!ret)
+    return;
+
+  riscv_elf_overlay_create_ovlgrps (&htab->elf, &htab->ovl, bed, info);
+  riscv_elf_overlay_duplicate_multigroup_sections (&htab->elf, &htab->ovl,
+						   bed, info,
+						   sizeof(ElfNN_External_Sym));
+  riscv_elf_overlay_pad_groups (&htab->elf, &htab->ovl, bed, info);
+}
+
+/* Overlays are not supported for big-endian but this hook is needed to be able
+   to build the tools.  It just outputs an error.  */
+
+void
+riscv_elf_overlay_hook_elfNNbriscv(struct bfd_link_info *info)
+{
+  info->callbacks->einfo (_("%F%P: overlays are not yet supported for "
+			    "big-endian RISC-V"),
+			  info->output_bfd);
+}
+
 #define TARGET_LITTLE_SYM			riscv_elfNN_vec
 #define TARGET_LITTLE_NAME			"elfNN-littleriscv"
 #define TARGET_BIG_SYM				riscv_elfNN_be_vec
@@ -5251,10 +5342,13 @@ riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
 #define bfd_elfNN_bfd_merge_private_bfd_data \
   _bfd_riscv_elf_merge_private_bfd_data
 #define bfd_elfNN_bfd_is_target_special_symbol	riscv_elf_is_target_special_symbol
+#define bfd_elfNN_bfd_get_section_overlay_sort_data \
+  riscv_elf_overlay_sort_value
 
 #define elf_backend_copy_indirect_symbol	riscv_elf_copy_indirect_symbol
 #define elf_backend_create_dynamic_sections	riscv_elf_create_dynamic_sections
 #define elf_backend_check_relocs		riscv_elf_check_relocs
+#define elf_backend_check_directives		riscv_elf_check_sections
 #define elf_backend_adjust_dynamic_symbol	riscv_elf_adjust_dynamic_symbol
 #define elf_backend_size_dynamic_sections	riscv_elf_size_dynamic_sections
 #define elf_backend_relocate_section		riscv_elf_relocate_section
diff --git a/bfd/elfxx-riscv-overlay.c b/bfd/elfxx-riscv-overlay.c
new file mode 100644
index 00000000000..575841505d1
--- /dev/null
+++ b/bfd/elfxx-riscv-overlay.c
@@ -0,0 +1,2563 @@
+/* RISC-V overlay support for ELF.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   Contributed by Edward Jones (ed.jones@embecosm.com).
+
+   This file is part of BFD, the Binary File Descriptor library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3. If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+/* 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.  This file contains the
+   hooks into BFD to achieve this behaviour.
+
+   All symbols which exist in an overlay eventually end up in a
+   specially formatted output section called `.ovlgrps'. This
+   output section contains both the loadable contents of each overlay
+   group and the metadata tables required by the runtime engine.
+
+   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 group table and the
+     multigroup table. This section occupies the first group
+     (group 0) in `.ovlgrps'.
+
+   - `.ovlinput.__internal.padding.<group>'
+
+     A special padding section which is placed after the last
+     `.ovlinput.<symbol>' section assigned to `<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
+     group contents.
+
+   - `.ovlinput.__internal.duplicate.<group>.<symbol>'
+
+     When a symbol exists in multiple overlay group 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 is always associated
+     with the first group it is assigned to.
+
+   In addition to the above input sections there is also a
+   '.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.
+   This ordering is as follows:
+
+   - '.ovlinput.__internal.grouptables`
+   - '.ovlinput.__internal.padding.0`
+   - for group 1 to N:
+       for each symbol in group:
+         '.ovlinput.<symbol>`
+             or '.ovlinput.__internal.duplicate.<group>.<symbol>`
+         '.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 group 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 following order of operations
+   takes place:
+
+   # riscv_elf_overlay_check_relocation
+
+   - Mark symbols as requiring a group assignment if they
+     are referred to by overlay relocations.
+   - Creates and allocates space in '.ovlplt` for any overlay
+     'plt` references to a symbol.
+
+   # riscv_elf_overlay_generate_tables
+
+   - Initialize internal data structures to track group
+     assignments.
+   - Populate data structures with symbols that require
+     an assignment.
+   - Assign groups using an external tool, a provided
+     '.csv` grouping file, or automatically.
+
+   # riscv_elf_overlay_create_ovlgrps
+
+   - Create the '.ovlinput.__internal.grouptables` section.
+
+   # riscv_elf_overlay_duplicate_multigroup_sections
+
+   - Generate '.ovlinput.__internal.duplicate.<group>.<symbol>`
+     sections for any symbol assigned to multiple groups.
+
+   # riscv_elf_overlay_pad_groups
+
+   - Generate the padding section for each group.
+
+   # riscv_elf_overlay_sort_value
+
+   - Correctly lay out the user provided and linker generated
+     input sections assigned to '.ovlgrps`
+
+   # riscv_overlay_relax_delete_bytes
+
+   - Resize 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.
+   - Resize any overlay duplicate sections for the same
+     symbol to match.
+
+   # riscv_overlay_handle_relocation
+
+   - Materialize token values and use them to fill in overlay
+     relocations.
+
+   # riscv_overlay_finish_sections
+
+   - Fill in the '.ovlplt` entries and the grouping table and
+     multigroup table in '.ovlinput.__internal.grouptables`.
+   - Duplicate the contents for multigroup symbols in '.ovlgrps`
+     to all of their assigned groups.
+   - Emit the padding and crc at the end of each group.
+*/
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#include "elf/riscv.h"
+#include "opcode/riscv.h"
+#include "libiberty.h"
+#include "elfxx-riscv.h"
+#include "elfxx-riscv-overlay.h"
+#include "safe-ctype.h"
+#include "objalloc.h"
+
+/* Byte size of the crc at the end of an overlay group.  */
+#define OVL_CRC_SZ  4
+
+/* Size in bytes of a 'page' of an overlay group. Overlay groups are always a
+   multiple of the page size.  */
+#define OVL_GROUP_PAGE_SIZE_POW2 9
+#define OVL_GROUP_PAGE_SIZE (1 << OVL_GROUP_PAGE_SIZE_POW2)
+
+/* Maximum size in bytes of an overlay group.  This will be a multiple of
+   the page size.  */
+#define OVL_MAX_GROUP_SIZE 4096
+
+/* The maximum group number that a symbol can be assigned to.  */
+#define OVL_MAX_GROUP_ID 65535
+
+/* Alignment in bytes of symbols within an overlay group.  */
+#define OVL_SYM_ALIGN 4
+
+/* The default first group to which overlay symbols can be allocated to.
+   Group number 0 is always reserved to store the group table and multigroup
+   tables themselves.  */
+#define OVL_DEFAULT_FIRST_FREE_GROUP  1
+
+/* Size in bytes of a multigroup entry.  */
+#define OVL_MULTIGROUP_ENTRY_SIZE 4
+
+/* Number of (32-bit) instructions in an overlay PLT table entry.  */
+#define OVLPLT_ENTRY_INSNS 3
+
+/* Size in bytes of an entry in the overlay plt table.  */
+#define OVLPLT_ENTRY_SIZE  OVLPLT_ENTRY_INSNS * 4
+
+/* Prefixes for internal section names used for overlays.  */
+#define OVL_INPUT_SEC_PREFIX     ".ovlinput."
+#define OVL_INTERNAL_SEC_PREFIX  OVL_INPUT_SEC_PREFIX "__internal."
+#define OVL_DUPLICATE_SEC_PREFIX OVL_INTERNAL_SEC_PREFIX "duplicate."
+#define OVL_PADDING_SEC_PREFIX   OVL_INTERNAL_SEC_PREFIX "padding."
+#define OVL_GROUPTABLES_SEC_NAME OVL_INTERNAL_SEC_PREFIX "grouptables"
+
+
+/* An entry in the list of overlay group. This tracks the information
+   about each group needed to layout functions in their groups,
+   build the sections and populate the contents.  */
+
+struct ovl_group_list_entry
+{
+  /* A group is not initialized until it is known that it will have some
+     contents.  This happens when either the first function is assigned
+     to the group, or group 0 (which holds the group table) needs to be
+     populated.  */
+  bool is_initialized;
+
+  /* A list of the names of functions assigned to this group. The order
+     in this list defines the order they will be laid out in memory.  */
+  int n_functions;
+  const char **functions;
+
+  /* Size of the contents of the group. This corresponds to the size of
+     all of the assigned functions and any padding in between them.  */
+
+  /* Size of the groups contents, size of it when padded, and the offset
+     to the start of the group in the .ovlgrps output section.  */
+  bfd_vma group_size;
+
+  /* Size of the group after it has been padded up to the page size. This
+     is *not* the same as 'group_size' rounded up to the next page size
+     as 'group_size' does not account for the size of the CRC.
+
+     This value will be 0 until after the 'group_size' has been settled.  */
+  bfd_vma padded_group_size;
+
+  /* Offset of the contents of this group in the .ovlgrps output section.  */
+  bfd_vma ovlgrpdata_offset;
+
+  /* The calculated CRC for this group.  These bytes are written at the
+     end of the padding, bringing the group up to the 'padded_group_size'  */
+  unsigned int crc;
+
+  /* Symbols names for the first and last functions allocated to this
+     group.  */
+  const char *first_func;
+  const char *last_func;
+};
+
+/* Hash entry storing the groups to which a function belongs.  */
+
+struct ovl_func_hash_entry
+{
+  struct bfd_hash_entry root;
+
+  /* Linked list of the overlay groups which hold the current function.  */
+  struct ovl_func_group_info *groups;
+
+  /* Tail of the linked list of overlay groups for this function.  */
+  struct ovl_func_group_info *tail;
+
+  /* True if this function exists in more than one overlay group.  */
+  bool multigroup;
+
+  /* Offset of the entry for this function in the multigroup table.  */
+  bfd_vma multigroup_offset;
+
+  /* Token value to refer to the multigroup entry for this function.  */
+  bfd_vma multigroup_token;
+
+  /* Tracks whether an overlay PLT entry is required for this function.  */
+  bool plt_entry;
+
+  /* Offset of an overlay PLT entry if required for this function.  */
+  bfd_vma plt_offset;
+};
+
+/* A flag noting whether the gc mark and sweep pass has run, and therefore can
+   reliably determine which functions should be treated as deleted.  */
+static bool overlay_use_gcmark = 0;
+
+/* Mark that the gc mark and sweep has run.  */
+void
+riscv_overlay_set_use_gcmark (void)
+{
+  overlay_use_gcmark = 1;
+}
+
+static struct
+bfd_hash_entry *ovl_func_hash_newfunc (struct bfd_hash_entry *entry,
+				       struct bfd_hash_table *table,
+				       const char *string);
+
+/* Create a new overlay grouping hash table.  */
+
+static bool
+ovl_func_hash_table_init (struct bfd_hash_table *table)
+{
+  return bfd_hash_table_init (table, ovl_func_hash_newfunc,
+			      sizeof (struct ovl_func_hash_entry));
+}
+
+static void
+ovl_group_list_init (struct ovl_group_list *list)
+{
+  BFD_ASSERT (list != NULL);
+  list->n_groups = 0;
+
+  /* Allocate space for a single group even though there are none needed yet.
+     This means bfd_realloc can be used unconditionally when new groups are
+     added.  */
+  list->groups = bfd_zmalloc (sizeof (*list->groups));
+}
+
+/* Store grouping info in a hash table.  */
+
+static bool
+create_ovl_group_table (struct bfd_hash_table *ovl_func_table,
+			struct ovl_group_list *ovl_group_list,
+			bool * ovl_tables_populated)
+{
+  *ovl_tables_populated = false;
+
+  if (!ovl_func_hash_table_init (ovl_func_table))
+    return false;
+
+  ovl_group_list_init (ovl_group_list);
+  return true;
+}
+
+/* Print functions for debugging.  */
+
+static void print_group_list (struct ovl_group_list *list);
+static void print_func_table (struct bfd_hash_table *table);
+
+bool
+riscv_elf_overlay_link_hash_table_init (
+    struct riscv_elf_overlay_link_hash_table *ovl)
+{
+  ovl->sovlplt = NULL;
+  ovl->next_ovlplt_offset = 0;
+  ovl->ovl_group_table_size = 0;
+  ovl->ovl_group_table_max_group = 0;
+  ovl->ovl_multigroup_table_size = 0;
+
+  if (!create_ovl_group_table (&ovl->ovl_func_table, &ovl->ovl_group_list,
+			       &ovl->ovl_tables_populated))
+    return false;
+
+  if (riscv_overlay_debug)
+    {
+      print_group_list (&ovl->ovl_group_list);
+      print_func_table (&ovl->ovl_func_table);
+    }
+  return true;
+}
+
+/* Highest group number seen so far.  */
+static unsigned ovl_max_group = 0;
+
+/* Linked list of overlay group ids.
+   NOTE: Offset will not change with relaxation, so this value MUST NOT be
+         relied upon for token generation, use the output_offset instead.
+   NOTE: processed_offset is used for mapfile generation and is calculated
+         during the final link stage.  */
+
+struct ovl_func_group_info
+{
+  bfd_vma id;
+  bfd_vma unrelaxed_offset;
+  bfd_vma processed_offset;
+  struct ovl_func_group_info *next;
+};
+
+static struct ovl_group_list_entry *
+ovl_group_list_newfunc (struct ovl_group_list *list, int group)
+{
+  BFD_ASSERT (list != NULL);
+  BFD_ASSERT (list->groups != NULL);
+
+  if (group >= list->n_groups)
+    {
+      list->groups = bfd_realloc (list->groups,
+				  sizeof (*list->groups) * (group + 1));
+      for (; list->n_groups < (group + 1); list->n_groups++)
+	list->groups[list->n_groups].is_initialized = false;
+    }
+  else
+    BFD_ASSERT (!list->groups[group].is_initialized);
+
+  struct ovl_group_list_entry *ret = &list->groups[group];
+
+  ret->n_functions = 0;
+  ret->functions = NULL;
+  ret->group_size = 0;
+  ret->padded_group_size = 0;
+  ret->ovlgrpdata_offset = 0;
+  ret->first_func = NULL;
+  ret->last_func = NULL;
+
+  ret->is_initialized = true;
+  return ret;
+}
+
+/* Retrieve or create a new entry in the overlay group list.  */
+static struct ovl_group_list_entry *
+ovl_group_list_lookup (struct ovl_group_list *ovl_group_list,
+		       int group, bool create)
+{
+  BFD_ASSERT (ovl_group_list);
+  BFD_ASSERT (ovl_group_list->groups);
+
+  if (group >= ovl_group_list->n_groups)
+    {
+      if (create)
+	return ovl_group_list_newfunc (ovl_group_list, group);
+      else
+	return NULL;
+    }
+  else
+    {
+      if (ovl_group_list->groups[group].is_initialized)
+	return &ovl_group_list->groups[group];
+      else if (create)
+	return ovl_group_list_newfunc (ovl_group_list, group);
+      else
+	return NULL;
+    }
+}
+
+static bool
+ovl_group_list_traverse (struct ovl_group_list *ovl_group_list,
+			 bool (*func) (struct ovl_group_list_entry *,
+					      int, void *), void *info)
+{
+  bool ret = true;
+  for (int i = 0; i < ovl_group_list->n_groups; i++)
+    {
+      struct ovl_group_list_entry *entry = &ovl_group_list->groups[i];
+      if (entry->is_initialized)
+	ret = func (entry, i, info);
+      if (ret == false)
+	break;
+    }
+  return ret;
+}
+
+static void
+ovl_update_group (struct ovl_group_list *list, int group_id, const char *func)
+{
+  struct ovl_group_list_entry *group_entry;
+
+  /* Update the entry in the group list.  */
+  group_entry = ovl_group_list_lookup (list, group_id, true);
+  BFD_ASSERT (group_entry != NULL);
+
+  int n_functions = group_entry->n_functions + 1;
+  if (group_entry->functions == NULL)
+    {
+      group_entry->functions =
+	bfd_malloc (n_functions * sizeof (*group_entry->functions));
+    }
+  else
+    {
+      group_entry->functions =
+	bfd_realloc (group_entry->functions,
+		     n_functions * sizeof (*group_entry->functions));
+    }
+  /* Take a copy of the function name.  */
+  char *func_name = bfd_malloc (strlen (func) + 1);
+  strcpy (func_name, func);
+
+  group_entry->functions[group_entry->n_functions] = func_name;
+  group_entry->n_functions = n_functions;
+}
+
+/* Create a new entry in an overlay grouping hash_table.  */
+static struct bfd_hash_entry *
+ovl_func_hash_newfunc (struct bfd_hash_entry *entry,
+		       struct bfd_hash_table *table, const char *string)
+{
+  struct ovl_func_hash_entry *ret = (struct ovl_func_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     derived class.  */
+  if (ret == NULL)
+    {
+      ret = bfd_hash_allocate (table, sizeof (*ret));
+      if (ret == NULL)
+	return NULL;
+    }
+
+  /* Call the allocation method of the base class.  */
+  ret = ((struct ovl_func_hash_entry *)
+	 bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+  /* Initialize local fields.  */
+  ret->groups = NULL;
+  ret->tail = NULL;
+  ret->multigroup = false;
+  ret->multigroup_offset = 0;
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Look up an entry in an overlay grouping hash table.  */
+
+#define ovl_func_hash_lookup(table, string, create, copy) \
+  ((struct ovl_func_hash_entry *)                         \
+   bfd_hash_lookup (table, (string), (create), (copy)))
+
+/* Traverse an overlay group hash table.  */
+
+#define ovl_func_hash_traverse(table, func, info)           \
+  (bfd_hash_traverse                                              \
+   (table,                                                        \
+    (bool (*) (struct bfd_hash_entry *, void *)) (func),   \
+    (info)))
+
+static void
+ovl_update_func (struct bfd_hash_table *table, const char *func,
+		 bfd_vma group)
+{
+  struct ovl_func_hash_entry *entry;
+  struct ovl_func_group_info *this_node;
+
+  entry = ovl_func_hash_lookup (table, func, true, true);
+  BFD_ASSERT (entry != NULL);
+
+  this_node = objalloc_alloc ((struct objalloc *) table->memory,
+			      sizeof (struct ovl_func_group_info));
+  this_node->id = group;
+  this_node->unrelaxed_offset = 0;
+  this_node->processed_offset = 0;
+  this_node->next = NULL;
+
+  if (entry->groups == NULL)
+    entry->groups = this_node;
+  else
+    {
+      entry->tail->next = this_node;
+      entry->multigroup = true;
+      entry->multigroup_offset = 0;
+    }
+  entry->tail = this_node;
+}
+
+/* Run through abfd and return the asection for the group with the
+   provided id.  */
+
+static asection *
+get_ovl_input_section (bfd * abfd, const char *sym_name)
+{
+  ssize_t sec_len;
+  char *sec_name;
+  asection *sec;
+
+  sec_len = strlen (OVL_INPUT_SEC_PREFIX) + strlen (sym_name) + 1;
+  sec_name = bfd_zmalloc (sec_len);
+  snprintf (sec_name, sec_len, OVL_INPUT_SEC_PREFIX "%s", sym_name);
+
+  sec = bfd_get_section_by_name (abfd, sec_name);
+  free (sec_name);
+  return sec;
+}
+
+/* Run through abfd and return the asection for the duplicate overlay group
+   for the provided group id and symbol name.  */
+
+static asection *
+get_ovl_duplicate_section (bfd * abfd, bfd_vma group_id, const char *sym_name)
+{
+  ssize_t sec_len;
+  char *sec_name;
+  asection *sec;
+
+  sec_len = strlen (OVL_DUPLICATE_SEC_PREFIX) + /*group_id */ 10 + 1
+    + strlen (sym_name) + 1;
+  sec_name = bfd_zmalloc (sec_len);
+  snprintf (sec_name, sec_len,
+	    OVL_DUPLICATE_SEC_PREFIX "%" BFD_VMA_FMT "u.%s", group_id,
+	    sym_name);
+
+  sec = bfd_get_section_by_name (abfd, sec_name);
+  free (sec_name);
+  return sec;
+}
+
+/* Run through abfd and return the internal padding section associated with
+   the provided group id.  */
+
+static asection *
+get_ovl_padding_section (bfd * abfd, bfd_vma group_id)
+{
+  ssize_t sec_len;
+  char *sec_name;
+  asection *sec;
+
+  sec_len = strlen (OVL_PADDING_SEC_PREFIX) + /*group_id */ 10 + 1;
+  sec_name = bfd_zmalloc (sec_len);
+  snprintf (sec_name, sec_len, OVL_PADDING_SEC_PREFIX "%" BFD_VMA_FMT "u",
+	    group_id);
+
+  sec = bfd_get_section_by_name (abfd, sec_name);
+  free (sec_name);
+  return sec;
+}
+
+/* Create a new section for a duplicate overlay group and attach it to the
+   end of the chain of sections for abfd.  */
+
+static asection *
+make_ovl_duplicate_section (bfd * abfd, bfd_vma group_id,
+			    const char *sym_name, flagword flags)
+{
+  ssize_t sec_len;
+  char *sec_name;
+
+  sec_len = strlen (OVL_DUPLICATE_SEC_PREFIX) + /*group_id */ 10 + 1
+    + strlen (sym_name) + 1;
+  sec_name = bfd_alloc (abfd, sec_len);
+  snprintf (sec_name, sec_len,
+	    OVL_DUPLICATE_SEC_PREFIX "%" BFD_VMA_FMT "u.%s", group_id,
+	    sym_name);
+
+  return bfd_make_section_anyway_with_flags (abfd, sec_name, flags);
+}
+
+/* Create a new section for padding for an overlay group and attach it to the
+   end of the chain of sections for abfd.  */
+
+static asection *
+make_ovl_padding_section (bfd * abfd, bfd_vma group_id, flagword flags)
+{
+  ssize_t sec_len;
+  char *sec_name;
+
+  sec_len = strlen (OVL_PADDING_SEC_PREFIX) + /*group_id */ 10 + 1;
+  sec_name = bfd_alloc (abfd, sec_len);
+  snprintf (sec_name, sec_len, OVL_PADDING_SEC_PREFIX "%" BFD_VMA_FMT "u",
+	    group_id);
+
+  return bfd_make_section_anyway_with_flags (abfd, sec_name, flags);
+}
+
+/* For a function in the overlay grouping file, return whether this is a valid
+   function, or whether it should be skipped (either because it doesn't exist,
+   or GC sections has removed it.) If a function is being ignored, REASON is
+   updated to provide a reason why.  */
+
+static bool
+ovl_enable_grouping_for_func (const char *func, struct bfd_link_info *info,
+			      char **reason)
+{
+  bfd *ibfd;
+
+  if (func == NULL)
+    return false;
+
+  /* Search for a section for this symbol, and check whether the function
+     has been deleted.  */
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      if (!is_riscv_elf (ibfd))
+	continue;
+
+      asection *sec = get_ovl_input_section (ibfd, func);
+      if (sec)
+	{
+	  /* If GC sections not in use, return true to indicate found. */
+	  if (!overlay_use_gcmark)
+	    return true;
+	  /* Otherwise return the GC Mark value */
+	  if (sec->gc_mark == 1)
+	    return true;
+	  *reason = "section removed by gc-sections";
+	  return false;
+	}
+    }
+
+  /* The function was not found, return false to not create its entry.  */
+  *reason = "section not found";
+  return false;
+}
+
+/* Generate an overlay PLT entry.  */
+
+static bool
+make_ovlplt_entry (bfd_vma addr, uint32_t * entry)
+{
+  /* lui   x30, %hi(.ovlplt entry)
+     addi  x30, x30, %lo(.ovlplt entry)
+     jalr  zero, x31, 0  */
+  entry[0] = RISCV_UTYPE (LUI, X_T5, RISCV_CONST_HIGH_PART (addr));
+  entry[1] = RISCV_ITYPE (ADDI, X_T5, X_T5, RISCV_CONST_LOW_PART (addr));
+  entry[2] = RISCV_ITYPE (JALR, X_ZERO, X_T6, 0);
+
+  return true;
+}
+
+/* Fill in all of the overlay PLT entries in turn, based on the token value at
+   the start of each entry.  */
+
+static bool
+fill_ovlplt_sections (bfd * output_bfd,
+		      struct riscv_elf_overlay_link_hash_table *ovl)
+{
+  if (ovl->sovlplt)
+    {
+      bfd_vma off, i, token;
+      uint32_t ovlplt_entry[OVLPLT_ENTRY_INSNS];
+
+      for (off = 0; off < ovl->next_ovlplt_offset; off += OVLPLT_ENTRY_SIZE)
+	{
+	  token = bfd_get_32 (output_bfd, ovl->sovlplt->contents + off);
+	  if (!make_ovlplt_entry (token, ovlplt_entry))
+	    return false;
+
+	  for (i = 0; i < OVLPLT_ENTRY_INSNS; i++)
+	    bfd_put_32 (output_bfd, ovlplt_entry[i],
+			ovl->sovlplt->contents + off + 4 * i);
+	}
+    }
+
+  return true;
+}
+
+/* Fill in all the .ovlgrptbl table entries. */
+static bool
+fill_ovlgrptbl (struct elf_link_hash_table *elf,
+		struct riscv_elf_overlay_link_hash_table *ovl)
+{
+  asection *group_table_sec =
+    bfd_get_section_by_name (elf->dynobj, OVL_GROUPTABLES_SEC_NAME);
+
+  bfd_vma offset = 0;
+  unsigned group = 0;
+  unsigned max_group = ovl->ovl_group_table_max_group;
+  for (group = 0; group <= (max_group + 1); group++)
+    {
+      /* Store current offset.  */
+      uint16_t offset_stored = offset / OVL_GROUP_PAGE_SIZE;
+      bfd_put_16 (elf->dynobj, offset_stored,
+		  group_table_sec->contents + group * 2);
+
+      struct ovl_group_list_entry *group_list_entry =
+	ovl_group_list_lookup (&ovl->ovl_group_list, group, false);
+      if (group_list_entry)
+	offset += group_list_entry->padded_group_size;
+    }
+  /* The last entry in the .ovlgrptbl is a null terminator.  */
+  bfd_put_16 (elf->dynobj, 0,
+	      group_table_sec->contents + ((max_group + 2) * 2));
+  /* There might also be some space after .ovlgrptbl to bring the
+     subsequent multigroup table into alignment. Fill that with nulls
+     too.  */
+  unsigned i;
+  for (i = (max_group + 3) * 2; i < ovl->ovl_group_table_size; i++)
+    bfd_put_8 (elf->dynobj, 0, group_table_sec->contents + i);
+
+  return true;
+}
+
+/* Build a version of the current ovl section based on what is currently loaded.  */
+
+static bool
+build_current_ovl_section (struct bfd_link_info *info, void **data)
+{
+  bfd *output_bfd = info->output_bfd;
+  asection *sec = bfd_get_section_by_name (output_bfd, ".ovlgrps");
+  BFD_ASSERT (sec != NULL);
+
+  /* Nasty hack: When the .ovlgrps output section is created it
+     is created with its flags initialized to the same flags as the
+     last constituent input section. Because the last input section
+     is a dynamic section, the output section erroneously picks up the
+     SEC_IN_MEMORY flag which causes bfd_get_section_contents to
+     fail when it tries to read from the "contents" of .ovlgrps.  */
+  sec->flags &= ~SEC_IN_MEMORY;
+
+  void *section_data = bfd_malloc (sec->size);
+  BFD_ASSERT (section_data != NULL);
+  *data = section_data;
+  /* Start by loading the entire contents of this section, this will cover non
+     dynamic sections.  */
+  bool res =
+    bfd_get_section_contents (output_bfd, sec, section_data, 0, sec->size);
+  BFD_ASSERT (res == true);
+
+  /* Look at all the input sections, if the output section matches this, then
+     load the contents into that section.  */
+  bfd *ibfd;
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      asection *isec;
+      for (isec = ibfd->sections; isec != NULL; isec = isec->next)
+	{
+	  if (isec->contents != NULL && isec->output_section == sec)
+	    memcpy (section_data + isec->output_offset, isec->contents,
+		    isec->size);
+	}
+    }
+
+  return true;
+}
+
+/* Fill in duplicates for multigroup overlay sections.  */
+static bool
+fill_multigrp_duplicates (struct elf_link_hash_table *elf,
+			  struct riscv_elf_overlay_link_hash_table *ovl,
+			  struct bfd_link_info *info)
+{
+  bfd *ibfd;
+  unsigned char *current_data;
+  build_current_ovl_section (info, (void *) &current_data);
+  BFD_ASSERT (current_data != NULL);
+
+  bfd *output_bfd = info->output_bfd;
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      asection *isec;
+      for (isec = ibfd->sections; isec != NULL; isec = isec->next)
+	{
+	  if (!strncmp (isec->name, OVL_INPUT_SEC_PREFIX,
+			strlen (OVL_INPUT_SEC_PREFIX)))
+	    {
+	      struct ovl_func_hash_entry *sym_groups =
+		ovl_func_hash_lookup (&ovl->ovl_func_table,
+				      isec->name +
+				      strlen (OVL_INPUT_SEC_PREFIX),
+				      false, false);
+	      if (sym_groups == NULL)
+		continue;
+
+	      struct ovl_func_group_info *func_group_info;
+	      for (func_group_info = sym_groups->groups->next;
+		   func_group_info != NULL;
+		   func_group_info = func_group_info->next)
+		{
+		  const char *sym_name;
+		  asection *sym_duplicate_sec;
+
+		  sym_name = isec->name + strlen (OVL_INPUT_SEC_PREFIX);
+
+		  if (riscv_overlay_debug)
+		    fprintf (stderr, "- Copy of %s in group %lu\n",
+			     sym_name, func_group_info->id);
+		  sym_duplicate_sec =
+		    get_ovl_duplicate_section (elf->dynobj,
+					       func_group_info->id, sym_name);
+		  BFD_ASSERT (sym_duplicate_sec != NULL);
+
+		  /* Nasty hack: When the .ovlgrps output section is created it
+		     is created with its flags initialized to the same flags as the
+		     last constituent input section. Because the last input section
+		     is a dynamic section, the output section erroneously picks up the
+		     SEC_IN_MEMORY flag which causes bfd_get_section_contents to
+		     fail when it tries to read from the "contents" of .ovlgrps.  */
+		  isec->output_section->flags &= ~SEC_IN_MEMORY;
+
+		  bfd_get_section_contents (output_bfd, isec->output_section,
+					    sym_duplicate_sec->contents,
+					    isec->output_offset,
+					    sym_duplicate_sec->size);
+		}
+	    }
+	}
+    }
+  free (current_data);
+
+  return true;
+}
+
+/* Custom CRC implementation, derived from the implementation in libiberty.  */
+
+static unsigned int
+bitrev32 (unsigned int x)
+{
+  x = (x & 0x55555555) << 1 | (x & 0xaaaaaaaa) >> 1;
+  x = (x & 0x33333333) << 2 | (x & 0xcccccccc) >> 2;
+  x = (x & 0x0f0f0f0f) << 4 | (x & 0xf0f0f0f0) >> 4;
+  x = (x & 0x00ff00ff) << 8 | (x & 0xff00ff00) >> 8;
+  x = (x & 0x0000ffff) << 16 | (x & 0xffff0000) >> 16;
+  return x;
+}
+
+static unsigned char
+bitrev8 (unsigned char x)
+{
+  x = (x & 0x55) << 1 | (x & 0xaa) >> 1;
+  x = (x & 0x33) << 2 | (x & 0xcc) >> 2;
+  x = (x & 0x0f) << 4 | (x & 0xf0) >> 4;
+  return x;
+}
+
+/* Array for holding the table for the CRC.  */
+static unsigned int crc32_table[256];
+
+/* Update the crc32_table for a given polynomial.  */
+
+static void
+update_crc32_table (unsigned poly)
+{
+  unsigned int i, j, k;
+  unsigned int c;
+
+  for (k = 0; k < 256; k++)
+    {
+      i = k;
+      for (c = i << 24, j = 8; j > 0; --j)
+	c = c & 0x80000000 ? (c << 1) ^ poly : (c << 1);
+      crc32_table[k] = c;
+    }
+}
+
+/* This differs from the "standard" CRC-32 algorithm in that the values
+   are not reflected, and there is no final XOR value.  These differences
+   make it easy to compose the values of multiple blocks.  */
+
+static unsigned int
+xcrc32_custom (const unsigned char *buf, int len, unsigned int init,
+	       unsigned int poly, unsigned int xorout, int refin, int refout)
+{
+  static unsigned int last_poly = 0;
+
+  if (last_poly != poly)
+    {
+      update_crc32_table (poly);
+      last_poly = poly;
+    }
+
+  unsigned int crc = init;
+  while (len--)
+    {
+      if (refin)
+	crc =
+	  (crc << 8) ^ crc32_table[((crc >> 24) ^ (bitrev8 (*buf))) & 255];
+      else
+	crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buf) & 255];
+      buf++;
+    }
+
+  if (refout)
+    crc = bitrev32 (crc);
+
+  crc ^= xorout;
+
+  return crc;
+}
+
+struct emit_ovl_padding_and_crc_args
+{
+  struct bfd_link_info *info;
+  void *section_data;
+};
+
+/* Calculate and insert the overlay section padding and CRC.  */
+static bool
+emit_ovl_padding_and_crc_entry (struct ovl_group_list_entry *entry,
+				int group, void *data)
+{
+  struct emit_ovl_padding_and_crc_args *args = data;
+
+  if (riscv_overlay_debug)
+    fprintf (stderr, "Group %d*: ", group);
+
+  if (entry->group_size == 0)
+    {
+      if (riscv_overlay_debug)
+	fprintf (stderr, "(empty, skipping)\n");
+      return true;
+    }
+
+  BFD_ASSERT (args->section_data != NULL);
+
+  /* Load the padding section.  */
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (args->info);
+  asection *padding_sec = get_ovl_padding_section (htab->elf.dynobj, group);
+  BFD_ASSERT (padding_sec != NULL);
+
+  /* Emit the padding into the padding section. This is copied to the cached
+     data so a CRC can be run over the entire block in one pass.  */
+  for (bfd_vma offs = 0; offs < padding_sec->size; offs += 2)
+    bfd_put_16 (htab->elf.dynobj, group, padding_sec->contents + offs);
+  memcpy (args->section_data + padding_sec->output_offset,
+	  padding_sec->contents, padding_sec->size);
+
+  /* Calculate the CRC of the group.  */
+  unsigned int crc;
+
+  /* This shouldn't happen, except in the case of something going wrong
+     previously. Warn if this is the case.  */
+  BFD_ASSERT ((entry->ovlgrpdata_offset + entry->padded_group_size) ==
+	      (padding_sec->output_offset + padding_sec->size));
+  if ((entry->ovlgrpdata_offset + entry->padded_group_size) !=
+      (padding_sec->output_offset + padding_sec->size))
+    crc = 0;
+  else
+    crc = xcrc32_custom (args->section_data + entry->ovlgrpdata_offset,
+			 entry->padded_group_size - OVL_CRC_SZ,
+			 riscv_crc_init, riscv_crc_poly, riscv_crc_xorout,
+			 riscv_crc_refin, riscv_crc_refout);
+
+  /* Put the 32-bit CRC at the end after the padding.  */
+  bfd_put_32 (args->info->output_bfd, crc,
+	      padding_sec->contents + padding_sec->size - OVL_CRC_SZ);
+
+  /* Store the CRC for printing in the mapfile.  */
+  entry->crc = crc;
+
+  if (riscv_overlay_debug)
+    fprintf (stderr, "%x\n", crc);
+  return true;
+}
+
+/* Calculate and insert all overlay group sections CRC values.  */
+static void
+emit_ovl_padding_and_crc (struct ovl_group_list *list,
+			  struct bfd_link_info *info)
+{
+  if (riscv_overlay_debug)
+    fprintf (stderr, "Calculating CRCs\n================\n");
+
+  struct emit_ovl_padding_and_crc_args args;
+  args.info = info;
+  build_current_ovl_section (args.info, &args.section_data);
+
+  BFD_ASSERT (args.section_data != NULL);
+  ovl_group_list_traverse (list, emit_ovl_padding_and_crc_entry, &args);
+  free (args.section_data);
+}
+
+/* Finish overlay sections by filling in:
+    * .ovlplt entries
+    * .ovlgrptbl
+    * duplicates of multigroup overlay sections
+    * overlay section padding and crc
+*/
+bool
+riscv_overlay_finish_sections (struct elf_link_hash_table *elf,
+			       struct riscv_elf_overlay_link_hash_table *ovl,
+			       struct bfd_link_info *info)
+{
+  bfd *output_bfd = info->output_bfd;
+  if (!fill_ovlplt_sections (output_bfd, ovl))
+    return false;
+  if (!fill_ovlgrptbl (elf, ovl))
+    return false;
+  if (!fill_multigrp_duplicates (elf, ovl, info))
+    return false;
+  /* Now all functions have been copied, calculate and insert the overlay
+     section padding and CRC.  */
+  emit_ovl_padding_and_crc (&ovl->ovl_group_list, info);
+
+  return true;
+}
+
+/* If deleted sec is in any overlay group, then the corresponding padding
+   sections for those groups need to be resized, as do any duplicates.  */
+void
+riscv_overlay_relax_delete_bytes (asection * sec, size_t count,
+				  struct bfd_link_info *link_info)
+{
+  if (!strncmp
+      (sec->name, OVL_INPUT_SEC_PREFIX, strlen (OVL_INPUT_SEC_PREFIX)))
+    {
+      const char *sym_name = sec->name + strlen (OVL_INPUT_SEC_PREFIX);
+      struct riscv_elf_link_hash_table *htab =
+	riscv_elf_hash_table (link_info);
+
+      struct ovl_func_hash_entry *func_entry =
+	ovl_func_hash_lookup (&htab->ovl.ovl_func_table, sym_name, false,
+			      false);
+      if (func_entry)
+	{
+	  struct ovl_func_group_info *groups;
+	  /* Get the accompanying padding sections and increase their size.  */
+	  for (groups = func_entry->groups; groups != NULL;
+	       groups = groups->next)
+	    {
+	      asection *group_padding_section =
+		get_ovl_padding_section (htab->elf.dynobj, groups->id);
+	      if (group_padding_section)
+		group_padding_section->size += count;
+	    }
+
+	  /* Get any duplicated sections and reduce their size.  */
+	  for (groups = func_entry->groups; groups != NULL;
+	       groups = groups->next)
+	    {
+	      asection *sym_duplicate_section =
+		get_ovl_duplicate_section (htab->elf.dynobj, groups->id,
+					   sym_name);
+	      if (sym_duplicate_section)
+		sym_duplicate_section->size -= count;
+	    }
+	}
+    }
+}
+
+/* Create the .ovlplt section, and .rela.ovlplt.  */
+
+static bool
+create_ovlplt_section (bfd * abfd,
+		       struct riscv_elf_overlay_link_hash_table *ovl)
+{
+  flagword flags;
+  asection *s;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+  /* This function may be called more than once.  */
+  if (ovl->sovlplt != NULL)
+    return true;
+
+  flags = bed->dynamic_sec_flags | SEC_READONLY | SEC_CODE;
+
+  s = bfd_make_section_anyway_with_flags (abfd, ".ovlplt", flags);
+  if (s == NULL || !bfd_set_section_alignment (s, bed->s->log_file_align))
+    return false;
+  /* The size of the overlay plt section is calculated later.  */
+  s->size = 0;
+  ovl->sovlplt = s;
+
+  return true;
+}
+
+bool
+riscv_elf_overlay_check_relocation (
+    bfd * abfd,
+    struct bfd_link_info *info,
+    struct riscv_elf_link_hash_entry_overlay *eh,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    int r_type)
+{
+  switch (r_type)
+    {
+    default:
+      return false;
+    case R_RISCV_OVLPLT_LO12_I:
+    case R_RISCV_OVLPLT_HI20:
+    case R_RISCV_OVLPLT32:
+      /* Create the overlay PLT section if it doesn't already exist.  */
+      if (!ovl->sovlplt)
+	{
+	  if (!create_ovlplt_section (abfd, ovl))
+	    return false;
+	  /* Enables analysis of dynamic sections. This is needed so
+	     that we can resize the overlay PLT section after we
+	     know how many entries will be needed.  */
+	  info->dynamic = 1;
+	}
+
+      if (eh && !eh->needs_ovlplt_entry)
+	{
+	  eh->needs_ovlplt_entry = true;
+	  ovl->sovlplt->size += OVLPLT_ENTRY_SIZE;
+	}
+      /* fallthrough */
+
+    case R_RISCV_OVLTOK_LO12_I:
+    case R_RISCV_OVLTOK_HI20:
+    case R_RISCV_OVLTOK32:
+      /* Enable analysis of dynamic sections since the size of the
+         created sections needs to be calculated later.  */
+      info->dynamic = 1;
+      ovl->overlay_enabled = 1;
+      if (eh)
+	eh->needs_overlay_group = true;
+
+      return true;
+    }
+  return false;
+}
+
+/* Parse a line from the overlay grouping csv and insert into hash table.  */
+
+static bool
+parse_grouping_line (char *line, struct bfd_hash_table *ovl_func_table,
+		     struct ovl_group_list *ovl_group_list,
+		     struct bfd_link_info *info)
+{
+  char *group_str, *endptr;
+  char *func = NULL;
+  char *reason = NULL;
+  long int group;
+  int group_cnt = 0;
+
+  /* Parse function name.  */
+  func = strtok (line, ",");
+
+  /* Skip functions which have been garbage collected, they should not appear
+     in any group.  */
+  if (!ovl_enable_grouping_for_func (func, info, &reason))
+    return true;
+
+  /* Parse group ids.  */
+  while ((group_str = strtok (NULL, ",")) != NULL)
+    {
+      group = strtol (group_str, &endptr, 10);
+      if (*endptr != 0)
+	info->callbacks->einfo (_
+				("%F%P: Malformed group id '%s` in overlay grouping file\n"),
+				group_str);
+
+      if (group == 0)
+	info->
+	  callbacks->einfo (_
+			    ("%F%P: Invalid group id '0` in overlay grouping file\n"));
+
+      if (group < 0)
+	info->callbacks->einfo (_
+				("%F%P: Invalid group id '%ld` in overlay grouping file: "
+				 "group numbers cannot be negative.\n"),
+				group);
+
+      if (group > OVL_MAX_GROUP_ID)
+	info->callbacks->einfo (_
+				("%F%P: Invalid group id '%ld` in overlay grouping file: "
+				 "group number greater than maximum (%ld).\n"),
+				group, OVL_MAX_GROUP_ID);
+
+      ovl_update_func (ovl_func_table, func, (bfd_vma) group);
+      ovl_update_group (ovl_group_list, (bfd_vma) group, func);
+      group_cnt++;
+    }
+
+  if (group_cnt == 0)
+    info->
+      callbacks->einfo (_
+			("%F%P: No groups found for '%s` in overlay grouping file\n"),
+			func);
+
+  return true;
+}
+
+/* Parse a csv containg grouping information for each function.  */
+
+static bool
+parse_grouping_file (FILE * f,
+		     struct bfd_hash_table *ovl_func_table,
+		     struct ovl_group_list *ovl_group_list,
+		     struct bfd_link_info *info)
+{
+  int c;
+  int i = 0;
+  int BUFSIZE = 1024;
+  char *line = bfd_zmalloc (BUFSIZE);
+  bool retcode;
+
+  while (1)
+    {
+      c = fgetc (f);
+
+      if (i >= BUFSIZE)
+	{
+	  /* Line too long for buffer, increase buffer size.  */
+	  BUFSIZE *= 2;
+	  line = bfd_realloc (line, BUFSIZE);
+	}
+
+      if (c == '\n' || c == '\r' || c == EOF)
+	{
+	  /* Parse line if not blank.  */
+	  if (i > 0)
+	    {
+	      line[i++] = '\0';
+	      if (!parse_grouping_line
+		  (line, ovl_func_table, ovl_group_list, info))
+		goto return_error;
+
+	      /* Line parsed, goto start of buffer.  */
+	      i = 0;
+	    }
+	  if (c == EOF)
+	    break;
+	}
+      else
+	line[i++] = c;
+    }
+
+  retcode = true;
+  goto done;
+
+return_error:
+  retcode = false;
+
+done:
+  free (line);
+  return retcode;
+}
+
+static void
+run_grouping_tool (struct riscv_elf_link_hash_table *htab,
+		   struct bfd_link_info *info, unsigned sizeof_external_sym)
+{
+  bfd *ibfd;
+  int n_fixed_args, n_extra_args, n_total_args;
+  int grouping_tool_argc;
+  char **grouping_tool_argv;
+  int arg_len;
+  const char *arg_start;
+
+  /* Check that the --grouping-tool option was provided.  */
+  if (!riscv_grouping_tool)
+    info->callbacks->einfo (_
+			    ("%F%P: '--grouping-tool` option not provided, so the grouping tool "
+			     "cannot be called\n"));
+
+  /* Setup the arguments for the grouping tool.  */
+  /* Arguments in riscv_grouping_tool_args are semicolon separated, so there
+     will be one more argument than the number of semicolons.  */
+  n_extra_args = 0;
+  if (riscv_grouping_tool_args)
+    {
+      n_extra_args = 1;
+      for (int i = 0; i < (int) strlen (riscv_grouping_tool_args); i++)
+	if (riscv_grouping_tool_args[i] == ';')
+	  n_extra_args++;
+    }
+
+  /* Build argv for the grouping tool. */
+  n_fixed_args = 2;
+  n_total_args = n_fixed_args + n_extra_args;
+  grouping_tool_argv =
+    bfd_zmalloc (n_total_args * sizeof (*grouping_tool_argv));
+
+  /* Copy the command word to the first argument.  */
+  arg_len = strlen (riscv_grouping_tool);
+  grouping_tool_argv[0] = bfd_zmalloc (arg_len + 1);
+  strncpy (grouping_tool_argv[0], riscv_grouping_tool, arg_len);
+
+  /* Copy the extra arguments from riscv_group_tools_args, each argument
+     is separated by a ';' */
+  arg_start = riscv_grouping_tool_args;
+  for (int i = 0; i < n_extra_args; i++)
+    {
+      char *tmp_arg;
+      const char *arg_end = strchr (arg_start, ';');
+
+      if (arg_end == NULL)
+	arg_len = strlen (arg_start);
+      else
+	arg_len = arg_end - arg_start;
+
+      tmp_arg = bfd_zmalloc (arg_len + 1);
+      strncpy (tmp_arg, arg_start, arg_len);
+      tmp_arg[arg_len] = '\0';
+
+      grouping_tool_argv[i + 1] = tmp_arg;
+      arg_start = arg_end + 1;
+    }
+
+  /* Null terminate the argv list.  */
+  grouping_tool_argc = 1 + n_extra_args;
+  grouping_tool_argv[grouping_tool_argc] = NULL;
+  BFD_ASSERT (grouping_tool_argc < n_total_args);
+
+  /* Search for the input and output filenames in the list of arguments
+     provided. These arguments will also be forwarded as-is to the
+     grouping tool.  */
+  int in_file_arg_index = 0;
+  int out_file_arg_index = 0;
+  for (int i = 0; i < grouping_tool_argc; i++)
+    {
+      if (!strcmp (grouping_tool_argv[i], "--in-file"))
+	in_file_arg_index = i + 1;
+      else if (!strcmp (grouping_tool_argv[i], "--out-file"))
+	out_file_arg_index = i + 1;
+    }
+  /* Check that both --in-file and --out-file options were provided, and
+     they were followed by file names.  */
+  if (in_file_arg_index == grouping_tool_argc)
+    info->callbacks->einfo (_
+			    ("%F%P: Missing file name for '--in-file` option to "
+			     "'--grouping-tool-args`\n"));
+
+  if (out_file_arg_index == grouping_tool_argc)
+    info->callbacks->einfo (_
+			    ("%F%P: Missing file name for '--out-file` option to "
+			     "'--grouping-tool-args`\n"));
+
+  if (in_file_arg_index == 0)
+    info->callbacks->einfo (_
+			    ("%F%P: No option '--in-file` provided to '--grouping-tool-args`\n"));
+
+  if (out_file_arg_index == 0)
+    info->callbacks->einfo (_
+			    ("%F%P: No option '--out-file` provided to '--grouping-tool-args`\n"));
+
+  char *grouping_tool_in_filename = grouping_tool_argv[in_file_arg_index];
+  char *grouping_tool_out_filename = grouping_tool_argv[out_file_arg_index];
+
+  FILE *grouping_tool_file = fopen (grouping_tool_in_filename, FOPEN_WT);
+  if (!grouping_tool_file)
+    info->callbacks->einfo (_
+			    ("%F%P: Could not open file '%s` to write input for grouping tool.\n"),
+			    grouping_tool_in_filename);
+
+  BFD_ASSERT (grouping_tool_file != NULL);
+
+  /* Emit all of the symbols which need to be grouped, plus their
+     sizes to the input .csv file for the grouping tool.  */
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      unsigned int i, symcount;
+      Elf_Internal_Shdr *symtab_hdr;
+      struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
+
+      if (!is_riscv_elf (ibfd))
+	continue;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      symcount = ((symtab_hdr->sh_size / sizeof_external_sym)
+		  - symtab_hdr->sh_info);
+
+      for (i = 0; i < symcount; i++)
+	{
+	  struct riscv_elf_link_hash_entry *eh =
+	    (struct riscv_elf_link_hash_entry *) sym_hashes[i];
+	  asection *sec = eh->elf.root.u.def.section;
+
+	  if (!eh->ovl.needs_overlay_group)
+	    continue;
+
+	  /* Skip this symbols if it's not a definition. This avoids
+	     accidentally assigning the same symbol more than once. */
+	  if (eh->elf.root.type != bfd_link_hash_defined
+	      && eh->elf.root.type != bfd_link_hash_defweak)
+	    continue;
+
+	  /* Also skip the symbol if it's already had its overlay
+	     group assigned.  */
+	  if (eh->ovl.overlay_groups_resolved == true)
+	    continue;
+	  else
+	    eh->ovl.overlay_groups_resolved = true;
+
+	  /* A symbol which needs an overlay group will be in a section with
+	     a name of the format .ovlinput.<symbol name>.  */
+	  BFD_ASSERT (!strncmp (sec->name, OVL_INPUT_SEC_PREFIX,
+				strlen (OVL_INPUT_SEC_PREFIX)));
+	  const char *sym_name = sec->name + strlen (OVL_INPUT_SEC_PREFIX);
+
+	  fprintf (grouping_tool_file, "%s,%lu\n", sym_name, sec->size);
+	}
+    }
+  fclose (grouping_tool_file);
+
+  /* call the grouping tool with the appropriate arguments.  */
+  /* NOTE: The documentation for pex_one says that the flags are restricted
+     to only PEX_SEARCH, PEX_STDERR_TO_STDOUT and PEX_BINARY_OUTPUT, and
+     that the output filename (outname) is interpreted as if PEX_LAST
+     were set. However if this were the case then an output filename of
+     NULL (as below) should cause the output to go to stdout, which it
+     doesn't. In order to get the output to actually go to stdout we must
+     also specify PEX_LAST in the flag field.  */
+  int status, err;
+  const char *errmsg = pex_one (PEX_LAST | PEX_SEARCH,
+				grouping_tool_argv[0],
+				grouping_tool_argv,
+				"grouping tool", NULL, NULL,
+				&status, &err);
+
+  if (errmsg == NULL)
+    {
+      /* Populate the tables based on the output from the grouping tool.  */
+      bool ret;
+      FILE *grouping_tool_out_file =
+	fopen (grouping_tool_out_filename, FOPEN_RT);
+      if (!grouping_tool_out_file)
+	info->callbacks->einfo (_
+				("%F%P: Could not open file '%s` to read output from grouping tool\n"),
+				grouping_tool_out_filename);
+
+      ret = parse_grouping_file (grouping_tool_out_file,
+				 &htab->ovl.ovl_func_table,
+				 &htab->ovl.ovl_group_list, info);
+      if (!ret)
+	info->callbacks->einfo (_
+				("%F%P: Failed to create overlay grouping table from groupings "
+				 "returned from grouping tool.\n"));
+
+      htab->ovl.ovl_tables_populated = true;
+    }
+  else
+    info->callbacks->einfo (_("%F%P: Failed to call grouping tool '%s`\n"),
+			    grouping_tool_argv[0]);
+}
+
+/* Put any overlay function that has not been assigned a group into its own
+   group.  */
+
+static void
+autogroup_assign (struct riscv_elf_link_hash_table *htab,
+		  struct bfd_link_info *info)
+{
+  bfd *ibfd;
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      if (!is_riscv_elf (ibfd))
+	continue;
+
+      unsigned next_empty_group;
+      if (riscv_ovl_first_group_number != 0)
+	next_empty_group = riscv_ovl_first_group_number;
+      else
+	next_empty_group = OVL_DEFAULT_FIRST_FREE_GROUP;
+
+      for (asection * sec = ibfd->sections; sec != NULL; sec = sec->next)
+	{
+	  if (strncmp
+	      (sec->name, OVL_INPUT_SEC_PREFIX,
+	       strlen (OVL_INPUT_SEC_PREFIX)) != 0)
+	    continue;
+
+	  const char *sym_name = sec->name + strlen (OVL_INPUT_SEC_PREFIX);
+
+	  /* Skip the symbol if it's already been assigned a group - either
+	     because it was assigned by the grouping file or grouping tool,
+	     or because it has just been auto assigned.  */
+	  struct ovl_func_hash_entry *sym_groups =
+	    ovl_func_hash_lookup (&htab->ovl.ovl_func_table,
+				  sym_name, false, false);
+	  if (sym_groups != NULL)
+	    continue;
+
+	  /* Skip the symbol if it has been garbage collected.  */
+	  if (overlay_use_gcmark && !sec->gc_mark)
+	    continue;
+
+	  /* Find the next group which is empty, starting from the
+	     current value in 'next_empty_group'.  */
+	  for (;; next_empty_group++)
+	    {
+	      /* Also look it up in the group list.  */
+	      struct ovl_group_list_entry *group_list_entry =
+		ovl_group_list_lookup (&htab->ovl.ovl_group_list,
+				       next_empty_group, false);
+	      if (group_list_entry == NULL)
+		break;
+	    }
+
+	  ovl_update_func (&htab->ovl.ovl_func_table, sym_name,
+			   next_empty_group);
+	  ovl_update_group (&htab->ovl.ovl_group_list, next_empty_group,
+			    sym_name);
+
+	  /* The search for the next empty group will start from the group
+	     number after this one.  */
+	  next_empty_group += 1;
+	}
+      htab->ovl.ovl_tables_populated = true;
+    }
+}
+
+/* Assign each overlay symbol to one or more groups.  */
+static void
+assign_groups (struct riscv_elf_link_hash_table *htab,
+	       struct bfd_link_info *info, unsigned sizeof_external_sym)
+{
+  /* If a grouping file has been provided, populate the tables with valid
+     entries. */
+  if (!htab->ovl.ovl_tables_populated && riscv_grouping_file != NULL)
+    {
+      bool ret;
+      ret =
+	parse_grouping_file (riscv_grouping_file, &htab->ovl.ovl_func_table,
+			     &htab->ovl.ovl_group_list, info);
+      if (!ret)
+	info->
+	  callbacks->einfo (_
+			    ("%F%P: Failed to parse overlay grouping file\n"));
+
+      htab->ovl.ovl_tables_populated = true;
+      fclose (riscv_grouping_file);
+    }
+
+  /* If a grouping has not yet been specified, then try calling the
+     grouping tool.  */
+  if (!htab->ovl.ovl_tables_populated && riscv_use_grouping_tool)
+    run_grouping_tool (htab, info, sizeof_external_sym);
+
+  /* If there are any symbols which weren't grouped by the grouping file
+     or grouping tool, then those symbols need to be put into a group on
+     their own.  */
+  autogroup_assign (htab, info);
+}
+
+/* Build up a token for the overlay system.  */
+static bfd_vma
+ovltoken (bfd_vma multigroup, bfd_vma from_plt, bfd_vma func_off,
+	  bfd_vma group_id)
+{
+  BFD_ASSERT (multigroup <= 1);
+  BFD_ASSERT (from_plt <= 1);
+  BFD_ASSERT (func_off <= 1023);
+  BFD_ASSERT (group_id <= 65535);
+
+  /* +--------+------+----------+----------+---------+---------+---------+
+     |  31    |30-29 |   28     |    27    |  26-17  |   16-1  |    0    |
+     +--------+------+----------+----------+---------+---------+---------+
+     | Multi- | Heap | Reserved |   PLT    |Function | Overlay | Overlay |
+     | group  |  ID  |          |          |Offset   |  Group  | Address |
+     | Token  |      |          |          |         |   ID    |  Token  |
+     +--------+------+----------+----------+---------+---------+---------+ */
+  bfd_vma token = 0;
+  token |= (multigroup & 0x1) << 31;	/* Multi-group token.  */
+  token |= (0 & 0x3) << 29;	/* Heap ID.  */
+  token |= (from_plt & 0x1) << 27;	/* From PLT.  */
+  token |= (func_off & 0x3ff) << 17;	/* Function Offset.  */
+  token |= (group_id & 0xffff) << 1;	/* Overlay Group ID.  */
+  token |= (1 & 0x1) << 0;	/* Overlay Address Token.  */
+  return token;
+}
+
+/* Return the relocation value for the token in the overlay system.  */
+
+static bfd_vma
+ovloff (struct bfd_link_info *info, bfd_vma from_plt,
+	struct elf_link_hash_entry *entry)
+{
+  struct riscv_elf_link_hash_table *htab;
+  htab = riscv_elf_hash_table (info);
+
+  /* Return -1 if there is an error with this link.  */
+  if (!entry)
+    return 0xffffffff;
+
+  /* Get the group(s) for the symbol.  */
+  struct ovl_func_hash_entry *func_groups =
+    ovl_func_hash_lookup (&htab->ovl.ovl_func_table, entry->root.root.string,
+			  false, false);
+  BFD_ASSERT (func_groups != NULL);
+
+  if (func_groups->multigroup == false)
+    {
+      struct ovl_group_list_entry *group_list_entry =
+	ovl_group_list_lookup (&htab->ovl.ovl_group_list,
+			       func_groups->groups->id, false);
+
+      bfd *ibfd;
+
+      /* First find the input section for the first function in this
+         group.  */
+      asection *group_first_input_sec = NULL;
+      for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+	{
+	  group_first_input_sec =
+	    get_ovl_duplicate_section (ibfd, func_groups->groups->id,
+				       group_list_entry->first_func);
+	  if (group_first_input_sec)
+	    break;
+	}
+      if (!group_first_input_sec)
+	{
+	  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+	    {
+	      group_first_input_sec =
+		get_ovl_input_section (ibfd, group_list_entry->first_func);
+	      if (group_first_input_sec)
+		break;
+	    }
+	}
+
+      /* Now find the input section for the target function in this
+         group.  */
+      asection *target_sym_input_sec = NULL;
+      for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+	{
+	  target_sym_input_sec =
+	    get_ovl_input_section (ibfd, entry->root.root.string);
+	  if (target_sym_input_sec)
+	    break;
+	}
+      BFD_ASSERT (group_first_input_sec != NULL);
+      BFD_ASSERT (target_sym_input_sec != NULL);
+
+      bfd_vma offset_into_group = target_sym_input_sec->output_offset
+	- group_first_input_sec->output_offset;
+      func_groups->groups->processed_offset = offset_into_group;
+
+      if (riscv_overlay_debug)
+	{
+	  fprintf (stderr, "group_first_input_sec->name: %s\n",
+		   group_first_input_sec->name);
+	  fprintf (stderr, "target_sym_input_sec->name:  %s\n",
+		   target_sym_input_sec->name);
+
+	  fprintf (stderr, "group_first_input_sec->output_offset: %lu\n",
+		   group_first_input_sec->output_offset);
+	  fprintf (stderr, "target_sym_input_sec->output_offset:  %lu\n",
+		   target_sym_input_sec->output_offset);
+
+	  fprintf (stderr, "OFFSET INTO GROUP: %lu\n", offset_into_group);
+	}
+      BFD_ASSERT ((offset_into_group % 4) == 0);
+
+      return ovltoken (0, from_plt, offset_into_group / 4,
+		       func_groups->groups->id);
+    }
+  else
+    {
+      asection *group_table_sec =
+	bfd_get_section_by_name (htab->elf.dynobj, OVL_GROUPTABLES_SEC_NAME);
+
+      /* The multigroup table is immediately after the group table. So add
+         the group table size to the offset.  */
+      bfd_byte *loc = group_table_sec->contents
+	+ htab->ovl.ovl_group_table_size + func_groups->multigroup_offset;
+
+      /* Check if the entry in the multigroup table needs to be filled in.  */
+      if (bfd_get_32 (htab->elf.dynobj, loc) == 0)
+	{
+	  /* Create the multigroup table entry, filling it with tokens
+	     for the function in each of the groups it is contained within.  */
+	  struct ovl_func_group_info *func_group_info;
+	  for (func_group_info = func_groups->groups; func_group_info != NULL;
+	       func_group_info = func_group_info->next)
+	    {
+	      bfd_vma token;
+	      struct ovl_group_list_entry *group_list_entry =
+		ovl_group_list_lookup (&htab->ovl.ovl_group_list,
+				       func_group_info->id, false);
+	      bfd *ibfd;
+
+	      /* First find the input section for the first function in this
+	         group.  */
+	      asection *group_first_input_sec = NULL;
+	      for (ibfd = info->input_bfds; ibfd != NULL;
+		   ibfd = ibfd->link.next)
+		{
+		  group_first_input_sec =
+		    get_ovl_duplicate_section (ibfd,
+					       func_group_info->id,
+					       group_list_entry->first_func);
+		  if (group_first_input_sec)
+		    break;
+		}
+	      if (!group_first_input_sec)
+		{
+		  for (ibfd = info->input_bfds; ibfd != NULL;
+		       ibfd = ibfd->link.next)
+		    {
+		      group_first_input_sec =
+			get_ovl_input_section (ibfd,
+					       group_list_entry->first_func);
+		      if (group_first_input_sec)
+			break;
+		    }
+		}
+
+	      /* Now find the input section for the target function in this
+	         group.  */
+	      asection *target_sym_input_sec = NULL;
+	      for (ibfd = info->input_bfds; ibfd != NULL;
+		   ibfd = ibfd->link.next)
+		{
+		  target_sym_input_sec =
+		    get_ovl_duplicate_section (ibfd,
+					       func_group_info->id,
+					       entry->root.root.string);
+		  if (target_sym_input_sec)
+		    break;
+		}
+	      if (!target_sym_input_sec)
+		{
+		  for (ibfd = info->input_bfds; ibfd != NULL;
+		       ibfd = ibfd->link.next)
+		    {
+		      target_sym_input_sec =
+			get_ovl_input_section (ibfd, entry->root.root.string);
+		      if (target_sym_input_sec)
+			break;
+		    }
+		}
+	      BFD_ASSERT (group_first_input_sec != NULL);
+	      BFD_ASSERT (target_sym_input_sec != NULL);
+
+	      bfd_vma offset_into_group = target_sym_input_sec->output_offset
+		- group_first_input_sec->output_offset;
+	      func_group_info->processed_offset = offset_into_group;
+
+	      if (riscv_overlay_debug)
+		{
+		  fprintf (stderr, "group_first_input_sec->name: %s\n",
+			   group_first_input_sec->name);
+		  fprintf (stderr, "target_sym_input_sec->name:  %s\n",
+			   target_sym_input_sec->name);
+
+		  fprintf (stderr,
+			   "group_first_input_sec->output_offset: %lu\n",
+			   group_first_input_sec->output_offset);
+		  fprintf (stderr,
+			   "target_sym_input_sec->output_offset:  %lu\n",
+			   target_sym_input_sec->output_offset);
+
+		  fprintf (stderr, "OFFSET INTO GROUP: %lu\n",
+			   offset_into_group);
+		}
+	      BFD_ASSERT ((offset_into_group % 4) == 0);
+
+	      token = ovltoken (0, 0, offset_into_group / 4,
+				func_group_info->id);
+
+	      bfd_put_32 (htab->elf.dynobj, token, loc);
+	      loc += OVL_MULTIGROUP_ENTRY_SIZE;
+	    }
+	  /* The list of tokens is NULL terminated.  */
+	  bfd_put_32 (htab->elf.dynobj, 0, loc);
+	}
+
+      /* Create the token referring to the multigroup.  */
+      bfd_vma multigroup_id;
+      multigroup_id =
+	func_groups->multigroup_offset / OVL_MULTIGROUP_ENTRY_SIZE;
+      func_groups->multigroup_token =
+	ovltoken (1, from_plt, 0, multigroup_id);
+      return func_groups->multigroup_token;
+    }
+}
+
+bool
+riscv_overlay_handle_relocation (
+    bfd_vma * relocation,
+    bfd * output_bfd,
+    struct bfd_link_info *info,
+    struct elf_link_hash_entry *h,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    Elf_Internal_Sym * sym,
+    int r_type)
+{
+  switch (r_type)
+    {
+    default:
+      return false;
+
+    case R_RISCV_OVLTOK_HI20:
+    case R_RISCV_OVLTOK_LO12_I:
+    case R_RISCV_OVLTOK32:
+      *relocation = ovloff (info, /*from_plt */ 0, h);
+      return true;
+
+    case R_RISCV_OVLPLT_HI20:
+    case R_RISCV_OVLPLT_LO12_I:
+    case R_RISCV_OVLPLT32:
+      *relocation = ovloff (info, /*from_plt */ 1, h);
+
+      BFD_ASSERT (ovl->sovlplt != NULL);
+      /* For now, each entry in the PLT is either empty, or the first
+         32-bits contains a previously encountered token value. First
+         try to find the index of an existing token in the PLT, if it
+         can't be found, append the token in the next unallocated entry
+         at the end.  */
+      bfd_vma offset;
+      bfd_vma next_ovlplt_offset = ovl->next_ovlplt_offset;
+
+      for (offset = 0; offset < next_ovlplt_offset;
+	   offset += OVLPLT_ENTRY_SIZE)
+	{
+	  bfd_vma entry = bfd_get_32 (output_bfd,
+				      ovl->sovlplt->contents + offset);
+	  if (entry == *relocation)
+	    break;
+	}
+
+      if (offset >= next_ovlplt_offset)
+	{
+	  if (h == NULL)
+	    return true;
+	  /* Store the PLT offset for this function in its metadata, this is
+	     used to print the linker map later on.  */
+	  struct ovl_func_hash_entry *func_groups =
+	    ovl_func_hash_lookup (&ovl->ovl_func_table, h->root.root.string,
+				  false, false);
+	  BFD_ASSERT (func_groups != NULL);
+	  BFD_ASSERT (ovl->sovlplt != NULL);
+	  if (func_groups == NULL || ovl->sovlplt == NULL)
+	    return true;
+	  func_groups->plt_entry = true;
+	  func_groups->plt_offset = offset;
+
+	  bfd_put_32 (output_bfd, *relocation,
+		      ovl->sovlplt->contents + offset);
+	  ovl->next_ovlplt_offset += OVLPLT_ENTRY_SIZE;
+	}
+
+      *relocation = sec_addr (ovl->sovlplt) + offset;
+
+      if (!h->def_regular)
+	{
+	  /* Mark the symbol as undefined, rather than as defined in
+	     the .plt section.  Leave the value alone.  */
+	  sym->st_shndx = SHN_UNDEF;
+	  /* If the symbol is weak, we do need to clear the value.
+	     Otherwise, the PLT entry would provide a definition for
+	     the symbol even if the symbol wasn't defined anywhere,
+	     and so the symbol would never be NULL.  */
+	  if (!h->ref_regular_nonweak)
+	    sym->st_value = 0;
+	}
+
+      return true;
+    }
+
+  return false;
+}
+
+static bool
+create_ovl_dup_symbol (struct bfd_link_info *info, const char *name,
+		       bfd_vma group, asection * s, bfd_vma size)
+{
+  char *symbol_name;
+  ssize_t symbol_len;
+  struct bfd_link_hash_entry *bfdh;
+  struct elf_link_hash_entry *elfh;
+  bool res;
+
+  symbol_len = strlen (name) + strlen ("$group") + /*group */ 10 + 1;
+  symbol_name = bfd_zmalloc (symbol_len);
+  sprintf (symbol_name, "%s$group%lu", name, group);
+
+  /* Create a new symbol */
+  bfdh = NULL;
+  res = _bfd_generic_link_add_one_symbol (info, s->owner, symbol_name,
+					  BSF_GLOBAL, s, size, NULL, true,
+					  false, &bfdh);
+  free (symbol_name);
+  if (!res)
+    return false;
+
+  /* Set ELF flags */
+  elfh = (struct elf_link_hash_entry *) bfdh;
+  elfh->type = ELF_ST_INFO (STB_GLOBAL, STT_FUNC);
+  elfh->size = size;
+  return true;
+}
+
+void
+riscv_elf_overlay_pad_groups (struct elf_link_hash_table *elf,
+			      struct riscv_elf_overlay_link_hash_table *ovl,
+			      const struct elf_backend_data *bed,
+			      struct bfd_link_info *info)
+{
+  bfd *ibfd;
+  /* For each group, create a padding section that will hold the group number
+     and SHA.  */
+  for (unsigned i = 0; i <= ovl_max_group; i++)
+    {
+      struct ovl_group_list_entry *group_list_entry =
+	ovl_group_list_lookup (&ovl->ovl_group_list, i, false);
+      if (group_list_entry)
+	{
+	  /* Get the first input section allocated to this group and set
+	     its alignment to OVL_PAGE_SIZE bytes.  */
+	  if (group_list_entry->n_functions != 0
+	      && group_list_entry->first_func)
+	    {
+	      /* First look for a duplicate, if one is not found, then it is the
+	         original verison.  */
+	      asection *isec = get_ovl_duplicate_section (elf->dynobj,
+							  i,
+							  group_list_entry->first_func);
+	      if (isec == NULL)
+		for (ibfd = info->input_bfds; isec == NULL && ibfd != NULL;
+		     ibfd = ibfd->link.next)
+		  isec =
+		    get_ovl_input_section (ibfd,
+					   group_list_entry->first_func);
+
+	      if (!isec)
+		continue;
+
+	      if (riscv_overlay_debug)
+		fprintf (stderr,
+			 "* Setting '%s` in '%s` to page byte alignment.\n",
+			 isec->name, isec->owner->filename);
+
+	      /* assign the section to the page size.  */
+	      bfd_set_section_alignment (isec, OVL_GROUP_PAGE_SIZE_POW2);
+	    }
+
+	  flagword flags;
+	  asection *s;
+	  bfd_vma padding = group_list_entry->padded_group_size -
+	    group_list_entry->group_size;
+
+	  /* It should be the case that there is always padding for the group
+	     SHA?  */
+	  BFD_ASSERT (padding > 0);
+
+	  flags = bed->dynamic_sec_flags | SEC_READONLY | SEC_CODE;
+	  s = make_ovl_padding_section (elf->dynobj, i, flags);
+
+	  if (riscv_overlay_debug)
+	    fprintf (stderr, "- Created padding section `%s` with size %lx\n",
+		     s->name, padding);
+
+	  s->size = padding;
+	  s->contents = bfd_zalloc (elf->dynobj, OVL_GROUP_PAGE_SIZE);
+	  BFD_ASSERT (s != NULL);
+	  bfd_set_section_alignment (s, 0);
+	}
+    }
+}
+
+/* Create duplicate sections for symbols in multigroups.  */
+void
+riscv_elf_overlay_duplicate_multigroup_sections (
+    struct elf_link_hash_table *elf,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    const struct elf_backend_data *bed,
+    struct bfd_link_info *info,
+    unsigned sizeof_external_sym)
+{
+  bfd *ibfd;
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      unsigned int i, symcount;
+      Elf_Internal_Shdr *symtab_hdr;
+      struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
+
+      if (!is_riscv_elf (ibfd))
+	continue;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      symcount = ((symtab_hdr->sh_size / sizeof_external_sym)
+		  - symtab_hdr->sh_info);
+
+      for (i = 0; i < symcount; i++)
+	{
+	  flagword flags;
+	  struct elf_link_hash_entry *h = sym_hashes[i];
+	  asection *sec = h->root.u.def.section;
+	  flags = bed->dynamic_sec_flags | SEC_READONLY | SEC_CODE;
+
+	  /* A symbol in an overlay group will be in a section with a
+	     name of the format .ovlinput.<symbol name>.  */
+	  if (strncmp (sec->name, OVL_INPUT_SEC_PREFIX,
+		       strlen (OVL_INPUT_SEC_PREFIX)))
+	    continue;
+	  const char *sym_name = sec->name + strlen (OVL_INPUT_SEC_PREFIX);
+
+	  /* Lookup all of the groups that this symbol exists in.  */
+	  struct ovl_func_hash_entry *sym_groups =
+	    ovl_func_hash_lookup (&ovl->ovl_func_table, sym_name, false,
+				  false);
+	  if (sym_groups == NULL)
+	    continue;
+
+	  /* For all but the first group in this list, create a duplicate
+	     section for that group based on the name.  */
+	  struct ovl_func_group_info *func_group_info;
+	  BFD_ASSERT (sym_groups->groups != NULL);
+	  for (func_group_info = sym_groups->groups->next;
+	       func_group_info != NULL;
+	       func_group_info = func_group_info->next)
+	    {
+	      /* Don't create the same duplicate section more than once.  */
+	      if (get_ovl_duplicate_section (elf->dynobj, func_group_info->id,
+					     sym_name))
+		continue;
+
+	      asection *s =
+		make_ovl_duplicate_section (elf->dynobj, func_group_info->id,
+					    sym_name, flags);
+	      BFD_ASSERT (s != NULL);
+	      if (riscv_overlay_debug)
+		fprintf (stderr,
+			 "- Created duplicate section `%s` with size 0x%lx\n",
+			 s->name, sec->size);
+
+	      bfd_set_section_alignment (s, bed->s->log_file_align);
+	      s->contents = (unsigned char *) bfd_zalloc (elf->dynobj,
+							  sec->size);
+	      s->size = sec->size;
+	      /* Create a symbol for this duplicate.  */
+	      create_ovl_dup_symbol (info, sym_name, func_group_info->id,
+				     s, /*size */ 0);
+	    }
+	}
+    }
+}
+
+/* For group 0, create a special input section that will hold the
+   group table and multigroup table.  */
+void
+riscv_elf_overlay_create_ovlgrps (
+    struct elf_link_hash_table *elf,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    const struct elf_backend_data *bed,
+    struct bfd_link_info *info)
+{
+  flagword flags;
+  asection *s;
+  bfd_vma size = ovl->ovl_group_table_size + ovl->ovl_multigroup_table_size;
+
+  flags = bed->dynamic_sec_flags | SEC_READONLY | SEC_CODE;
+  s = bfd_make_section_anyway_with_flags (elf->dynobj,
+					  OVL_GROUPTABLES_SEC_NAME, flags);
+  BFD_ASSERT (s != NULL);
+  s->contents = (unsigned char *) bfd_zalloc (elf->dynobj, size);
+  s->size = size;
+
+  /* Add symbols for the start of the group table, and a symbol
+     for the start of the multigroup table (which will be populated
+     later).  */
+  bool res;
+  struct bfd_link_hash_entry *bfdh;
+  struct elf_link_hash_entry *elfh;
+
+  bfdh = NULL;
+  res = _bfd_generic_link_add_one_symbol (info, elf->dynobj,
+					  "__OVERLAY_GROUP_TABLE_START",
+					  BSF_GLOBAL, s,
+					  0, NULL, true, false, &bfdh);
+  BFD_ASSERT (res != false);
+  /* Set ELF flags */
+  elfh = (struct elf_link_hash_entry *) bfdh;
+  elfh->type = ELF_ST_INFO (STB_GLOBAL, STT_OBJECT);
+  elfh->size = ovl->ovl_group_table_size;
+
+  bfdh = NULL;
+  res = _bfd_generic_link_add_one_symbol (info, elf->dynobj,
+					  "__OVERLAY_MULTIGROUP_TABLE_START",
+					  BSF_GLOBAL, s,
+					  ovl->ovl_group_table_size,
+					  NULL, true, false, &bfdh);
+  BFD_ASSERT (res != false);
+  elfh = (struct elf_link_hash_entry *) bfdh;
+  elfh->type = ELF_ST_INFO (STB_GLOBAL, STT_OBJECT);
+  elfh->size = ovl->ovl_multigroup_table_size;
+
+  bfdh = NULL;
+  res = _bfd_generic_link_add_one_symbol (info, elf->dynobj,
+					  "__OVERLAY_MULTIGROUP_TABLE_END",
+					  BSF_GLOBAL, s,
+					  ovl->ovl_group_table_size +
+					  ovl->ovl_multigroup_table_size,
+					  NULL, true, false, &bfdh);
+  BFD_ASSERT (res != false);
+  elfh = (struct elf_link_hash_entry *) bfdh;
+  elfh->type = ELF_ST_INFO (STB_GLOBAL, STT_OBJECT);
+  elfh->size = 0;
+}
+
+/* Generate overlay group and multigroup tables.  */
+
+bool
+riscv_elf_overlay_generate_tables (bfd * output_bfd,
+				   struct bfd_link_info *info,
+				   unsigned sizeof_external_sym)
+{
+  if (riscv_overlay_debug)
+    fprintf (stderr, " * riscv_elf_overlay_generate_tables\n");
+  struct riscv_elf_link_hash_table *htab;
+  bfd *ibfd;
+
+  htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+  if (htab->elf.dynobj == NULL)
+    return true;
+
+  /* Allocate space for the overlay PLT table based on it's size
+     (determined when checking the relocs).  */
+  if (htab->ovl.sovlplt)
+    htab->ovl.sovlplt->contents =
+      (unsigned char *) bfd_zalloc (output_bfd, htab->ovl.sovlplt->size);
+
+  assign_groups (htab, info, sizeof_external_sym);
+
+  /* Make sure that group 0 is allocated, since the group table and multi
+     group tables will be put into this section.  */
+  struct ovl_group_list_entry *group0_list_entry =
+    ovl_group_list_lookup (&htab->ovl.ovl_group_list, 0, true);
+
+  BFD_ASSERT (htab->ovl.ovl_tables_populated == true);
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      unsigned int i, symcount;
+      Elf_Internal_Shdr *symtab_hdr;
+      struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
+
+      if (!is_riscv_elf (ibfd))
+	continue;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      symcount = ((symtab_hdr->sh_size / sizeof_external_sym)
+		  - symtab_hdr->sh_info);
+
+      /* Iterate through the input symbols and if they are allocated to an
+         overlay group allocate them to the next offset in that group.  */
+      for (i = 0; i < symcount; i++)
+	{
+	  struct riscv_elf_link_hash_entry *eh =
+	    (struct riscv_elf_link_hash_entry *) sym_hashes[i];
+	  asection *sec = eh->elf.root.u.def.section;
+
+	  /* Skip this symbol if it's not a definition.  */
+	  if (eh->elf.root.type != bfd_link_hash_defined
+	      && eh->elf.root.type != bfd_link_hash_defweak)
+	    continue;
+
+	  /* Also skip the symbol if it's already been handled here.  */
+	  if (eh->ovl.overlay_groups_resolved == true)
+	    continue;
+	  else
+	    eh->ovl.overlay_groups_resolved = true;
+
+	  /* A symbol in an overlay group will be in a section with a
+	     name of the format .ovlinput.<symbol name>.  */
+	  if (strncmp (sec->name, OVL_INPUT_SEC_PREFIX,
+		       strlen (OVL_INPUT_SEC_PREFIX)))
+	    {
+	      if (!eh->ovl.needs_overlay_group)
+		continue;
+
+	      info->callbacks->einfo
+		(_("%F%P: A symbol in section '%s` is referred to by an "
+		   "overlay relocation, but the section does not have a "
+		   "'" OVL_INPUT_SEC_PREFIX "` prefix.\n"), sec->name);
+	    }
+	  const char *sym_name = sec->name + strlen (OVL_INPUT_SEC_PREFIX);
+
+	  /* Lookup all of the groups that this symbol exists in.  */
+	  struct ovl_func_hash_entry *sym_groups =
+	    ovl_func_hash_lookup (&htab->ovl.ovl_func_table, sym_name, false,
+				  false);
+	  /* Every symbol that is referred to by an overlay relocation should
+	     have been allocated to a group by this point. The group should
+	     be provided by the grouping file, the grouping tool, or have been
+	     autoassigned.  */
+	  if (eh->ovl.needs_overlay_group && sym_groups == NULL)
+	    info->callbacks->einfo
+	      (_("%F%P: Symbol '%s` is not assigned to an overlay group "
+		 "but is referenced by an overlay relocation.\n"), sym_name);
+
+	  if (sym_groups == NULL)
+	    continue;
+
+	  /* The symbol has been assigned to an overlay group, but there
+	     also exist non-overlay references to it!.  */
+	  if (eh->ovl.non_overlay_reference)
+	    info->callbacks->einfo
+	      (_("%F%P: Symbol '%s` is assigned to an overlay group "
+		 "but it is referenced by a non-overlay relocation.\n"),
+	       sym_name);
+
+	  /* If this is in multiple groups, then a multigroup entry needs
+	     to be allocated.  */
+	  if (sym_groups->multigroup == true)
+	    {
+	      /* Calculate the size of the entry in the multigroup
+	         table. A multigroup entry consists of a list of tokens
+	         (4-bytes each) followed by a 4-byte 0 terminator.  */
+	      int multigroup_entry_size;
+	      struct ovl_func_group_info *func_group_info;
+
+	      multigroup_entry_size = 0;
+	      for (func_group_info = sym_groups->groups;
+		   func_group_info != NULL;
+		   func_group_info = func_group_info->next)
+		multigroup_entry_size += 4;
+	      /* NULL terminator.  */
+	      multigroup_entry_size += 4;
+
+	      sym_groups->multigroup_offset =
+		htab->ovl.ovl_multigroup_table_size;
+	      htab->ovl.ovl_multigroup_table_size += multigroup_entry_size;
+	    }
+
+	  struct ovl_func_group_info *func_group_info;
+	  for (func_group_info = sym_groups->groups; func_group_info != NULL;
+	       func_group_info = func_group_info->next)
+	    {
+	      ovl_max_group = func_group_info->id > ovl_max_group
+		? func_group_info->id : ovl_max_group;
+
+	      struct ovl_group_list_entry *group_list_entry =
+		ovl_group_list_lookup (&htab->ovl.ovl_group_list,
+				       func_group_info->id, false);
+	      BFD_ASSERT (group_list_entry != NULL);
+
+	      /* Allocate the symbol's offset into the output section for the
+	         group. This corresponds to the current size of the output
+	         section.  */
+	      func_group_info->unrelaxed_offset =
+		group_list_entry->group_size;
+
+	      /* Keep track of the first function which was allocated to this
+	         group.  */
+	      if (group_list_entry->group_size == 0)
+		group_list_entry->first_func = sym_name;
+	      group_list_entry->last_func = sym_name;
+
+	      /* Allocate space in the output group for the contents of the
+	         input section corresponding to the symbol, and re-pad to a
+	         4-byte boundary to allow offsets to remain valid.  */
+	      group_list_entry->group_size += sec->size;
+	      if ((group_list_entry->group_size % OVL_SYM_ALIGN) != 0)
+		group_list_entry->group_size +=
+		  (OVL_SYM_ALIGN
+		   - group_list_entry->group_size % OVL_SYM_ALIGN);
+
+	      if (group_list_entry->group_size + OVL_CRC_SZ
+		  > OVL_MAX_GROUP_SIZE)
+		info->callbacks->einfo
+		  (_("%F%P: overlay group %d exceeds maximum group size\n"),
+		   (int) func_group_info->id);
+	    }
+	}
+    }
+
+  /* Now the size of any multigroups has been determined, so space for the
+     multigroup table can be allocated.  */
+  /* Set the size of .ovlgrptbl section, adding a placeholder last entry and
+     space for a null terminator.  */
+  htab->ovl.ovl_group_table_size = (ovl_max_group + 3) * 2;
+  htab->ovl.ovl_group_table_max_group = ovl_max_group;
+  if (htab->ovl.ovl_group_table_size % 4)
+    htab->ovl.ovl_group_table_size +=
+      4 - (htab->ovl.ovl_group_table_size % 4);
+
+  /* Now that the size of the group table and multigroup table has been
+     determined, we can use the sum of these as the size of group 0, which
+     will hold these tables.  */
+  group0_list_entry->group_size =
+    htab->ovl.ovl_group_table_size + htab->ovl.ovl_multigroup_table_size;
+
+  if (riscv_overlay_debug)
+    {
+      fprintf (stderr, "Pre-size Table\n===========\n");
+      print_group_list (&htab->ovl.ovl_group_list);
+      print_func_table (&htab->ovl.ovl_func_table);
+    }
+
+  /* Now that the size of the groups is fixed calculated the padded size
+     of each group, finalize the offset of each group, and calculate the
+     total size needed in ".ovlgrps".  */
+  unsigned int i;
+  bfd_vma next_group_offset = 0;
+  for (i = 0; i <= ovl_max_group; i++)
+    {
+      struct ovl_group_list_entry *group_list_entry =
+	ovl_group_list_lookup (&htab->ovl.ovl_group_list,
+			       i, false);
+      /* Ignore any gaps in the table.  */
+      if (group_list_entry == NULL)
+	continue;
+
+      /* Calculate the padded size of the group.  */
+      group_list_entry->padded_group_size = group_list_entry->group_size;
+      group_list_entry->padded_group_size += OVL_CRC_SZ;
+      if (group_list_entry->padded_group_size % OVL_GROUP_PAGE_SIZE)
+	group_list_entry->padded_group_size =
+	  ((group_list_entry->padded_group_size / OVL_GROUP_PAGE_SIZE) +
+	   1) * OVL_GROUP_PAGE_SIZE;
+
+      /* Set the offset of the group to the next available offset.  */
+      group_list_entry->ovlgrpdata_offset = next_group_offset;
+
+      /* Add the padded group size to get the offet for the next group.
+         The padding will be filled in once contents for the output
+         section have been allocated.  */
+      next_group_offset += group_list_entry->padded_group_size;
+    }
+
+  if (riscv_overlay_debug)
+    {
+      fprintf (stderr, "Final Table\n===========\n");
+      print_group_list (&htab->ovl.ovl_group_list);
+      print_func_table (&htab->ovl.ovl_func_table);
+    }
+  return true;
+}
+
+/* Function to determine sorting of input sections when being placed. For
+   overlay functions, return the offset of that section in the output.
+   NOTE: Due to how the linker uses this value, this function has to return
+         a *NEGATIVE* offset in order to sort correctly. */
+int
+riscv_elf_overlay_sort_value (asection * s, struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (s != NULL);
+  BFD_ASSERT (htab != NULL);
+
+  /* If this is not an overlay function, return 1.  */
+  if (strncmp (s->name, OVL_INPUT_SEC_PREFIX, strlen (OVL_INPUT_SEC_PREFIX)))
+    return 1;
+
+  /* If this is the group table, it will always be the first thing
+     to appear in the output section.  */
+  if (!strncmp (s->name, OVL_GROUPTABLES_SEC_NAME,
+		strlen (OVL_GROUPTABLES_SEC_NAME)))
+    {
+      /* The group tables are always the first things to appear in the
+         output section.  */
+      return 0;
+    }
+
+  /* If this is an internal padding value, look at the group number, and use
+     its offset to return an offset.  */
+  if (!strncmp (s->name, OVL_PADDING_SEC_PREFIX,
+		strlen (OVL_PADDING_SEC_PREFIX)))
+    {
+      const char *group_id_str = s->name + strlen (OVL_PADDING_SEC_PREFIX);
+
+      int group_id = atoi (group_id_str);
+      struct ovl_group_list_entry *group_list_entry =
+	ovl_group_list_lookup (&htab->ovl.ovl_group_list, group_id, false);
+      BFD_ASSERT (group_list_entry != NULL);
+
+      bfd_vma padding_offset = group_list_entry->group_size;
+
+      if (riscv_overlay_debug)
+	fprintf (stderr, " - Offset of %s is %lx\n", s->name,
+		 group_list_entry->ovlgrpdata_offset + padding_offset);
+
+      return -(group_list_entry->ovlgrpdata_offset + padding_offset);
+    }
+
+  /* If this is a duplicate of a function, look up its symbol hash and find the
+     offset corresponding to that group, otherwise it must be the first entry. */
+  struct ovl_func_group_info *func_group_info = NULL;
+  if (!strncmp (s->name, OVL_DUPLICATE_SEC_PREFIX,
+		strlen (OVL_DUPLICATE_SEC_PREFIX)))
+    {
+      const char *name_and_group =
+	s->name + strlen (OVL_DUPLICATE_SEC_PREFIX);
+      const char *sym_name = strchr (name_and_group, '.') + 1;
+      BFD_ASSERT (sym_name != (char *) 1);
+      bfd_vma group_id;
+      int matched = sscanf (name_and_group, "%lu.", &group_id);
+      BFD_ASSERT (matched = 1);
+
+      struct ovl_func_hash_entry *sym_groups =
+	ovl_func_hash_lookup (&htab->ovl.ovl_func_table, sym_name, false,
+			      false);
+      BFD_ASSERT (sym_groups != NULL);
+      BFD_ASSERT (sym_groups->groups != NULL);
+
+      /* Start with the second group info, since the first one cannot be a
+         duplicate.  */
+      for (func_group_info = sym_groups->groups->next;
+	   func_group_info != NULL; func_group_info = func_group_info->next)
+	{
+	  if (func_group_info->id == group_id)
+	    break;
+	}
+    }
+  else
+    {
+      /* Future proof against further internal types.  */
+      BFD_ASSERT (strncmp (s->name, OVL_INTERNAL_SEC_PREFIX,
+			   strlen (OVL_INTERNAL_SEC_PREFIX)) != 0);
+
+      /* Return an offset of 0 for functions that have been GC'd.  */
+      if (overlay_use_gcmark && !s->gc_mark)
+	return 0;
+
+      const char *sym_name = s->name + strlen (OVL_INPUT_SEC_PREFIX);
+
+      /* This is not a duplicate, therefore it is the first group in the list. */
+      struct ovl_func_hash_entry *sym_groups =
+	ovl_func_hash_lookup (&htab->ovl.ovl_func_table, sym_name, false,
+			      false);
+      BFD_ASSERT (sym_groups != NULL);
+      BFD_ASSERT (sym_groups->groups != NULL);
+      func_group_info = sym_groups->groups;
+    }
+
+  BFD_ASSERT (func_group_info != NULL);
+
+  /* func_group_info holds current group and offset, need to find full offset. */
+  struct ovl_group_list_entry *group_list_entry =
+    ovl_group_list_lookup (&htab->ovl.ovl_group_list, func_group_info->id,
+			   false);
+  BFD_ASSERT (group_list_entry != NULL);
+
+  bfd_vma offset = group_list_entry->ovlgrpdata_offset
+    + func_group_info->unrelaxed_offset;
+
+  if (riscv_overlay_debug)
+    fprintf (stderr, " - Offset of %s is %lx\n", s->name, offset);
+
+  return -offset;
+}
+
+void
+riscv_elf_overlay_check_sections (
+    bfd * abfd,
+    struct bfd_link_info *info,
+    struct riscv_elf_overlay_link_hash_table *ovl)
+{
+  asection *sec;
+  for (sec = abfd->sections; sec != NULL; sec = sec->next)
+    {
+      /* If this is an overlay input section, flag the link as being overlay
+         enabled.  */
+      if (strncmp
+	  (sec->name, OVL_INPUT_SEC_PREFIX,
+	   strlen (OVL_INPUT_SEC_PREFIX) == 0))
+	{
+	  info->dynamic = 1;
+	  ovl->overlay_enabled = 1;
+	}
+    }
+}
+
+/* Print an entry from an overlay grouping list.  */
+static bool
+print_group_list_entry (struct ovl_group_list_entry *entry, int index,
+			void *info ATTRIBUTE_UNUSED)
+{
+  fprintf (stderr, "Group %d", index);
+  fprintf (stderr,
+	   " (output section offset: 0x%lx, size 0x%lx, padded size 0x%lx)",
+	   entry->ovlgrpdata_offset, entry->group_size,
+	   entry->padded_group_size);
+  fputc (':', stderr);
+
+  for (int i = 0; i < entry->n_functions; i++)
+    fprintf (stderr, " %s", entry->functions[i]);
+  fprintf (stderr, "\n");
+
+  return true;
+}
+
+/* Print each entry in a group to function list.  */
+static void
+print_group_list (struct ovl_group_list *list)
+{
+  ovl_group_list_traverse (list, print_group_list_entry, NULL);
+}
+
+/* Print an entry from an overlay grouping hash table.  */
+static bool
+print_func_entry (struct ovl_func_hash_entry *entry,
+		  void *info ATTRIBUTE_UNUSED)
+{
+  fprintf (stderr, "Function %s:", entry->root.string);
+  struct ovl_func_group_info *head = entry->groups;
+  while (head != NULL)
+    {
+      fprintf (stderr, " %lu (@%lu)", head->id, head->unrelaxed_offset);
+      head = head->next;
+    }
+  fprintf (stderr, "\n");
+
+  return true;
+}
+
+/* Print each entry in an overlay grouping hash table.  */
+
+static void
+print_func_table (struct bfd_hash_table *table)
+{
+  ovl_func_hash_traverse (table, print_func_entry, NULL);
+}
diff --git a/bfd/elfxx-riscv-overlay.h b/bfd/elfxx-riscv-overlay.h
new file mode 100644
index 00000000000..f6a8d1621d9
--- /dev/null
+++ b/bfd/elfxx-riscv-overlay.h
@@ -0,0 +1,205 @@
+#ifndef ELFXX_RISCV_OVERLAY_H
+#define ELFXX_RISCV_OVERLAY_H
+
+#include "elfxx-riscv.h"
+
+struct ovl_group_list_entry;
+
+/* Additional members for the riscv_elf_link_hash_entry structure,
+   needed for the overlay system.  */
+
+struct riscv_elf_link_hash_entry_overlay
+{
+  /* Track whether this symbol needs an overlay PLT entry.  */
+  int needs_ovlplt_entry;
+
+  /* Track whether this symbols is referred to by an overlay relocation,
+     and therefore needs to exist in at least one overlay group.  */
+  int needs_overlay_group;
+
+  /* Track whether this symbol is referred to by a non-overlay relocation.  */
+  int non_overlay_reference;
+
+  /* Track whether this symbol has already been handled and any overlay
+     groups have already been generated.  */
+  int overlay_groups_resolved;
+};
+
+/* List tracking all (populated or unpopulated) overlay groups.  */
+
+struct ovl_group_list
+{
+  int n_groups;
+  struct ovl_group_list_entry *groups;
+};
+
+/* Additional members for the riscv_elf_link_hash_table structure,
+   needed for the overlay system.  */
+
+struct riscv_elf_overlay_link_hash_table
+{
+  /* Note whether linking overlay-enabled binary.  */
+  bool overlay_enabled;
+
+  /* Short cut to overlay plt section.  */
+  asection *sovlplt;
+
+  /* Offset to the next free space in the overlay plt section for a
+     new plt entry.  */
+  bfd_vma next_ovlplt_offset;
+
+  /* Sizes for the group table and multigroup table, which together
+     will populated group 0.  */
+  bfd_vma ovl_group_table_size;
+  bfd_vma ovl_multigroup_table_size;
+
+  /* The maximum group number for any group in the group table.  */
+  bfd_vma ovl_group_table_max_group;
+
+  /* One to many mapping from a function to the groups it is contained in.  */
+  struct bfd_hash_table ovl_func_table;
+
+  /* List of all of the groups, which includes a list of the contained
+     functions for that group.  */
+  struct ovl_group_list ovl_group_list;
+
+  /* Tracks whether the overlay tables have finished being populated.  */
+  bool ovl_tables_populated;
+};
+
+/* File containing user defined groupings for each overlay symbol.  */
+FILE *riscv_grouping_file;
+
+/* Marks whether the grouping tool is being used to assign overlay symbols
+   to groups.  */
+bool riscv_use_grouping_tool;
+
+/* Command for the grouping tool, and command line arguments.  */
+char *riscv_grouping_tool;
+char *riscv_grouping_tool_args;
+
+bfd_vma riscv_ovl_first_group_number;
+bool riscv_overlay_debug;
+bfd_vma riscv_crc_init;
+bfd_vma riscv_crc_poly;
+bfd_vma riscv_crc_xorout;
+bool riscv_crc_refin;
+bool riscv_crc_refout;
+
+/* Used to assert that garbage collection is being performed, which means
+   some overlay operations can be skipped for dead functions.  */
+
+void riscv_overlay_set_use_gcmark (void);
+
+/* Initialize riscv_elf_overlay_link_hash_table struct.  */
+
+bool
+riscv_elf_overlay_link_hash_table_init (
+    struct riscv_elf_overlay_link_hash_table *ovl);
+
+/* Finishes various sections used by the overlay system. This fills in
+   the overlay plt section and the group table, populates the duplicates
+   for any symbol in a multigroup, and performs padding and computes
+   the CRC for the end of groups.  */
+
+bool
+riscv_overlay_finish_sections (struct elf_link_hash_table *elf,
+			       struct riscv_elf_overlay_link_hash_table *ovl,
+			       struct bfd_link_info *info);
+
+/* When bytes are deleted from a section in an overlay group, the
+   accompanying padding section which follows the group needs to be
+   adjusted to ensure that the total size of the section ends on a
+   multiple of the overlay page size. This performs that operation.  */
+
+void
+riscv_overlay_relax_delete_bytes (asection * sec, size_t count,
+				  struct bfd_link_info *link_info);
+
+/* Handle the check of overlay specific relocations. Called from
+   riscv_elf_check_relocs.  */
+
+bool
+riscv_elf_overlay_check_relocation (
+    bfd * abfd,
+    struct bfd_link_info *info,
+    struct riscv_elf_link_hash_entry_overlay *eh,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    int r_type);
+
+/* Resolve the value for overlay specific relocations.  Called from
+   riscv_elf_relocate_section.  */
+
+bool
+riscv_overlay_handle_relocation (
+    bfd_vma * relocation,
+    bfd * output_bfd,
+    struct bfd_link_info *info,
+    struct elf_link_hash_entry *h,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    Elf_Internal_Sym * sym,
+    int r_type);
+
+/* Function to determine the sorting of input sections when being placed.  For
+   overlay functions this returns the offset of that section in the output.
+
+   NOTE: Due to how the linker uses this value, this function returns a
+   *negative* offset in order to sort correctly.  */
+
+int riscv_elf_overlay_sort_value (asection * s, struct bfd_link_info *info);
+
+/* Check sections which may be in an overlay.  Called from
+   riscv_elf_overlay_check_sections.  */
+
+void
+riscv_elf_overlay_check_sections (
+    bfd * abfd,
+    struct bfd_link_info *info,
+    struct riscv_elf_overlay_link_hash_table *ovl);
+
+/* Generate the overlay group and multigroup tables.  This also assigns
+   symbols to the groups themselves.  */
+bool
+riscv_elf_overlay_generate_tables (bfd * output_bfd,
+				   struct bfd_link_info *info,
+				   unsigned sizeof_external_sym);
+
+/* Create a special input section containing the metadata for the
+   group and multigroup assignments.  This section is used to populate
+   group number 0.  */
+
+void
+riscv_elf_overlay_create_ovlgrps (
+    struct elf_link_hash_table *elf,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    const struct elf_backend_data *bed,
+    struct bfd_link_info *info);
+
+/* Create duplicate sections for symbols which exist in multigroups.  */
+
+void
+riscv_elf_overlay_duplicate_multigroup_sections (
+    struct elf_link_hash_table *elf,
+    struct riscv_elf_overlay_link_hash_table *ovl,
+    const struct elf_backend_data *bed,
+    struct bfd_link_info *info,
+    unsigned sizeof_external_sym);
+
+/* For each group, create a padding section after the group which will
+   round the group up to the page size. The padding section will contain
+   padding followed by the CRC at the end.  */
+
+void
+riscv_elf_overlay_pad_groups (struct elf_link_hash_table *elf,
+			      struct riscv_elf_overlay_link_hash_table *ovl,
+			      const struct elf_backend_data *bed,
+			      struct bfd_link_info *info);
+
+/* Print an summary of overlay assignments to the map file.  */
+
+void
+riscv_elf_overlay_printmap (bfd * obfd, struct elf_link_hash_table *elf,
+			    struct riscv_elf_overlay_link_hash_table *ovl,
+			    FILE * mapfile);
+
+#endif /* ELFXX_RISCV_OVERLAY_H  */
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 9f52bb545ac..fca584038fb 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -870,6 +870,96 @@ static reloc_howto_type howto_table[] =
 	 0,				/* src_mask */
 	 0xffffffff,			/* dst_mask */
 	 false),			/* pcrel_offset */
+
+  /* High 20 bits of overlay table offset.  */
+  HOWTO (R_RISCV_OVLTOK_HI20,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_OVLTOK_HI20",		/* name */
+	 true,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* Low 12 bits of overlay table offset.  */
+  HOWTO (R_RISCV_OVLTOK_LO12_I,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_OVLTOK_LO12_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 32-bit overlay token.  */
+  HOWTO (R_RISCV_OVLTOK32,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_OVLTOK32",		/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 0xffffffff,			/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* High 20 bits of the PLT entry for an overlay function.  */
+  HOWTO (R_RISCV_OVLPLT_HI20,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_OVLPLT_HI20",		/* name */
+	 true,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* Low 12 bits of the PLT entry for an overlay function.  */
+  HOWTO (R_RISCV_OVLPLT_LO12_I,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_OVLPLT_LO12_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 32-bit overlay PLT address.  */
+  HOWTO (R_RISCV_OVLPLT32,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_OVLPLT32",		/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 0xffffffff,			/* dst_mask */
+	 false),			/* pcrel_offset */
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -931,6 +1021,12 @@ static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_SET16, R_RISCV_SET16 },
   { BFD_RELOC_RISCV_SET32, R_RISCV_SET32 },
   { BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL },
+  { BFD_RELOC_RISCV_OVLTOK_HI20, R_RISCV_OVLTOK_HI20 },
+  { BFD_RELOC_RISCV_OVLTOK_LO12_I, R_RISCV_OVLTOK_LO12_I },
+  { BFD_RELOC_RISCV_OVLTOK32, R_RISCV_OVLTOK32 },
+  { BFD_RELOC_RISCV_OVLPLT_HI20, R_RISCV_OVLPLT_HI20 },
+  { BFD_RELOC_RISCV_OVLPLT_LO12_I, R_RISCV_OVLPLT_LO12_I },
+  { BFD_RELOC_RISCV_OVLPLT32, R_RISCV_OVLPLT32 },
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
@@ -943,7 +1039,21 @@ riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 
   for (i = 0; i < ARRAY_SIZE (riscv_reloc_map); i++)
     if (riscv_reloc_map[i].bfd_val == code)
-      return &howto_table[(int) riscv_reloc_map[i].elf_val];
+      {
+	int elf_val = riscv_reloc_map[i].elf_val;
+	/* FIXME: There's a big discontinuity in the howto table for the
+	   overlay relocations which don't yet have official relocation ids.
+	   This means `elf_val' needs to be adjusted down to close the gap.
+
+	   The official relocation numbers should be nearly contiguous
+	   with the existing relocations, at which point this problem will
+	   either go away or can be properly resolved by inserting padding
+	   entries into the howto table instead.  */
+	if (elf_val >= R_RISCV_OVLTOK_HI20)
+	  elf_val -= R_RISCV_OVLTOK_HI20 - R_RISCV_IRELATIVE - 1;
+
+	return &howto_table[elf_val];
+      }
 
   bfd_set_error (bfd_error_bad_value);
   return NULL;
@@ -964,14 +1074,15 @@ riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
 reloc_howto_type *
 riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
 {
-  if (r_type >= ARRAY_SIZE (howto_table))
-    {
-      (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
-			     abfd, r_type);
-      bfd_set_error (bfd_error_bad_value);
-      return NULL;
-    }
-  return &howto_table[r_type];
+  unsigned int i;
+  for (i = 0; i < ARRAY_SIZE (howto_table); i++)
+    if (howto_table[i].type == r_type)
+      return &howto_table[i];
+
+  (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
+			 abfd, r_type);
+  bfd_set_error (bfd_error_bad_value);
+  return NULL;
 }
 
 /* Special_function of RISCV_ADD and RISCV_SUB relocations.  */
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 67b7d078232..1fc1b750859 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -20,13 +20,77 @@
    along with this program; see the file COPYING3. If not,
    see <http://www.gnu.org/licenses/>.  */
 
+#ifndef ELFXX_RISCV_H
+#define ELFXX_RISCV_H
+
+#include "libbfd.h"
 #include "elf/common.h"
 #include "elf/internal.h"
 #include "opcode/riscv.h"
 #include "cpu-riscv.h"
+#include "elfxx-riscv-overlay.h"
 
 #define RISCV_UNKNOWN_VERSION -1
 
+/* RISC-V ELF linker hash entry.  */
+
+struct riscv_elf_link_hash_entry
+{
+  struct elf_link_hash_entry elf;
+
+#define GOT_UNKNOWN	0
+#define GOT_NORMAL	1
+#define GOT_TLS_GD	2
+#define GOT_TLS_IE	4
+#define GOT_TLS_LE	8
+  char tls_type;
+
+  struct riscv_elf_link_hash_entry_overlay ovl;
+};
+
+struct riscv_elf_link_hash_table
+{
+  struct elf_link_hash_table elf;
+
+  /* Short-cuts to get to dynamic linker sections.  */
+  asection *sdyntdata;
+
+  /* The max alignment of output sections.  */
+  bfd_vma max_alignment;
+
+  /* Used by local STT_GNU_IFUNC symbols.  */
+  htab_t loc_hash_table;
+  void * loc_hash_memory;
+
+  /* The index of the last unused .rel.iplt slot.  */
+  bfd_vma last_iplt_index;
+
+  /* The data segment phase, don't relax the section
+     when it is exp_seg_relro_adjust.  */
+  int *data_segment_phase;
+
+  /* Relocations for variant CC symbols may be present.  */
+  int variant_cc;
+
+  struct riscv_elf_overlay_link_hash_table ovl;
+};
+
+#define riscv_elf_hash_entry(ent) \
+  ((struct riscv_elf_link_hash_entry *) (ent))
+
+/* Get the RISC-V ELF linker hash table from a link_info structure.  */
+#define riscv_elf_hash_table(p) \
+  ((is_elf_hash_table ((p)->hash)					\
+    && elf_hash_table_id (elf_hash_table (p)) == RISCV_ELF_DATA)	\
+   ? (struct riscv_elf_link_hash_table *) (p)->hash : NULL)
+
+#define is_riscv_elf(bfd)				\
+  (bfd_get_flavour (bfd) == bfd_target_elf_flavour	\
+   && elf_tdata (bfd) != NULL				\
+   && elf_object_id (bfd) == RISCV_ELF_DATA)
+
+#define sec_addr(sec) ((sec)->output_section->vma + (sec)->output_offset)
+
 extern reloc_howto_type *
 riscv_reloc_name_lookup (bfd *, const char *);
 
@@ -108,3 +172,5 @@ extern void
 bfd_elf32_riscv_set_data_segment_info (struct bfd_link_info *, int *);
 extern void
 bfd_elf64_riscv_set_data_segment_info (struct bfd_link_info *, int *);
+
+#endif /* ELFXX_RISCV_H */
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 0579f64d1a0..153d1594328 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -318,6 +318,10 @@
 #define bfd_elfNN_bfd_link_check_relocs  _bfd_elf_link_check_relocs
 #endif
 
+#ifndef bfd_elfNN_bfd_get_section_overlay_sort_data
+#define bfd_elfNN_bfd_get_section_overlay_sort_data bfd_generic_get_section_overlay_sort_data
+#endif
+
 #ifndef bfd_elfNN_archive_p
 #define bfd_elfNN_archive_p bfd_generic_archive_p
 #endif
diff --git a/bfd/ihex.c b/bfd/ihex.c
index cccec8f164e..d80b9a0f1d2 100644
--- a/bfd/ihex.c
+++ b/bfd/ihex.c
@@ -966,6 +966,7 @@ ihex_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define ihex_bfd_final_link			  _bfd_generic_final_link
 #define ihex_bfd_link_split_section		  _bfd_generic_link_split_section
 #define ihex_bfd_link_check_relocs		  _bfd_generic_link_check_relocs
+#define ihex_bfd_get_section_overlay_sort_data	  bfd_generic_get_section_overlay_sort_data
 
 /* The Intel Hex target vector.  */
 
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index 2905de8ef92..82168d51b41 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -534,6 +534,8 @@ extern struct bfd_link_hash_entry *_bfd_nolink_bfd_define_start_stop
   (struct bfd_link_info *, const char *, asection *) ATTRIBUTE_HIDDEN;
 #define _bfd_nolink_bfd_link_check_relocs \
   _bfd_generic_link_check_relocs
+#define _bfd_nolink_bfd_get_section_overlay_sort_data \
+  bfd_generic_get_section_overlay_sort_data
 
 /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
    have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 6e62e556962..40b3d2b7b51 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -539,6 +539,8 @@ extern struct bfd_link_hash_entry *_bfd_nolink_bfd_define_start_stop
   (struct bfd_link_info *, const char *, asection *) ATTRIBUTE_HIDDEN;
 #define _bfd_nolink_bfd_link_check_relocs \
   _bfd_generic_link_check_relocs
+#define _bfd_nolink_bfd_get_section_overlay_sort_data \
+  bfd_generic_get_section_overlay_sort_data
 
 /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
    have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
@@ -2400,6 +2402,12 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_RISCV_SET16",
   "BFD_RELOC_RISCV_SET32",
   "BFD_RELOC_RISCV_32_PCREL",
+  "BFD_RELOC_RISCV_OVLTOK_HI20",
+  "BFD_RELOC_RISCV_OVLTOK_LO12_I",
+  "BFD_RELOC_RISCV_OVLTOK32",
+  "BFD_RELOC_RISCV_OVLPLT_HI20",
+  "BFD_RELOC_RISCV_OVLPLT_LO12_I",
+  "BFD_RELOC_RISCV_OVLPLT32",
   "BFD_RELOC_RL78_NEG8",
   "BFD_RELOC_RL78_NEG16",
   "BFD_RELOC_RL78_NEG24",
diff --git a/bfd/plugin.c b/bfd/plugin.c
index fb45cbee514..02a3fd58b58 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -110,6 +110,7 @@ dlerror (void)
 #define bfd_plugin_bfd_define_start_stop	      bfd_generic_define_start_stop
 #define bfd_plugin_bfd_copy_link_hash_symbol_type     _bfd_generic_copy_link_hash_symbol_type
 #define bfd_plugin_bfd_link_check_relocs	      _bfd_generic_link_check_relocs
+#define bfd_plugin_bfd_get_section_overlay_sort_data  bfd_generic_get_section_overlay_sort_data
 
 static enum ld_plugin_status
 message (int level ATTRIBUTE_UNUSED,
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 164060361a9..4c5cb71517c 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -5245,6 +5245,18 @@ ENUMX
   BFD_RELOC_RISCV_SET32
 ENUMX
   BFD_RELOC_RISCV_32_PCREL
+ENUMX
+  BFD_RELOC_RISCV_OVLTOK_HI20
+ENUMX
+  BFD_RELOC_RISCV_OVLTOK_LO12_I
+ENUMX
+  BFD_RELOC_RISCV_OVLTOK32
+ENUMX
+  BFD_RELOC_RISCV_OVLPLT_HI20
+ENUMX
+  BFD_RELOC_RISCV_OVLPLT_LO12_I
+ENUMX
+  BFD_RELOC_RISCV_OVLPLT32
 ENUMDOC
   RISC-V relocations.
 
diff --git a/bfd/section.c b/bfd/section.c
index 2de7dbf661a..0f593dcd3a8 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -1713,3 +1713,22 @@ _bfd_nowrite_set_section_contents (bfd *abfd,
 {
   return _bfd_bool_bfd_false_error (abfd);
 }
+
+/*
+FUNCTION
+	bfd_generic_get_section_overlay_sort_data
+
+SYNOPSIS
+	int bfd_generic_get_section_overlay_sort_data
+	  (asection *sec, struct bfd_link_info *info);
+
+DESCRIPTION
+	Get the user sort data for the section @var{sec} under @var{info}.
+*/
+
+int
+bfd_generic_get_section_overlay_sort_data (asection *sec ATTRIBUTE_UNUSED,
+					   struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+  return 0;
+}
diff --git a/bfd/srec.c b/bfd/srec.c
index 5f8c8c18087..facd3898167 100644
--- a/bfd/srec.c
+++ b/bfd/srec.c
@@ -1283,6 +1283,7 @@ srec_print_symbol (bfd *abfd,
 #define srec_bfd_final_link			  _bfd_generic_final_link
 #define srec_bfd_link_split_section		  _bfd_generic_link_split_section
 #define srec_bfd_link_check_relocs		  _bfd_generic_link_check_relocs
+#define srec_bfd_get_section_overlay_sort_data	  bfd_generic_get_section_overlay_sort_data
 
 const bfd_target srec_vec =
 {
diff --git a/bfd/targets.c b/bfd/targets.c
index 18fec45f02a..84cc0b9c445 100644
--- a/bfd/targets.c
+++ b/bfd/targets.c
@@ -474,7 +474,8 @@ BFD_JUMP_TABLE macros.
 .  NAME##_section_already_linked, \
 .  NAME##_bfd_define_common_symbol, \
 .  NAME##_bfd_link_hide_symbol, \
-.  NAME##_bfd_define_start_stop
+.  NAME##_bfd_define_start_stop, \
+.  NAME##_bfd_get_section_overlay_sort_data
 .
 .  int	(*_bfd_sizeof_headers) (bfd *, struct bfd_link_info *);
 .  bfd_byte *
@@ -553,6 +554,10 @@ BFD_JUMP_TABLE macros.
 .	(*_bfd_define_start_stop) (struct bfd_link_info *, const char *,
 .				   asection *);
 .
+.  {* Get section specific sort data.  *}
+.  int (*_bfd_get_section_overlay_sort_data) (asection *,
+.                                             struct bfd_link_info *);
+.
 .  {* Routines to handle dynamic symbols and relocs.  *}
 .#define BFD_JUMP_TABLE_DYNAMIC(NAME) \
 .  NAME##_get_dynamic_symtab_upper_bound, \
diff --git a/bfd/tekhex.c b/bfd/tekhex.c
index 7ff87af8a9d..d12f02005a3 100644
--- a/bfd/tekhex.c
+++ b/bfd/tekhex.c
@@ -992,6 +992,7 @@ tekhex_print_symbol (bfd *abfd,
 #define tekhex_bfd_link_split_section		    _bfd_generic_link_split_section
 #define tekhex_get_section_contents_in_window	    _bfd_generic_get_section_contents_in_window
 #define tekhex_bfd_link_check_relocs		    _bfd_generic_link_check_relocs
+#define tekhex_bfd_get_section_overlay_sort_data    bfd_generic_get_section_overlay_sort_data
 
 const bfd_target tekhex_vec =
 {
diff --git a/gas/ChangeLog b/gas/ChangeLog
index e2e48973204..165c2ef832f 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,14 @@
+2022-02-28  Edward Jones  <ed.jones@embecosm.com>
+
+	* config/tc-riscv.c (percent_op_utype):
+	Add mappings for BFD_RELOC_RISCV_OVLTOK_HI20
+	and BFD_RELOC_RISCV_OVLPLT_HI20 relocs.
+	(percent_op_itype):
+	Add mappings for BFD_RELOC_RISCV_OVLTOK_LO12_I
+	and BFD_RELOC_RISCV_OVLPLT_LO12_I relocs.
+	(md_apply_fix): Add handling of overlay
+	relocations.
+
 2022-01-28  Nick Clifton  <nickc@redhat.com>
 
 	* po/fr.po: Updated French translation.
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 25908597436..0483bf0991d 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1901,6 +1901,8 @@ static const struct percent_op_match percent_op_utype[] =
   {"%tls_ie_pcrel_hi", BFD_RELOC_RISCV_TLS_GOT_HI20},
   {"%tls_gd_pcrel_hi", BFD_RELOC_RISCV_TLS_GD_HI20},
   {"%hi", BFD_RELOC_RISCV_HI20},
+  {"%ovltok_hi", BFD_RELOC_RISCV_OVLTOK_HI20},
+  {"%ovlplt_hi", BFD_RELOC_RISCV_OVLPLT_HI20},
   {0, 0}
 };
 
@@ -1909,6 +1911,8 @@ static const struct percent_op_match percent_op_itype[] =
   {"%lo", BFD_RELOC_RISCV_LO12_I},
   {"%tprel_lo", BFD_RELOC_RISCV_TPREL_LO12_I},
   {"%pcrel_lo", BFD_RELOC_RISCV_PCREL_LO12_I},
+  {"%ovltok_lo", BFD_RELOC_RISCV_OVLTOK_LO12_I},
+  {"%ovlplt_lo", BFD_RELOC_RISCV_OVLPLT_LO12_I},
   {0, 0}
 };
 
@@ -3669,6 +3673,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_RISCV_ALIGN:
       break;
 
+    case BFD_RELOC_RISCV_OVLTOK_LO12_I:
+    case BFD_RELOC_RISCV_OVLTOK_HI20:
+    case BFD_RELOC_RISCV_OVLTOK32:
+    case BFD_RELOC_RISCV_OVLPLT_LO12_I:
+    case BFD_RELOC_RISCV_OVLPLT_HI20:
+    case BFD_RELOC_RISCV_OVLPLT32:
+      break;
+
     default:
       /* We ignore generic BFD relocations we don't know about.  */
       if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
diff --git a/include/ChangeLog b/include/ChangeLog
index 416fb6b8778..489f1f42435 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,13 @@
+2022-02-28  Edward Jones  <ed.jones@embecosm.com>
+
+	* elf/riscv.h: Add R_RISCV_OVLTOK_HI20,
+	R_RISCV_OVLTOK_LO12_I, R_RISCV_OVLTOK32,
+	R_RISCV_OVLPLT_HI20, R_RISCV_OVLPLT_LO12_I,
+	R_RISCV_OVLPLT32 for reloc numbers from 220
+	to 225.
+	* opcode/riscv.h (X_ZERO X_T5 X_T6): Add
+	defines.
+
 2022-01-25  Klaus Ziegler  <klausz@haus-gisela.de>
 
 	PR 28816
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index d0acf6886d8..07a19ba81fc 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -89,6 +89,12 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
   RELOC_NUMBER (R_RISCV_SET32, 56)
   RELOC_NUMBER (R_RISCV_32_PCREL, 57)
   RELOC_NUMBER (R_RISCV_IRELATIVE, 58)
+  RELOC_NUMBER (R_RISCV_OVLTOK_HI20, 220)
+  RELOC_NUMBER (R_RISCV_OVLTOK_LO12_I, 221)
+  RELOC_NUMBER (R_RISCV_OVLTOK32, 222)
+  RELOC_NUMBER (R_RISCV_OVLPLT_HI20, 223)
+  RELOC_NUMBER (R_RISCV_OVLPLT_LO12_I, 224)
+  RELOC_NUMBER (R_RISCV_OVLPLT32, 225)
 END_RELOC_NUMBERS (R_RISCV_max)
 
 /* Processor specific flags for the ELF header e_flags field.  */
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 048ab0a5d68..1e6807dc437 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -314,6 +314,7 @@ static const char * const riscv_pred_succ[16] =
 
 /* ABI names for selected x-registers.  */
 
+#define X_ZERO 0
 #define X_RA 1
 #define X_SP 2
 #define X_GP 3
@@ -322,6 +323,8 @@ static const char * const riscv_pred_succ[16] =
 #define X_T1 6
 #define X_T2 7
 #define X_T3 28
+#define X_T5 30
+#define X_T6 31
 
 #define NGPR 32
 #define NFPR 32
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 3fea7995481..d897d4150a3 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,110 @@
+2022-03-06  Edward Jones  <ed.jones@embecosm.com>
+
+	* emultempl/emulation.em (LDEMUL_EXTRA_EARLY_MAP_FILE_TEST):
+	Default to NULL.
+	* emultempl/riscvelf.em (riscv_grouping_file): Forward declare.
+	(DEFAULT_CRC_INIT DEFAULT_CRC_POLY DEFAULT_CRC_XOROUT)
+	(DEFAULT_CRC_REFIN DEFAULT_CRC_REFOUT): New defines.
+	(elf_riscv_before_parse): New function.
+	(riscv_elf_before_allocation): Add error for .ovlinput
+	prefixed input sections not assigned to .ovlgrps output
+	section.
+	(riscv_elf_after_check_relocs): New function.
+	(get_type_from_name_and_flags): Likewise.
+	(riscv_ovl_additional_link_map_text): Likewise.
+	(PARSE_AND_LIST_PROLOGUE): Set to add various
+	defines to the standard elf argument parsing.
+	(PARSE_AND_LIST_LONGOPTS): Add various overlay options
+	to standard option list.
+	(PARSE_AND_LIST_OPTIONS): Add various overlay short
+	options to standard elf option list.
+	(PARSE_AND_LIST_ARGS_CASES): Set to add cases to
+	standard elf argument parsing.
+	(LDEMUL_BEFORE_PARSE): Define to elf_riscv_before_parse.
+	(LDEMUL_AFTER_CHECK_RELOCS): Define to
+	riscv_elf_after_check_relocs.
+	(LDEMUL_EXTRA_EARLY_MAP_FILE_TEXT):
+	Define to =riscv_ovl_additional_link_map_text
+	* ld.h (sort_type): Add by_overlay sort type.
+	* ldemul.c (ldemul_extra_early_map_file_text): Add
+	new entry point.
+	* ldemul.h (ldemul_extra_early_map_file_text): Add
+	forward declaration.
+	(struct ld_emulation_xfer_struct): Add
+	extra_early_map_file_text member.
+	* ldlang.c (compare_section): Add handling of
+	by_overlay sort type.
+	(get_lang_memory_region_list): Add function.
+	(lang_map): Call ldemul_extra_early_map_text
+	at start of map file printing.
+	(set_overlay_sort_default): Add function.
+	(update_wild_statements): Force unsorted sections
+	assigned to the .ovlgrps output section to use
+	by_overlay sorting.
+	(print_wild_statement): Handle by_overlay sorting type.
+	(size_input_section): Force alignment of overlay
+	padding sections.
+	* ldlang.h (get_lang_memory_region_list): Add forward
+	declaration.
+	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Add
+	run_dump_test calls for overlay tests.
+	(riscv_get_testdir): Add procedure.
+	* testsuite/ld-riscv-elf/overlay-assign-dead.csv:
+	Added file.
+	* testsuite/ld-riscv-elf/overlay-assing-dead.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-assign-group0.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-assign-group0.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-assign-missing.csv:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-assign-missing.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-assign-non-contiguous.csv:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-assign-non-contiguous.d:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-autoassign-01.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-autoassign-02.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-autoassign-dead.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-big-funcs.s: Likewise.
+	* testsuite/ld-riscv-elf/overlay-dead-symbol.s: Likewise.
+	* testsuite/ld-riscv-elf/overlay-invalid-group-number.csv:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-invalid-group-number.d:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-many-func-group.csv:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-many-func-group.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-many-funcs.s: Likewise.
+	* testsuite/ld-riscv-elf/overlay-max-group-number.csv:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-max-group-number.d:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-max-size-group.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-max-size-group.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-multigroup-01.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-multigroup-01.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-multigroup-02.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-multigroup-02.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-no-grouping-file.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-partial-assign.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-partial-assign.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-plt.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-plt.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-plt.s: Likewise.
+	* testsuite/ld-riscv-elf/overlay-relax-01.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-relax-02.csv: Likewise.
+	* testsuite/ld-riscv-elf/overlay-relax-02.d: Likewise.
+	* testsuite/ld-riscv-elf/overlay-relax-multigroup.csv:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-relax-multigroup.d:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-relax.s: Likewise.
+	* testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.d:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.s:
+	Likewise.
+	* testsuite/ld-riscv-elf/overlay-trivial.s: Likewise.
+	* testsuite/ld-riscv-elf/overlay.ld: Likewise.
+
 2022-02-17  Roland McGrath  <mcgrathr@google.com>
 
 	* ld.texi (Output Section Type): Fix typo in @code syntax.
diff --git a/ld/emultempl/emulation.em b/ld/emultempl/emulation.em
index cfa6567ac2b..6fdbb559afe 100644
--- a/ld/emultempl/emulation.em
+++ b/ld/emultempl/emulation.em
@@ -32,6 +32,7 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
   ${LDEMUL_FIND_POTENTIAL_LIBRARIES-NULL},
   ${LDEMUL_NEW_VERS_PATTERN-NULL},
   ${LDEMUL_EXTRA_MAP_FILE_TEXT-NULL},
+  ${LDEMUL_EXTRA_EARLY_MAP_FILE_TEXT-NULL},
   ${LDEMUL_EMIT_CTF_EARLY-NULL},
   ${LDEMUL_ACQUIRE_STRINGS_FOR_CTF-NULL},
   ${LDEMUL_NEW_DYNSYM_FOR_CTF-NULL},
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index 645a807f239..f750e47b359 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -25,11 +25,54 @@ fragment <<EOF
 #include "elf/riscv.h"
 #include "elfxx-riscv.h"
 
+extern FILE *riscv_grouping_file;
+
+#define DEFAULT_CRC_INIT   0xffffffff
+#define DEFAULT_CRC_POLY   0x04c11db7
+#define DEFAULT_CRC_XOROUT 0x00000000
+#define DEFAULT_CRC_REFIN  false
+#define DEFAULT_CRC_REFOUT false
+
+static void
+elf_riscv_before_parse (void)
+{
+  /* Default initialize the CRC parameters.  */
+  riscv_crc_init   = DEFAULT_CRC_INIT;
+  riscv_crc_poly   = DEFAULT_CRC_POLY;
+  riscv_crc_xorout = DEFAULT_CRC_XOROUT;
+  riscv_crc_refin  = DEFAULT_CRC_REFIN;
+  riscv_crc_refout = DEFAULT_CRC_REFOUT;
+
+  gld${EMULATION_NAME}_before_parse ();
+}
+
 static void
 riscv_elf_before_allocation (void)
 {
   gld${EMULATION_NAME}_before_allocation ();
 
+  LANG_FOR_EACH_INPUT_STATEMENT(is)
+    {
+      asection *sec = is->the_bfd->sections;
+      while (sec != NULL)
+	{
+	  const char *secname = sec->name;
+	  const char *dstname = sec->output_section
+				? sec->output_section->name : "";
+	  /* Produce an error if the input section name starts with ".ovlinput"
+	     and the output name is not ".ovlgrps". Don't error if marked as
+	     excluded.  */
+	  if (!strncmp (secname, ".ovlinput", strlen (".ovlinput"))
+	      && strcmp (dstname, ".ovlgrps")
+	      && !(sec->flags & SEC_EXCLUDE))
+	    {
+	      einfo(_("%F%P: Input section %s not correctly placed in "
+		      ".ovlgrps\n"), secname);
+	    }
+	  sec = sec->next;
+	}
+    }
+
   if (link_info.discard == discard_sec_merge)
     link_info.discard = discard_l;
 
@@ -98,8 +141,298 @@ riscv_create_output_section_statements (void)
     }
 }
 
+extern void
+riscv_elf_overlay_hook_${EMULATION_NAME} (struct bfd_link_info *info);
+
+static void
+riscv_elf_after_check_relocs (void)
+{
+  riscv_elf_overlay_hook_${EMULATION_NAME} (&link_info);
+}
+
+static const char *
+get_type_from_name_and_flags (const char *name, flagword flags,
+			      bfd_vma elftype)
+{
+  if (flags & SEC_DEBUGGING)
+    return "Debug Information";
+  if (flags & SEC_CODE)
+    {
+      if (!strcmp (name, ".ovlgrps"))
+	return "Overlay Group Data";
+      return "Code";
+    }
+  if (flags & SEC_DATA)
+    {
+      if (elftype == SHT_INIT_ARRAY)
+	return "Constructor Array";
+      if (elftype == SHT_FINI_ARRAY)
+	return "Destructor Array";
+      if (!strcmp (name, ".eh_frame"))
+	return "Exception Handling Frame Information";
+      if (flags & SEC_READONLY)
+	return "Read-only Data";
+      return "Data";
+    }
+  if (elftype == SHT_NOBITS)
+    {
+      if (!strcmp (name, ".bss"))
+	return "BSS";
+      if (!strcmp (name, ".stack"))
+	return "Stack";
+      return "NOBITS";
+    }
+  if (elftype == 1879048195)
+    return "RISC-V Attributes";
+  if (elftype == SHT_PROGBITS)
+    return "PROGBITS";
+  return "Unknown";
+}
+
+static void
+riscv_ovl_additional_link_map_text (bfd *obfd,
+				   struct bfd_link_info *info ATTRIBUTE_UNUSED,
+				    FILE *mapfile)
+{
+  if (mapfile == NULL)
+    return;
+
+  lang_memory_region_type *m;
+
+  /* 1.1 Map file size summary  */
+  minfo ("\nMemory summary\n\n");
+  minfo ("MEMORY REGION                 MEMORY USED\n");
+  minfo ("    SECTION                   SECTION SIZE       TYPE\n");
+  for (m = get_lang_memory_region_list (); m!= NULL; m = m->next)
+    {
+      char szbuf[100];
+      bfd_vma memsize;
+      lang_output_section_statement_type *s;
+
+      /* Don't print the default memory region.  */
+      if (!strcmp (m->name_list.name, DEFAULT_MEMORY_REGION))
+	continue;
+
+      memsize = m->current - m->origin;
+      float memsizef = memsize / 1024.0;
+      char *suffix = "KiB";
+      if (memsizef >= 1024.0)
+	{
+	  memsizef /= 1024.0;
+	  suffix = "MiB";
+	}
+      if (memsizef >= 1024.0)
+	{
+	  memsizef /= 1024.0;
+	  suffix = "GiB";
+	}
+      sprintf (szbuf, "%3.2f", memsizef);
+      fprintf (config.map_file, "%-24s= %8li (%6s %s)\n", m->name_list.name,
+	       memsize, szbuf, suffix);
+
+      s = &lang_os_list.head->output_section_statement;
+      while (s != NULL)
+	{
+	  if (s->bfd_section != NULL && s->region == m)
+	    {
+	      asection *section = s->bfd_section;
+	      if (section->size > 0)
+		{
+		  float sectsizef = section->size / 1024.0;
+		  sprintf (szbuf, "%3.2f", sectsizef);
+		  fprintf (config.map_file, "    %-20s= %8li (%6s KiB)",
+			   section->name, section->size, szbuf);
+
+		  flagword flags = section->flags;
+		  unsigned sec_shndx =
+		    _bfd_elf_section_from_bfd_section (section->owner,
+						       section);
+		  Elf_Internal_Shdr *hdr =
+		    elf_elfsections (section->owner)[sec_shndx];
+		  fprintf (config.map_file, "  %s\n",
+			   get_type_from_name_and_flags (section->name,
+							 flags,
+							 hdr->sh_type));
+		}
+	    }
+	  s = s->next;
+	}
+    }
+
+  /* 1.2 Map file sections summary.  */
+  minfo ("\nSection summary\n\n");
+  minfo ("NAME                  START    END        TYPE\n");
+  {
+    lang_output_section_statement_type *s;
+    s = &lang_os_list.head->output_section_statement;
+    while (s != NULL)
+      {
+	if (s->bfd_section != NULL && s->bfd_section->owner == obfd)
+	  {
+	    asection *section = s->bfd_section;
+	    bfd_vma end = section->vma + section->size;
+	    flagword flags = section->flags;
+	    if (!(flags & SEC_EXCLUDE || flags & SEC_DEBUGGING))
+	      {
+		unsigned sec_shndx =
+		  _bfd_elf_section_from_bfd_section (section->owner, section);
+		Elf_Internal_Shdr *hdr =
+		  elf_elfsections (section->owner)[sec_shndx];
+		fprintf (config.map_file, "%-20s [%08lx-%08lx)",
+			 section->name, section->vma, end);
+		fprintf (config.map_file, "  %s\n",
+			 get_type_from_name_and_flags (section->name, flags,
+						       hdr->sh_type));
+	      }
+	  }
+	s = s->next;
+      }
+  }
+
+  minfo ("\n");
+}
+
 EOF
 
+# Define some shell vars to insert bits of code into the standard elf
+# parse_args and list_options functions.
+#
+PARSE_AND_LIST_PROLOGUE='
+#define OPTION_GROUPING_FILE		301
+#define OPTION_GROUPING_TOOL		302
+#define OPTION_GROUPING_TOOL_ARGS	303
+#define OPTION_FIRST_GROUP_NUMBER	304
+#define OPTION_OVERLAY_DEBUG		305
+#define OPTION_OVERLAY_CRC_INIT		306
+#define OPTION_OVERLAY_CRC_POLY		307
+#define OPTION_OVERLAY_CRC_XOROUT	308
+#define OPTION_OVERLAY_CRC_REFIN_ON	309
+#define OPTION_OVERLAY_CRC_REFIN_OFF	310
+#define OPTION_OVERLAY_CRC_REFOUT_ON	311
+#define OPTION_OVERLAY_CRC_REFOUT_OFF	312
+'
+
+PARSE_AND_LIST_LONGOPTS='
+  { "grouping-file",        required_argument, NULL, OPTION_GROUPING_FILE },
+  { "grouping-tool",        required_argument, NULL, OPTION_GROUPING_TOOL },
+  { "grouping-tool-args",   required_argument, NULL, OPTION_GROUPING_TOOL_ARGS },
+  { "first-group-number",   required_argument, NULL, OPTION_FIRST_GROUP_NUMBER },
+  { "overlay-debug",        no_argument,       NULL, OPTION_OVERLAY_DEBUG },
+  { "overlay-crcinit",      required_argument, NULL, OPTION_OVERLAY_CRC_INIT },
+  { "overlay-crcpolynomial",required_argument, NULL, OPTION_OVERLAY_CRC_POLY },
+  { "overlay-crcxorout",    required_argument, NULL, OPTION_OVERLAY_CRC_XOROUT },
+  { "overlay-crcrefin",     no_argument,       NULL, OPTION_OVERLAY_CRC_REFIN_ON },
+  { "overlay-crcnorefin",   no_argument,       NULL, OPTION_OVERLAY_CRC_REFIN_OFF },
+  { "overlay-crcrefout",    no_argument,       NULL, OPTION_OVERLAY_CRC_REFOUT_ON },
+  { "overlay-crcnorefout",  no_argument,       NULL, OPTION_OVERLAY_CRC_REFOUT_OFF },
+'
+
+PARSE_AND_LIST_OPTIONS='
+  fprintf (file, _("--grouping-file             Grouping file name\n"));
+  fprintf (file, _("--grouping-tool             Name of the grouping tool command\n"));
+  fprintf (file, _("--grouping-tool-args        Arguments to the grouping tool\n"));
+  fprintf (file, _("--first-group-number        First group number for autogrouping\n"));
+  fprintf (file, _("--overlay-crcinit           Initilization value used for CRCs\n"));
+  fprintf (file, _("--overlay-crcpolynomial     Polynomial used for overlay CRCs\n"));
+  fprintf (file, _("--overlay-crcxorout         Final XOR value used for overlay CRCs\n"));
+  fprintf (file, _("--overlay-crcrefin          Enable input reflection for overlay CRCs\n"));
+  fprintf (file, _("--overlay-crcnorefin        Disable input reflection for overlay CRCs\n"));
+  fprintf (file, _("--overlay-crcrefout         Enable output reflection for overlay CRCs\n"));
+  fprintf (file, _("--overlay-crcnorefout       Disable output reflection for overlay CRCs\n"));
+'
+
+PARSE_AND_LIST_ARGS_CASES='
+    case OPTION_GROUPING_FILE:
+      if (riscv_use_grouping_tool)
+	einfo (_("--grouping-file provided, but --grouping-tools-args already "
+		 "specified"), optarg);
+      riscv_grouping_file = fopen (optarg, FOPEN_RT);
+      if (riscv_grouping_file == NULL)
+	einfo (_("%F%P: cannot open grouping file %s\n"), optarg);
+      break;
+    case OPTION_GROUPING_TOOL:
+      if (riscv_grouping_file != NULL)
+	einfo (_("--grouping-tool provided, but --grouping-file already "
+		 "specified"), optarg);
+      riscv_use_grouping_tool = true;
+      riscv_grouping_tool = malloc (strlen (optarg) + 1);
+      strcpy (riscv_grouping_tool, optarg);
+      riscv_grouping_tool[strlen (optarg)] = '\0';
+      break;
+    case OPTION_GROUPING_TOOL_ARGS:
+      if (riscv_grouping_file != NULL)
+	einfo (_("--grouping-tool-args provided, but --grouping-file already "
+		 "specified"), optarg);
+      riscv_use_grouping_tool = true;
+      riscv_grouping_tool_args = malloc (strlen (optarg) + 1);
+      strcpy (riscv_grouping_tool_args, optarg);
+      riscv_grouping_tool_args[strlen (optarg)] = '\0';
+      break;
+    case OPTION_FIRST_GROUP_NUMBER:
+      {
+	const char *end;
+	riscv_ovl_first_group_number = bfd_scan_vma (optarg, &end, 0);
+	if (*end != '\0' || riscv_ovl_first_group_number <= 0)
+	  einfo (_("%P: warning: ignoring invalid --first-group-number value "
+		   "%s\n"), optarg);
+      }
+      break;
+    case OPTION_OVERLAY_DEBUG:
+      riscv_overlay_debug = true;
+      break;
+    case OPTION_OVERLAY_CRC_INIT:
+      {
+	const char *end;
+	riscv_crc_init = bfd_scan_vma (optarg, &end, 0);
+	if (*end != 0 || riscv_crc_init >= 0xffffffffU)
+	  {
+	    einfo (_("%P: warning: ignoring invalid overlay CRC inital value "
+		     "%s\n"), optarg);
+	    riscv_crc_init = DEFAULT_CRC_INIT;
+	  }
+      }
+      break;
+    case OPTION_OVERLAY_CRC_POLY:
+      {
+	const char *end;
+	riscv_crc_poly = bfd_scan_vma (optarg, &end, 0);
+	if (*end != 0 || riscv_crc_poly >= 0xffffffffU)
+	  {
+	    einfo (_("%P: warning: ignoring invalid overlay CRC polynomial "
+		     "%s\n"), optarg);
+	    riscv_crc_poly = DEFAULT_CRC_POLY;
+	  }
+      }
+      break;
+    case OPTION_OVERLAY_CRC_XOROUT:
+      {
+	const char *end;
+	riscv_crc_xorout = bfd_scan_vma (optarg, &end, 0);
+	if (*end != 0 || riscv_crc_xorout >= 0xffffffffU)
+	  {
+	    einfo (_("%P: warning: ignoring invalid overlay CRC xorout %s\n"),
+		   optarg);
+	    riscv_crc_xorout = DEFAULT_CRC_XOROUT;
+	  }
+      }
+      break;
+    case OPTION_OVERLAY_CRC_REFIN_ON:
+      riscv_crc_refin = true;
+      break;
+    case OPTION_OVERLAY_CRC_REFIN_OFF:
+      riscv_crc_refin = false;
+      break;
+    case OPTION_OVERLAY_CRC_REFOUT_ON:
+      riscv_crc_refout = true;
+      break;
+    case OPTION_OVERLAY_CRC_REFOUT_OFF:
+      riscv_crc_refout = false;
+      break;
+'
+
+LDEMUL_BEFORE_PARSE=elf_riscv_before_parse
 LDEMUL_BEFORE_ALLOCATION=riscv_elf_before_allocation
 LDEMUL_AFTER_ALLOCATION=gld${EMULATION_NAME}_after_allocation
 LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=riscv_create_output_section_statements
+LDEMUL_AFTER_CHECK_RELOCS=riscv_elf_after_check_relocs
+LDEMUL_EXTRA_EARLY_MAP_FILE_TEXT=riscv_ovl_additional_link_map_text
diff --git a/ld/ld.h b/ld/ld.h
index f3086bf30de..9e21ca29f69 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -89,7 +89,7 @@ typedef enum {sort_none, sort_ascending, sort_descending} sort_order;
 typedef enum
 {
   none, by_name, by_alignment, by_name_alignment, by_alignment_name,
-  by_none, by_init_priority
+  by_none, by_init_priority, by_overlay
 } sort_type;
 
 extern sort_type sort_section;
diff --git a/ld/ldemul.c b/ld/ldemul.c
index 85c00ded75e..b82ff87a83d 100644
--- a/ld/ldemul.c
+++ b/ld/ldemul.c
@@ -403,6 +403,14 @@ ldemul_extra_map_file_text (bfd *abfd, struct bfd_link_info *info, FILE *mapf)
     ld_emulation->extra_map_file_text (abfd, info, mapf);
 }
 
+void
+ldemul_extra_early_map_file_text (bfd *abfd, struct bfd_link_info *info,
+				  FILE *mapf)
+{
+  if (ld_emulation->extra_early_map_file_text)
+    ld_emulation->extra_early_map_file_text (abfd, info, mapf);
+}
+
 int
 ldemul_emit_ctf_early (void)
 {
diff --git a/ld/ldemul.h b/ld/ldemul.h
index 33e690d78ac..6fa0279ecdc 100644
--- a/ld/ldemul.h
+++ b/ld/ldemul.h
@@ -104,6 +104,8 @@ extern struct bfd_elf_version_expr *ldemul_new_vers_pattern
   (struct bfd_elf_version_expr *);
 extern void ldemul_extra_map_file_text
   (bfd *, struct bfd_link_info *, FILE *);
+extern void ldemul_extra_early_map_file_text
+  (bfd *, struct bfd_link_info *, FILE *);
 /* Return 1 if we are emitting CTF early, and 0 if ldemul_examine_strtab_for_ctf
    will be called by the target.  */
 extern int ldemul_emit_ctf_early
@@ -227,6 +229,12 @@ typedef struct ld_emulation_xfer_struct {
   void (*extra_map_file_text)
     (bfd *, struct bfd_link_info *, FILE *);
 
+  /* Called when printing the map file, in case there are
+     emulation-specific sections for it, before most of the file is
+     printed.  */
+  void (*extra_early_map_file_text)
+    (bfd *, struct bfd_link_info *, FILE *);
+
   /* If this returns true, we emit CTF as early as possible: if false, we emit
      CTF once the strtab and symtab are laid out.  */
   int (*emit_ctf_early)
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 1733f8e65c4..d0f7b408591 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -521,6 +521,11 @@ compare_section (sort_type sort, asection *asec, asection *bsec)
     case by_alignment:
       ret = bfd_section_alignment (bsec) - bfd_section_alignment (asec);
       break;
+
+    case by_overlay:
+      ret = bfd_get_section_overlay_sort_data (bsec->owner, bsec, &link_info)
+	    - bfd_get_section_overlay_sort_data (asec->owner, asec, &link_info);
+      break;
     }
 
   return ret;
@@ -1390,6 +1395,12 @@ lang_memory_region_lookup (const char *const name, bool create)
   return new_region;
 }
 
+lang_memory_region_type *
+get_lang_memory_region_list (void)
+{
+  return lang_memory_region_list;
+}
+
 void
 lang_memory_region_alias (const char *alias, const char *region_name)
 {
@@ -2267,6 +2278,9 @@ lang_map (void)
   lang_memory_region_type *m;
   bool dis_header_printed = false;
 
+  ldemul_extra_early_map_file_text (link_info.output_bfd, &link_info,
+				    config.map_file);
+
   LANG_FOR_EACH_INPUT_STATEMENT (file)
     {
       asection *s;
@@ -4134,6 +4148,18 @@ check_input_sections
     }
 }
 
+static void
+set_overlay_sort_default (lang_statement_union_type *s)
+{
+  struct wildcard_list *sec;
+
+  for (; s != NULL; s = s->header.next)
+    if (s->header.type == lang_wild_statement_enum)
+      for (sec = s->wild_statement.section_list; sec != NULL;
+	   sec = sec->next)
+	sec->spec.sorted = by_overlay;
+}
+
 /* Update wildcard statements if needed.  */
 
 static void
@@ -4147,6 +4173,12 @@ update_wild_statements (lang_statement_union_type *s)
       FAIL ();
 
     case none:
+      /* The case of no user provided default, set the .ovlgrps output section
+         to always use by_overlay sorting.  */
+      for (; s != NULL; s = s->header.next)
+	if (s->header.type == lang_output_section_statement_enum)
+	  if (!strcmp (s->output_section_statement.name, ".ovlgrps"))
+	    set_overlay_sort_default (s->output_section_statement.children.head);
       break;
 
     case by_name:
@@ -5147,6 +5179,11 @@ print_wild_statement (lang_wild_statement_type *w,
 	  minfo ("SORT_BY_INIT_PRIORITY(");
 	  closing_paren = 1;
 	  break;
+
+	case by_overlay:
+	  minfo ("SORT_BY_OVERLAY(");
+	  closing_paren = 1;
+	  break;
 	}
 
       if (sec->spec.exclude_name_list != NULL)
@@ -5442,6 +5479,17 @@ size_input_section
       /* Remember where in the output section this input section goes.  */
       i->output_offset = dot - o->vma;
 
+      /* FIXME: Where to put this?  */
+      /* If the section is a target-specific alignment section, size it
+         accordingly.  */
+      if (!strncmp (i->name, ".ovlinput.__internal.padding.",
+		    strlen (".ovlinput.__internal.padding.")))
+	{
+	  i->size = align_power (dot, 9) - dot;
+	  if (i->size < 4 /*CRC_SZ*/)
+	    i->size += 512;
+	}
+
       /* Mark how big the output section must be to contain this now.  */
       dot += TO_ADDR (i->size);
       if (!(o->flags & SEC_FIXED_SIZE))
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 95f6e468b30..5c01e311112 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -538,6 +538,8 @@ extern void lang_finish
   (void);
 extern lang_memory_region_type * lang_memory_region_lookup
   (const char * const, bool);
+extern lang_memory_region_type * get_lang_memory_region_list
+  (void);
 extern void lang_memory_region_alias
   (const char *, const char *);
 extern void lang_map
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 272424b33e3..a8cfcc9fb3e 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -37,6 +37,12 @@ proc riscv_choose_lp64_emul {} {
     return "elf64lriscv"
 }
 
+proc riscv_get_testdir {} {
+  global srcdir
+  global subdir
+  return $srcdir/$subdir
+}
+
 # target: rv32 or rv64.
 # output: Which output you want?  (exe, pie, .so)
 proc run_dump_test_ifunc { name target output} {
@@ -177,6 +183,27 @@ if [istarget "riscv*-*-*"] {
 	    "-march=rv64i -mabi=lp64" {weakref64.s} \
 	    {{objdump -d weakref64.d}} "weakref64"]]
 
+    run_dump_test "overlay-assign-dead"
+    run_dump_test "overlay-assign-group0"
+    run_dump_test "overlay-assign-missing"
+    run_dump_test "overlay-assign-non-contiguous"
+    run_dump_test "overlay-autoassign-01"
+    run_dump_test "overlay-autoassign-02"
+    run_dump_test "overlay-autoassign-dead"
+    run_dump_test "overlay-invalid-group-number"
+    run_dump_test "overlay-many-func-group"
+    run_dump_test "overlay-max-group-number"
+    run_dump_test "overlay-max-size-group"
+    run_dump_test "overlay-multigroup-01"
+    run_dump_test "overlay-multigroup-02"
+    run_dump_test "overlay-no-grouping-file"
+    run_dump_test "overlay-partial-assign"
+    run_dump_test "overlay-plt"
+    run_dump_test "overlay-relax-01"
+    run_dump_test "overlay-relax-02"
+    run_dump_test "overlay-relax-multigroup"
+    run_dump_test "overlay-symbol-not-in-ovlinput"
+
     # The following tests require shared library support.
     if ![check_shared_lib_support] {
 	return
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-dead.csv b/ld/testsuite/ld-riscv-elf/overlay-assign-dead.csv
new file mode 100644
index 00000000000..71b68600b66
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-dead.csv
@@ -0,0 +1 @@
+dead_symbol,3
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-dead.d b/ld/testsuite/ld-riscv-elf/overlay-assign-dead.d
new file mode 100644
index 00000000000..86e26d6ca1c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-dead.d
@@ -0,0 +1,12 @@
+#source: overlay-dead-symbol.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-assign-dead.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	00008067          	ret
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-group0.csv b/ld/testsuite/ld-riscv-elf/overlay-assign-group0.csv
new file mode 100644
index 00000000000..2e3cf5b018d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-group0.csv
@@ -0,0 +1 @@
+apple,0
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-group0.d b/ld/testsuite/ld-riscv-elf/overlay-assign-group0.d
new file mode 100644
index 00000000000..3f96513a828
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-group0.d
@@ -0,0 +1,4 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-assign-group0.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#error: .*Invalid group id.*0.*in overlay grouping file.*
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-missing.csv b/ld/testsuite/ld-riscv-elf/overlay-assign-missing.csv
new file mode 100644
index 00000000000..c5c6c8682e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-missing.csv
@@ -0,0 +1 @@
+missing_function,23
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-missing.d b/ld/testsuite/ld-riscv-elf/overlay-assign-missing.d
new file mode 100644
index 00000000000..ecd409ffce4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-missing.d
@@ -0,0 +1,31 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-assign-missing.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	00308093          	addi	ra,ra,3.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00000002.*
+
+00800008 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	feef78de.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+  800204:	0001.*
+  800206:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.csv b/ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.csv
new file mode 100644
index 00000000000..187827ffac7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.csv
@@ -0,0 +1,6 @@
+apple,5
+banana,8
+duck,11
+elephant,12
+cabbage,45
+food,1
diff --git a/ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.d b/ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.d
new file mode 100644
index 00000000000..7580c69a33a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-assign-non-contiguous.d
@@ -0,0 +1,89 @@
+#source: overlay-many-funcs.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-assign-non-contiguous.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	00b08093          	addi	ra,ra,11.*
+   8:	000000b7          	lui	ra,0x0
+   c:	01108093          	addi	ra,ra,17.*
+  10:	000000b7          	lui	ra,0x0
+  14:	05b08093          	addi	ra,ra,91.*
+  18:	000000b7          	lui	ra,0x0
+  1c:	01708093          	addi	ra,ra,23.*
+  20:	000000b7          	lui	ra,0x0
+  24:	01908093          	addi	ra,ra,25.*
+  28:	000000b7          	lui	ra,0x0
+  2c:	00308093          	addi	ra,ra,3.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00020002 00020002 00030003.*
+  800010:	00040003 00040004 00060005 00060006.*
+  800020:	00060006 00060006 00060006 00060006.*
+  800030:	00060006 00060006 00060006 00060006.*
+  800040:	00060006 00060006 00060006 00060006.*
+  800050:	00060006 00060006 00060006 00000007.*
+
+00800060 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	652501e5.*
+
+00800200 <food>:
+  800200:	00008067          	ret
+  800204:	0001.*
+  800206:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
+
+00800400 <apple>:
+  800400:	00008067          	ret
+  800404:	0005.*
+  800406:	0005.*
+#...
+  8005f8:	0005.*
+  8005fa:	0005.*
+#...
+
+00800600 <banana>:
+  800600:	00008067          	ret
+  800604:	0008.*
+  800606:	0008.*
+#...
+  8007f8:	0008.*
+  8007fa:	0008.*
+#...
+
+00800800 <duck>:
+  800800:	00008067          	ret
+  800804:	000b000b.*
+#...
+  8009f8:	000b000b.*
+#...
+
+00800a00 <elephant>:
+  800a00:	00008067          	ret
+  800a04:	000c.*
+  800a06:	000c.*
+#...
+  800bf8:	000c.*
+  800bfa:	000c.*
+#...
+
+00800c00 <cabbage>:
+  800c00:	00008067          	ret
+  800c04:	002d.*
+  800c06:	002d.*
+#...
+  800df8:	002d.*
+  800dfa:	002d.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-autoassign-01.d b/ld/testsuite/ld-riscv-elf/overlay-autoassign-01.d
new file mode 100644
index 00000000000..9f1fcd7185d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-autoassign-01.d
@@ -0,0 +1,31 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	00308093          	addi	ra,ra,3.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00000002.*
+
+00800008 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	feef78de.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+  800204:	0001.*
+  800206:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-autoassign-02.d b/ld/testsuite/ld-riscv-elf/overlay-autoassign-02.d
new file mode 100644
index 00000000000..3b2396223be
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-autoassign-02.d
@@ -0,0 +1,85 @@
+#source: overlay-many-funcs.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	00308093          	addi	ra,ra,3.*
+   8:	000000b7          	lui	ra,0x0
+   c:	00508093          	addi	ra,ra,5.*
+  10:	000000b7          	lui	ra,0x0
+  14:	00708093          	addi	ra,ra,7.*
+  18:	000000b7          	lui	ra,0x0
+  1c:	00908093          	addi	ra,ra,9.*
+  20:	000000b7          	lui	ra,0x0
+  24:	00b08093          	addi	ra,ra,11.*
+  28:	000000b7          	lui	ra,0x0
+  2c:	00d08093          	addi	ra,ra,13.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00030002 00050004 00070006.*
+  800010:	00000000.*
+
+00800014 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	47b66d24.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+  800204:	0001.*
+  800206:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
+
+00800400 <banana>:
+  800400:	00008067          	ret
+  800404:	0002.*
+  800406:	0002.*
+#...
+  8005f8:	0002.*
+  8005fa:	0002.*
+#...
+
+00800600 <cabbage>:
+  800600:	00008067          	ret
+  800604:	00030003.*
+#...
+  8007f8:	00030003.*
+#...
+
+00800800 <duck>:
+  800800:	00008067          	ret
+  800804:	0004.*
+  800806:	0004.*
+#...
+  8009f8:	0004.*
+  8009fa:	0004.*
+#...
+
+00800a00 <elephant>:
+  800a00:	00008067          	ret
+  800a04:	0005.*
+  800a06:	0005.*
+#...
+  800bf8:	0005.*
+  800bfa:	0005.*
+#...
+
+00800c00 <food>:
+  800c00:	00008067          	ret
+  800c04:	0006.*
+  800c06:	0006.*
+#...
+  800df8:	0006.*
+  800dfa:	0006.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-autoassign-dead.d b/ld/testsuite/ld-riscv-elf/overlay-autoassign-dead.d
new file mode 100644
index 00000000000..4d1e3ee57eb
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-autoassign-dead.d
@@ -0,0 +1,12 @@
+#source: overlay-dead-symbol.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	00008067          	ret
diff --git a/ld/testsuite/ld-riscv-elf/overlay-big-funcs.s b/ld/testsuite/ld-riscv-elf/overlay-big-funcs.s
new file mode 100644
index 00000000000..1970f3584b1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-big-funcs.s
@@ -0,0 +1,64 @@
+  .section .ovlinput.apple,"ax",@progbits
+  .global  apple
+  .p2align  2
+  .type    apple,@function
+apple:
+  .fill 64, 1, 0xa
+  ret
+
+  .section .ovlinput.banana,"ax",@progbits
+  .global  banana
+  .p2align  2
+  .type    banana,@function
+banana:
+  .fill 128, 1, 0xb
+  ret
+
+  .section .ovlinput.cabbage,"ax",@progbits
+  .global  cabbage
+  .p2align  2
+  .type    cabbage,@function
+cabbage:
+  .fill 256, 1, 0xc
+  ret
+
+  .section .ovlinput.duck,"ax",@progbits
+  .global  duck
+  .p2align  2
+  .type    duck,@function
+duck:
+  .fill 512, 1, 0xd
+  ret
+
+  .section .ovlinput.elephant,"ax",@progbits
+  .global  elephant
+  .p2align  2
+  .type    elephant,@function
+elephant:
+  .fill 2048, 1, 0xe
+  ret
+
+  .section .ovlinput.food,"ax",@progbits
+  .global  food
+  .p2align  2
+  .type    food,@function
+food:
+  .fill 2048, 1, 0xf
+  ret
+
+  .text
+  .global  _start
+  .type    _start,@function
+_start:
+  lui  x1, %ovltok_hi(apple)
+  addi x1, x1, %ovltok_lo(apple)
+  lui  x1, %ovltok_hi(banana)
+  addi x1, x1, %ovltok_lo(banana)
+  lui  x1, %ovltok_hi(cabbage)
+  addi x1, x1, %ovltok_lo(cabbage)
+  lui  x1, %ovltok_hi(duck)
+  addi x1, x1, %ovltok_lo(duck)
+  lui  x1, %ovltok_hi(elephant)
+  addi x1, x1, %ovltok_lo(elephant)
+  lui  x1, %ovltok_hi(food)
+  addi x1, x1, %ovltok_lo(food)
diff --git a/ld/testsuite/ld-riscv-elf/overlay-dead-symbol.s b/ld/testsuite/ld-riscv-elf/overlay-dead-symbol.s
new file mode 100644
index 00000000000..b7ed395bed7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-dead-symbol.s
@@ -0,0 +1,12 @@
+  .section .ovlinput.dead_symbol,"ax",@progbits
+  .global  dead_symbol
+  .p2align  2
+  .type    dead_symbol,@function
+dead_symbol:
+  ret
+
+  .text
+  .global  _start
+  .type    _start,@function
+_start:
+  ret
diff --git a/ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.csv b/ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.csv
new file mode 100644
index 00000000000..598eaafa47a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.csv
@@ -0,0 +1 @@
+apple,65536
diff --git a/ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.d b/ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.d
new file mode 100644
index 00000000000..ff532591a41
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-invalid-group-number.d
@@ -0,0 +1,4 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-invalid-group-number.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#error: .*Invalid group id.*in overlay grouping file: group number greater than maximum \(65535\)\..*
diff --git a/ld/testsuite/ld-riscv-elf/overlay-many-func-group.csv b/ld/testsuite/ld-riscv-elf/overlay-many-func-group.csv
new file mode 100644
index 00000000000..216484713a3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-many-func-group.csv
@@ -0,0 +1,6 @@
+apple,1
+banana,1
+cabbage,1
+duck,4
+elephant,4
+food,2
diff --git a/ld/testsuite/ld-riscv-elf/overlay-many-func-group.d b/ld/testsuite/ld-riscv-elf/overlay-many-func-group.d
new file mode 100644
index 00000000000..be71dbde819
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-many-func-group.d
@@ -0,0 +1,68 @@
+#source: overlay-many-funcs.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-many-func-group.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	00308093          	addi	ra,ra,3.*
+   8:	000200b7          	lui	ra,0x20
+   c:	00308093          	addi	ra,ra,3.*
+  10:	000400b7          	lui	ra,0x40
+  14:	00308093          	addi	ra,ra,3.*
+  18:	000000b7          	lui	ra,0x0
+  1c:	00908093          	addi	ra,ra,9.*
+  20:	000200b7          	lui	ra,0x20
+  24:	00908093          	addi	ra,ra,9.*
+  28:	000000b7          	lui	ra,0x0
+  2c:	00508093          	addi	ra,ra,5.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00030002 00040003 00000000.*
+
+00800010 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	5a03df8d.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+
+00800204 <banana>:
+  800204:	00008067          	ret
+
+00800208 <cabbage>:
+  800208:	00008067          	ret
+  80020c:	0001.*
+  80020e:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
+
+00800400 <food>:
+  800400:	00008067          	ret
+  800404:	0002.*
+  800406:	0002.*
+#...
+  8005f8:	0002.*
+  8005fa:	0002.*
+#...
+
+00800600 <duck>:
+  800600:	00008067          	ret
+
+00800604 <elephant>:
+  800604:	00008067          	ret
+  800608:	0004.*
+  80060a:	0004.*
+#...
+  8007f8:	0004.*
+  8007fa:	0004.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-many-funcs.s b/ld/testsuite/ld-riscv-elf/overlay-many-funcs.s
new file mode 100644
index 00000000000..03413507b9c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-many-funcs.s
@@ -0,0 +1,58 @@
+  .section .ovlinput.apple,"ax",@progbits
+  .global  apple
+  .p2align  2
+  .type    apple,@function
+apple:
+  ret
+
+  .section .ovlinput.banana,"ax",@progbits
+  .global  banana
+  .p2align  2
+  .type    banana,@function
+banana:
+  ret
+
+  .section .ovlinput.cabbage,"ax",@progbits
+  .global  cabbage
+  .p2align  2
+  .type    cabbage,@function
+cabbage:
+  ret
+
+  .section .ovlinput.duck,"ax",@progbits
+  .global  duck
+  .p2align  2
+  .type    duck,@function
+duck:
+  ret
+
+  .section .ovlinput.elephant,"ax",@progbits
+  .global  elephant
+  .p2align  2
+  .type    elephant,@function
+elephant:
+  ret
+
+  .section .ovlinput.food,"ax",@progbits
+  .global  food
+  .p2align  2
+  .type    food,@function
+food:
+  ret
+
+  .text
+  .global  _start
+  .type    _start,@function
+_start:
+  lui  x1, %ovltok_hi(apple)
+  addi x1, x1, %ovltok_lo(apple)
+  lui  x1, %ovltok_hi(banana)
+  addi x1, x1, %ovltok_lo(banana)
+  lui  x1, %ovltok_hi(cabbage)
+  addi x1, x1, %ovltok_lo(cabbage)
+  lui  x1, %ovltok_hi(duck)
+  addi x1, x1, %ovltok_lo(duck)
+  lui  x1, %ovltok_hi(elephant)
+  addi x1, x1, %ovltok_lo(elephant)
+  lui  x1, %ovltok_hi(food)
+  addi x1, x1, %ovltok_lo(food)
diff --git a/ld/testsuite/ld-riscv-elf/overlay-max-group-number.csv b/ld/testsuite/ld-riscv-elf/overlay-max-group-number.csv
new file mode 100644
index 00000000000..5d3ddd54153
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-max-group-number.csv
@@ -0,0 +1 @@
+apple,65535
diff --git a/ld/testsuite/ld-riscv-elf/overlay-max-group-number.d b/ld/testsuite/ld-riscv-elf/overlay-max-group-number.d
new file mode 100644
index 00000000000..6faf7be43f1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-max-group-number.d
@@ -0,0 +1,33 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-max-group-number.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000200b7          	lui	ra,0x20
+   4:	fff08093          	addi	ra,ra,-1.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	01010000 01010101 01010101 01010101.*
+#...
+  81fff0:	01010101 01010101 01010101 01010101.*
+  820000:	00000102.*
+
+00820004 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8201fc:	a720db76.*
+
+00820200 <apple>:
+  820200:	00008067          	ret
+  820204:	ffff.*
+#...
+  8203f8:	ffff.*
+  8203fa:	ffff.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-max-size-group.csv b/ld/testsuite/ld-riscv-elf/overlay-max-size-group.csv
new file mode 100644
index 00000000000..b6c26ce3e58
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-max-size-group.csv
@@ -0,0 +1,6 @@
+apple,1
+banana,1
+cabbage,1
+duck,2
+elephant,2
+food,2
diff --git a/ld/testsuite/ld-riscv-elf/overlay-max-size-group.d b/ld/testsuite/ld-riscv-elf/overlay-max-size-group.d
new file mode 100644
index 00000000000..3a936feb12f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-max-size-group.d
@@ -0,0 +1,4 @@
+#source: overlay-big-funcs.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-max-size-group.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#error: .*overlay group .* exceeds maximum group size.*
diff --git a/ld/testsuite/ld-riscv-elf/overlay-multigroup-01.csv b/ld/testsuite/ld-riscv-elf/overlay-multigroup-01.csv
new file mode 100644
index 00000000000..42d808412f1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-multigroup-01.csv
@@ -0,0 +1 @@
+apple,1,2,3,4,5,6,7,8,9,10
diff --git a/ld/testsuite/ld-riscv-elf/overlay-multigroup-01.d b/ld/testsuite/ld-riscv-elf/overlay-multigroup-01.d
new file mode 100644
index 00000000000..14cfa091eb8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-multigroup-01.d
@@ -0,0 +1,114 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-multigroup-01.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	800000b7          	lui	ra,0x80000
+   4:	00108093          	addi	ra,ra,1.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00030002 00050004 00070006.*
+  800010:	00090008 000b000a 00000000.*
+
+0080001c <__OVERLAY_MULTIGROUP_TABLE_START>:
+  80001c:	00000003 00000005 00000007 00000009.*
+  80002c:	0000000b 0000000d 0000000f 00000011.*
+  80003c:	00000013 00000015 00000000.*
+
+00800048 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	1157296b.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+  800204:	0001.*
+  800206:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
+
+00800400 <apple\$group2>:
+  800400:	00008067          	ret
+  800404:	0002.*
+  800406:	0002.*
+#...
+  8005f8:	0002.*
+  8005fa:	0002.*
+#...
+
+00800600 <apple\$group3>:
+  800600:	00008067          	ret
+  800604:	00030003.*
+#...
+  8007f8:	00030003.*
+#...
+
+00800800 <apple\$group4>:
+  800800:	00008067          	ret
+  800804:	0004.*
+  800806:	0004.*
+#...
+  8009f8:	0004.*
+  8009fa:	0004.*
+#...
+
+00800a00 <apple\$group5>:
+  800a00:	00008067          	ret
+  800a04:	0005.*
+  800a06:	0005.*
+#...
+  800bf8:	0005.*
+  800bfa:	0005.*
+#...
+
+00800c00 <apple\$group6>:
+  800c00:	00008067          	ret
+  800c04:	0006.*
+  800c06:	0006.*
+#...
+  800df8:	0006.*
+  800dfa:	0006.*
+#...
+
+00800e00 <apple\$group7>:
+  800e00:	00008067          	ret
+  800e04:	00070007.*
+#...
+  800ff8:	00070007.*
+#...
+
+00801000 <apple\$group8>:
+  801000:	00008067          	ret
+  801004:	0008.*
+  801006:	0008.*
+#...
+  8011f8:	0008.*
+  8011fa:	0008.*
+#...
+
+00801200 <apple\$group9>:
+  801200:	00008067          	ret
+  801204:	0009.*
+  801206:	0009.*
+#...
+  8013f8:	0009.*
+  8013fa:	0009.*
+#...
+
+00801400 <apple\$group10>:
+  801400:	00008067          	ret
+  801404:	000a.*
+  801406:	000a.*
+#...
+  8015f8:	000a.*
+  8015fa:	000a.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-multigroup-02.csv b/ld/testsuite/ld-riscv-elf/overlay-multigroup-02.csv
new file mode 100644
index 00000000000..e1bf0c5d2b5
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-multigroup-02.csv
@@ -0,0 +1,6 @@
+apple,1,2
+banana,2,3
+cabbage,3,4,5,6
+duck,5,6
+elephant,5,6,23
+food,1,2,3,5,22,23,24
diff --git a/ld/testsuite/ld-riscv-elf/overlay-multigroup-02.d b/ld/testsuite/ld-riscv-elf/overlay-multigroup-02.d
new file mode 100644
index 00000000000..25502906407
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-multigroup-02.d
@@ -0,0 +1,154 @@
+#source: overlay-many-funcs.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-multigroup-02.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	800000b7          	lui	ra,0x80000
+   4:	00108093          	addi	ra,ra,1.*
+   8:	800000b7          	lui	ra,0x80000
+   c:	00708093          	addi	ra,ra,7.*
+  10:	800000b7          	lui	ra,0x80000
+  14:	00d08093          	addi	ra,ra,13.*
+  18:	800000b7          	lui	ra,0x80000
+  1c:	01708093          	addi	ra,ra,23.*
+  20:	800000b7          	lui	ra,0x80000
+  24:	01d08093          	addi	ra,ra,29.*
+  28:	800000b7          	lui	ra,0x80000
+  2c:	02508093          	addi	ra,ra,37.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00030002 00050004 00070006.*
+  800010:	00070007 00070007 00070007 00070007.*
+  800020:	00070007 00070007 00070007 00080007.*
+  800030:	000a0009 00000000.*
+
+00800038 <__OVERLAY_MULTIGROUP_TABLE_START>:
+  800038:	00000003 00000005 00000000 00020005.*
+  800048:	00000007 00000000 00020007 00000009.*
+  800058:	0000000b 0000000d 00000000 0002000b.*
+  800068:	0002000d 00000000 0004000b 0004000d.*
+  800078:	0000002f 00000000 00020003 00040005.*
+  800088:	00040007 0006000b 0000002d 0002002f.*
+  800098:	00000031 00000000.*
+
+008000a0 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	b8df17d7.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+
+00800204 <food>:
+  800204:	00008067          	ret
+  800208:	0001.*
+  80020a:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
+
+00800400 <apple\$group2>:
+  800400:	00008067          	ret
+
+00800404 <banana>:
+  800404:	00008067          	ret
+
+00800408 <food\$group2>:
+  800408:	00008067          	ret
+  80040c:	0002.*
+  80040e:	0002.*
+#...
+  8005f8:	0002.*
+  8005fa:	0002.*
+#...
+
+00800600 <banana\$group3>:
+  800600:	00008067          	ret
+
+00800604 <cabbage>:
+  800604:	00008067          	ret
+
+00800608 <food\$group3>:
+  800608:	00008067          	ret
+  80060c:	00030003.*
+#...
+  8007f8:	00030003.*
+#...
+
+00800800 <cabbage\$group4>:
+  800800:	00008067          	ret
+  800804:	0004.*
+  800806:	0004.*
+#...
+  8009f8:	0004.*
+  8009fa:	0004.*
+#...
+
+00800a00 <cabbage\$group5>:
+  800a00:	00008067          	ret
+
+00800a04 <duck>:
+  800a04:	00008067          	ret
+
+00800a08 <elephant>:
+  800a08:	00008067          	ret
+
+00800a0c <food\$group5>:
+  800a0c:	00008067          	ret
+  800a10:	0005.*
+  800a12:	0005.*
+#...
+  800bf8:	0005.*
+  800bfa:	0005.*
+#...
+
+00800c00 <cabbage\$group6>:
+  800c00:	00008067          	ret
+
+00800c04 <duck\$group6>:
+  800c04:	00008067          	ret
+
+00800c08 <elephant\$group6>:
+  800c08:	00008067          	ret
+  800c0c:	0006.*
+  800c0e:	0006.*
+#...
+  800df8:	0006.*
+  800dfa:	0006.*
+#...
+
+00800e00 <food\$group22>:
+  800e00:	00008067          	ret
+  800e04:	0016.*
+  800e06:	0016.*
+#...
+  800ff8:	0016.*
+  800ffa:	0016.*
+#...
+
+00801000 <elephant\$group23>:
+  801000:	00008067          	ret
+
+00801004 <food\$group23>:
+  801004:	00008067          	ret
+  801008:	00170017.*
+#...
+  8011f8:	00170017.*
+#...
+
+00801200 <food\$group24>:
+  801200:	00008067          	ret
+  801204:	0018.*
+  801206:	0018.*
+#...
+  8013f8:	0018.*
+  8013fa:	0018.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-no-grouping-file.d b/ld/testsuite/ld-riscv-elf/overlay-no-grouping-file.d
new file mode 100644
index 00000000000..29613ce47f9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-no-grouping-file.d
@@ -0,0 +1,4 @@
+#source: overlay-trivial.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file overlay-non-existent-file.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#error: .*cannot open grouping file.*overlay-non-existent-file\.csv
diff --git a/ld/testsuite/ld-riscv-elf/overlay-partial-assign.csv b/ld/testsuite/ld-riscv-elf/overlay-partial-assign.csv
new file mode 100644
index 00000000000..b035c8c021f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-partial-assign.csv
@@ -0,0 +1,3 @@
+banana,1
+elephant,3
+food,5
diff --git a/ld/testsuite/ld-riscv-elf/overlay-partial-assign.d b/ld/testsuite/ld-riscv-elf/overlay-partial-assign.d
new file mode 100644
index 00000000000..89591016d81
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-partial-assign.d
@@ -0,0 +1,85 @@
+#source: overlay-many-funcs.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-partial-assign.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	00508093          	addi	ra,ra,5.*
+   8:	000000b7          	lui	ra,0x0
+   c:	00308093          	addi	ra,ra,3.*
+  10:	000000b7          	lui	ra,0x0
+  14:	00908093          	addi	ra,ra,9.*
+  18:	000000b7          	lui	ra,0x0
+  1c:	00d08093          	addi	ra,ra,13.*
+  20:	000000b7          	lui	ra,0x0
+  24:	00708093          	addi	ra,ra,7.*
+  28:	000000b7          	lui	ra,0x0
+  2c:	00b08093          	addi	ra,ra,11.*
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00030002 00050004 00070006.*
+  800010:	00000000.*
+
+00800014 <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	47b66d24.*
+
+00800200 <banana>:
+  800200:	00008067          	ret
+  800204:	0001.*
+  800206:	0001.*
+#...
+  8003f8:	0001.*
+  8003fa:	0001.*
+#...
+
+00800400 <apple>:
+  800400:	00008067          	ret
+  800404:	0002.*
+  800406:	0002.*
+#...
+  8005f8:	0002.*
+  8005fa:	0002.*
+#...
+
+00800600 <elephant>:
+  800600:	00008067          	ret
+  800604:	00030003.*
+#...
+  8007f8:	00030003.*
+#...
+
+00800800 <cabbage>:
+  800800:	00008067          	ret
+  800804:	0004.*
+  800806:	0004.*
+#...
+  8009f8:	0004.*
+  8009fa:	0004.*
+#...
+
+00800a00 <food>:
+  800a00:	00008067          	ret
+  800a04:	0005.*
+  800a06:	0005.*
+#...
+  800bf8:	0005.*
+  800bfa:	0005.*
+#...
+
+00800c00 <duck>:
+  800c00:	00008067          	ret
+  800c04:	0006.*
+  800c06:	0006.*
+#...
+  800df8:	0006.*
+  800dfa:	0006.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-plt.csv b/ld/testsuite/ld-riscv-elf/overlay-plt.csv
new file mode 100644
index 00000000000..0eeeac18995
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-plt.csv
@@ -0,0 +1,4 @@
+apple,3
+banana,3
+cabbage,5
+duck,5,3
diff --git a/ld/testsuite/ld-riscv-elf/overlay-plt.d b/ld/testsuite/ld-riscv-elf/overlay-plt.d
new file mode 100644
index 00000000000..d18241398f7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-plt.d
@@ -0,0 +1,72 @@
+#source: overlay-plt.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-plt.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+   0:	000000b7          	lui	ra,0x0
+   4:	02008093          	addi	ra,ra,32.*
+   8:	000000b7          	lui	ra,0x0
+   c:	02c08093          	addi	ra,ra,44.*
+  10:	000000b7          	lui	ra,0x0
+  14:	03808093          	addi	ra,ra,56.*
+  18:	000000b7          	lui	ra,0x0
+  1c:	04408093          	addi	ra,ra,68.*
+
+Disassembly of section .ovlplt:
+
+00000020 <.ovlplt>:
+  20:	08000f37          	lui	t5,0x8000
+  24:	007f0f13          	addi	t5,t5,7.*
+  28:	000f8067          	jr	t6
+  2c:	08000f37          	lui	t5,0x8000
+  30:	00bf0f13          	addi	t5,t5,11.*
+  34:	000f8067          	jr	t6
+  38:	08020f37          	lui	t5,0x8020
+  3c:	007f0f13          	addi	t5,t5,7.*
+  40:	000f8067          	jr	t6
+  44:	88000f37          	lui	t5,0x88000
+  48:	001f0f13          	addi	t5,t5,1.*
+  4c:	000f8067          	jr	t6
+
+Disassembly of section .ovlgrps:
+
+00800000 <__OVERLAY_GROUP_TABLE_START>:
+  800000:	00010000 00010001 00020002 00000003.*
+
+00800010 <__OVERLAY_MULTIGROUP_TABLE_START>:
+  800010:	0002000b 00040007 00000000.*
+
+0080001c <__OVERLAY_MULTIGROUP_TABLE_END>:
+	...
+  8001fc:	56ee8bfb.*
+
+00800200 <apple>:
+  800200:	00008067          	ret
+
+00800204 <banana>:
+  800204:	00008067          	ret
+
+00800208 <duck\$group3>:
+  800208:	00008067          	ret
+  80020c:	00030003.*
+#...
+  8003f8:	00030003.*
+#...
+
+00800400 <cabbage>:
+  800400:	00008067          	ret
+
+00800404 <duck>:
+  800404:	00008067          	ret
+  800408:	0005.*
+  80040a:	0005.*
+#...
+  8005f8:	0005.*
+  8005fa:	0005.*
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-plt.s b/ld/testsuite/ld-riscv-elf/overlay-plt.s
new file mode 100644
index 00000000000..d3bdc5c6ade
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-plt.s
@@ -0,0 +1,40 @@
+  .section .ovlinput.apple,"ax",@progbits
+  .global  apple
+  .p2align  2
+  .type    apple,@function
+apple:
+  ret
+
+  .section .ovlinput.banana,"ax",@progbits
+  .global  banana
+  .p2align  2
+  .type    banana,@function
+banana:
+  ret
+
+  .section .ovlinput.cabbage,"ax",@progbits
+  .global  cabbage
+  .p2align  2
+  .type    cabbage,@function
+cabbage:
+  ret
+
+  .section .ovlinput.duck,"ax",@progbits
+  .global  duck
+  .p2align  2
+  .type    duck,@function
+duck:
+  ret
+
+  .text
+  .global  _start
+  .type    _start,@function
+_start:
+  lui  x1, %ovlplt_hi(apple)
+  addi x1, x1, %ovlplt_lo(apple)
+  lui  x1, %ovlplt_hi(cabbage)
+  addi x1, x1, %ovlplt_lo(cabbage)
+  lui  x1, %ovlplt_hi(banana)
+  addi x1, x1, %ovlplt_lo(banana)
+  lui  x1, %ovlplt_hi(duck)
+  addi x1, x1, %ovlplt_lo(duck)
diff --git a/ld/testsuite/ld-riscv-elf/overlay-relax-01.d b/ld/testsuite/ld-riscv-elf/overlay-relax-01.d
new file mode 100644
index 00000000000..0a400e9bfd1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-relax-01.d
@@ -0,0 +1,19 @@
+#source: overlay-relax.s
+#as: -march=rv32ic -mabi=ilp32
+#ld: -T overlay.ld -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+#...
+
+Disassembly of section \.ovlgrps:
+
+.* <__OVERLAY_GROUP_TABLE_START>:
+#...
+
+.* <__OVERLAY_MULTIGROUP_TABLE_END>:
+#...
+
+.* <apple>:
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-relax-02.csv b/ld/testsuite/ld-riscv-elf/overlay-relax-02.csv
new file mode 100644
index 00000000000..172e005d0dc
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-relax-02.csv
@@ -0,0 +1,3 @@
+apple,1
+banana,1
+cabbage,2
diff --git a/ld/testsuite/ld-riscv-elf/overlay-relax-02.d b/ld/testsuite/ld-riscv-elf/overlay-relax-02.d
new file mode 100644
index 00000000000..eb823593ea3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-relax-02.d
@@ -0,0 +1,19 @@
+#source: overlay-relax.s
+#as: -march=rv32ic -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-relax-02.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+#...
+
+Disassembly of section \.ovlgrps:
+
+.* <__OVERLAY_GROUP_TABLE_START>:
+#...
+
+.* <__OVERLAY_MULTIGROUP_TABLE_END>:
+#...
+
+.* <apple>:
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.csv b/ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.csv
new file mode 100644
index 00000000000..d3d498d57bc
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.csv
@@ -0,0 +1,3 @@
+apple,2,3,4
+banana,3,4,5
+cabbage,5,6,7
diff --git a/ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.d b/ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.d
new file mode 100644
index 00000000000..cd1aa75bb51
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-relax-multigroup.d
@@ -0,0 +1,19 @@
+#source: overlay-relax.s
+#as: -march=rv32ic -mabi=ilp32
+#ld: -T overlay.ld --grouping-file [riscv_get_testdir]/overlay-relax-multigroup.csv -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#objdump: -d
+
+.*:[  ]+file format .*
+
+#...
+
+Disassembly of section \.ovlgrps:
+
+.* <__OVERLAY_GROUP_TABLE_START>:
+#...
+
+.* <__OVERLAY_MULTIGROUP_TABLE_END>:
+#...
+
+.* <apple>:
+#...
diff --git a/ld/testsuite/ld-riscv-elf/overlay-relax.s b/ld/testsuite/ld-riscv-elf/overlay-relax.s
new file mode 100644
index 00000000000..d84ba27d38b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-relax.s
@@ -0,0 +1,55 @@
+  .section .ovlinput.apple,"ax",@progbits
+  .global  apple
+  .type    apple,@function
+apple:
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+0:
+  ret
+
+  .section .ovlinput.banana,"ax",@progbits
+  .global  banana
+  .type    banana,@function
+banana:
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+0:
+  ret
+
+  .section .ovlinput.cabbage,"ax",@progbits
+  .global  cabbage
+  .type    cabbage,@function
+cabbage:
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+  j 0f
+0:
+  ret
+
+  .text
+  .global  _start
+  .type    _start,@function
+_start:
+  lui  x1, %ovltok_hi(apple)
+  addi x1, x1, %ovltok_lo(apple)
+  lui  x1, %ovltok_hi(banana)
+  addi x1, x1, %ovltok_lo(banana)
+  lui  x1, %ovltok_hi(cabbage)
+  addi x1, x1, %ovltok_lo(cabbage)
diff --git a/ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.d b/ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.d
new file mode 100644
index 00000000000..464a9730767
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.d
@@ -0,0 +1,4 @@
+#source: overlay-symbol-not-in-ovlinput.s
+#as: -march=rv32i -mabi=ilp32
+#ld: -T overlay.ld -m[riscv_choose_ilp32_emul] -relax -gc-sections
+#error: .*A symbol in section.*\.text.*is referred to by an overlay relocation, but the section does not have a.*\.ovlinput\..*prefix\..*
diff --git a/ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.s b/ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.s
new file mode 100644
index 00000000000..495dda6334e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-symbol-not-in-ovlinput.s
@@ -0,0 +1,10 @@
+  .global  apple
+  .type    apple,@function
+apple:
+  ret
+
+  .global  _start
+  .type    _start,@function
+_start:
+  lui  x1, %ovltok_hi(apple)
+  addi x1, x1, %ovltok_lo(apple)
diff --git a/ld/testsuite/ld-riscv-elf/overlay-trivial.s b/ld/testsuite/ld-riscv-elf/overlay-trivial.s
new file mode 100644
index 00000000000..c4d2e941bdb
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay-trivial.s
@@ -0,0 +1,12 @@
+  .section .ovlinput.apple,"ax",@progbits
+  .global  apple
+  .type    apple,@function
+apple:
+  ret
+
+  .text
+  .global  _start
+  .type    _start,@function
+_start:
+  lui  x1, %ovltok_hi(apple)
+  addi x1, x1, %ovltok_lo(apple)
diff --git a/ld/testsuite/ld-riscv-elf/overlay.ld b/ld/testsuite/ld-riscv-elf/overlay.ld
new file mode 100644
index 00000000000..eb3a6223b13
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/overlay.ld
@@ -0,0 +1,17 @@
+ENTRY(_start)
+
+
+MEMORY {
+  ovl : ORIGIN = 0x800000, LENGTH = 0x100000
+}
+
+SECTIONS {
+  .text : {
+    *(.text*)
+  }
+  .ovlgrps : {
+    OVERLAY_START_OF_OVERLAYS = .;
+    *(.ovlinput.*)
+    OVERLAY_END_OF_OVERLAYS = .;
+  } >ovl AT>ovl
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-04-25  9:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-25  9:42 [RFC][PATCH] Binutils changes for RISC-V overlay system Ed Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).