[PATCH v2] RISC-V: Optimize relaxations without estimating size as possible
Nelson Chu
nelson@rivosinc.com
Wed Dec 18 06:15:00 GMT 2024
ChangeLog of v2,
1. Renamed the ld options to --[no-]relax-reserve.
2. Added support for TPREL.
3. Only need two internal relocation to keep the symbol and addend information
for those deleted relocations, so that we can easier check overflows and disbale
relaxations for those pattern, which have the same symbols, but without chaining
them up.
4. Added comments and descriptions of new ld options for --target-help.
5. defualt set to --relax-reserve as usual.
====
Currently, we try to estimate and reserve enough space when relaxing, to
prevent these factors from causing the relaxed code to not shrink as expected,
thus causing truncated errors. The reserved spaces include input/output
section alignments, data segment alignment, and the array size which elements
shared high part instructions. However, there are currently still two main
problems,
1. May reserve too many size since large alignments.
The following commits tried to improve it, but has limited effect on some cases.
Commit d0f744f, bfd: RISC-V: relax to gp in more cases.
Commit c6261a0, RISC-V: Fix ld relax failure with calls and align directives.
Commit 9d1da81, RISC-V: Optimize lui and auipc relaxations for undefweak symbol.
Commit 0699f2d, RISC-V: Optimize relaxation of gp with max_alignment.
2. Still have truncated errors.
No matter how hard we estimate, we still cannot cover all possibilities. For
example, the alignments between output sections, and the fixed address
assignments in the linker scripts. Even in the default linker script, there
are constantly new failure cases.
Therefore, this patch proposed a new experimental linker target option,
--no-relax-reserve, which not only can resolve the truncated errors mentioned
above, but also can get more chance to do relaxations since we don't need to
reserve too much of size. Of course there is another --relax-reserve option
set by default to keep the original behavior.
The idea is that - we backup all the information that we need when relaxaing,
and then check if overflows occur or not after finishing all relaxations,
including alignments. If overflows occur, then we disable the relaxation for
those patterns by marking the backup R_RISCV_RELAX to R_RISCV_NONE, recover
based on the backup information, and then re-do the whole relaxations again,
until there is no overlfow.
So the flow of relaxations are as follow,
pass 0: Recover symbols, relocations and codes according to backups.
pass 1: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and deletes the
obsolete bytes. Backup information before doing any relaxation.
pass 2: Which cannot be disabled, handles code alignment directives.
pass 3: Check if any relaxation overflows. If so, re-run from pass 0.
The reason that we recover the stuff at pass 0 rather than pass 3 is in
the commit 9abcdc1, for the ld/testsuite/ld-riscv/align-small-region.
The backup information includes codes, symbol tables, and relocation tables.
Backup and recover codes and relocations is quite easy to do by malloc and
memcpy. But backup the whole symbol tables, especially the global hash tables,
is a bit impractical. Therefore, we just backup the delta address/size after
relaxing, and only for the affected symbols.
There are two new internal relocations, R_RISCV_GPREL_DELETE and
R_RISCV_TPREL_DELETE, to keep the symbol and addend information for those
deleted/relaxed R_RISCV_HI20/PCREL_HI20/TPREL_HI20/TPREL_ADD relocations. So
that we can easier check overflows and disbale relaxations for those patterns,
which have the same symbols, but without chaining them up.
Original Relaxed, --relax-reserve --no-relax-reserve
R_RISCV_HI20 -> R_RISCV_DELETE(4) -> R_RISCV_GPREL_DELETE (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_DELETE(4)
R_RISCV_LO12_I/S -> R_RISCV_GPREL_I/S -> R_RISCV_GPREL_I/S (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_RELAX
R_RISCV_HI20 -> R_RISCV_RVC_LUI (checked overflow)
R_RISCV_RELAX -> R_RISCV_DELETE(2)
R_RISCV_LO12_I/S -> R_RISCV_LO12_I/S
R_RISCV_RELAX -> R_RISCV_RELAX
R_RISCV_PCREL_HI20 -> R_RISCV_DELETE(4) -> R_RISCV_GPREL_DELETE (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_DELETE(4)
R_RISCV_PCREL_LO12_I/S -> R_RISCV_GPREL_I/S -> R_RISCV_GPREL_I/S (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_RELAX
R_RISCV_CALL -> R_RISCV_RELAX_JAL/RVC_JUMP/LO12_I (checked overflow)
R_RISCV_RELAX -> R_RISCV_DELETE(4)
R_RISCV_TPREL_HI20 -> R_RISCV_DELETE(4) -> R_RISCV_TPREL_DELETE (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_DELETE(4)
R_RISCV_TPREL_ADD -> R_RISCV_DELETE(4) -> R_RISCV_TPREL_DELETE (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_DELETE(4)
R_RISCV_TPREL_LO12_I/S -> R_RISCV_TPREL_I/S -> R_RISCV_TPREL_I/S (checked overflow)
R_RISCV_RELAX -> R_RISCV_RELAX -> R_RISCV_RELAX
I tried to keep R_RISCV_HI20/PCREL_HI20/TPREL_HI20/TPREL_ADD first, and check
them if overflow or not, when the corresponding R_RISCV_RELAX was converted to
R_RISCV_DELETE. But this makes stuff complicate, since - It's hard to
distinguish between relaxed and non-relaxed relocations when relocating.
The corresponding R_RISCV_DELETE was resolved and converted to R_RISCV_NONE
after relax pass 1, so we need to check the backup relocations when checking
overflows at relax pass 3, and also clean these non-overflow relocations to
R_RISCV_NONE. There is an intersting thing here - the same section may enter
the same relax pass multiple times in different data_segment phases.
Therefore, overflows may not be checked on first entry, and when no overflows
are checked, we cannot clean them to R_RISCV_NONE right away... To simplify
the process, add internal relocations is the easiest way.
For the `reserve_size', unfortunately we cannot ignore it even when setting
--no-relax-reserve. The `reserve_size' is usually the size of array, the
elements of array may share the high LUI instruction. If we ignore it, then
we may delete the shared LUI, but parts of the low instruction still need it.
I get at least two following gcc testsuite failed when we set `reserve_size'
to zero and --no-relax-reserve.
FAIL: tmpdir-gcc.dg-struct-layout-1/t010 c_compat_x_tst.o-c_compat_y_tst.o execute
FAIL: gcc.dg/torture/fp-int-convert-timode.c -O0 execution test
There are still some issues as follows,
* Should we reserve at least the size of instruction alignment, which is 2 for
rvc and 4 for others for now.
* The chance of rvc LUI relaxation may be reduced when the LUI to gp relaxation
overflow and we just disable them directly at the next round. Maybe we should
give them another chance at the next round to do the rvc LUI relaxation.
Record something like relax-level or whatever stuff into the addend of
R_RISCV_RELAX should work.
---
bfd/elfnn-riscv.c | 601 +++++++++++++++---
bfd/elfxx-riscv.c | 5 +
bfd/elfxx-riscv.h | 12 +
include/elf/riscv.h | 10 +-
ld/emultempl/riscvelf.em | 28 +-
ld/ldlex.h | 2 +
ld/testsuite/ld-riscv-elf/ifunc-nonplt.s | 2 +
ld/testsuite/ld-riscv-elf/ifunc-plt-02.s | 2 +
ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s | 2 +
ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp | 9 +-
ld/testsuite/ld-riscv-elf/pcrel-lo-addend.s | 1 +
.../ld-riscv-elf/relax-data-segment-align.d | 8 -
.../relax-truncated-align-addr-no-reserve.d | 26 +
.../ld-riscv-elf/relax-truncated-align-addr.d | 27 +
.../relax-truncated-align-addr.ld | 5 +
...ated-align-between-output-sec-no-reserve.d | 26 +
...relax-truncated-align-between-output-sec.d | 3 +
...elax-truncated-align-between-output-sec.ld | 6 +
...-truncated-data-segment-align-no-reserve.d | 18 +
.../relax-truncated-data-segment-align.d | 18 +
...s => relax-truncated-data-segment-align.s} | 7 +-
.../relax-truncated-fixed-addr-no-reserve.d | 26 +
.../ld-riscv-elf/relax-truncated-fixed-addr.d | 3 +
.../relax-truncated-fixed-addr.ld | 5 +
ld/testsuite/ld-riscv-elf/relax-truncated.s | 20 +
25 files changed, 753 insertions(+), 119 deletions(-)
delete mode 100644 ld/testsuite/ld-riscv-elf/relax-data-segment-align.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-no-reserve.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-no-reserve.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-no-reserve.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d
rename ld/testsuite/ld-riscv-elf/{relax-data-segment-align.s => relax-truncated-data-segment-align.s} (69%)
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-no-reserve.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld
create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated.s
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 8bbdd15d552..93f688ece87 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -204,6 +204,8 @@ elfNN_riscv_mkobject (bfd *abfd)
#include "elf/common.h"
#include "elf/internal.h"
+typedef struct riscv_relax_backup riscv_relax_backup;
+
struct riscv_elf_link_hash_table
{
struct elf_link_hash_table elf;
@@ -233,6 +235,11 @@ struct riscv_elf_link_hash_table
/* Relocations for variant CC symbols may be present. */
int variant_cc;
+
+ /* Re-run the relaxations from relax pass 0 if TRUE. */
+ bool restart_relax;
+ /* Backup relaxation information. */
+ riscv_relax_backup *relax_backups;
};
/* Instruction access functions. */
@@ -512,6 +519,8 @@ riscv_elf_link_hash_table_create (bfd *abfd)
ret->max_alignment = (bfd_vma) -1;
ret->max_alignment_for_gp = (bfd_vma) -1;
+ ret->restart_relax = false;
+ ret->relax_backups = NULL;
/* Create hash table for local ifunc. */
ret->loc_hash_table = htab_try_create (1024,
@@ -1937,9 +1946,6 @@ perform_relocation (const reloc_howto_type *howto,
case R_RISCV_TLS_DTPREL64:
break;
- case R_RISCV_DELETE:
- return bfd_reloc_ok;
-
default:
return bfd_reloc_notsupported;
}
@@ -2577,6 +2583,14 @@ riscv_elf_relocate_section (bfd *output_bfd,
/* These require nothing of us at all. */
continue;
+ case R_RISCV_DELETE:
+ /* Should all be resolved in riscv_relax_resolve_delete_relocs. */
+ case R_RISCV_GPREL_DELETE:
+ case R_RISCV_TPREL_DELETE:
+ rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+ rel->r_addend = 0;
+ continue;
+
case R_RISCV_HI20:
case R_RISCV_BRANCH:
case R_RISCV_RVC_BRANCH:
@@ -2588,7 +2602,6 @@ riscv_elf_relocate_section (bfd *output_bfd,
case R_RISCV_SET16:
case R_RISCV_SET32:
case R_RISCV_32_PCREL:
- case R_RISCV_DELETE:
/* These require no special handling beyond perform_relocation. */
break;
@@ -4403,6 +4416,248 @@ riscv_update_pcgp_relocs (riscv_pcgp_relocs *p, asection *deleted_sec,
}
}
+/* Backup information to re-do the whole relaxations. */
+
+typedef struct riscv_relax_backup_sec riscv_relax_backup_sec;
+struct riscv_relax_backup_sec
+{
+ riscv_relax_backup *root;
+ asection *sec;
+ bfd_vma size;
+ bfd_byte *contents; /* Code. */
+ Elf_Internal_Rela *relocs; /* Relocations. */
+ riscv_relax_backup_sec *next;
+};
+
+typedef struct
+{
+ /* May point to Elf_Internal_Sym or struct elf_link_hash_entry. */
+ void *symbol;
+ bfd_vma delta_val;
+ bfd_vma delta_size;
+} riscv_relax_backup_sym;
+
+struct riscv_relax_backup
+{
+ bfd *bfd;
+ riscv_relax_backup_sec *sections;
+ riscv_relax_backup_sym *local_symbols;
+ riscv_relax_backup_sym *global_symbols;
+ unsigned int local_symcount;
+ unsigned int global_symcount;
+ riscv_relax_backup *next;
+};
+
+/* Return the backups of SEC. If the SEC hasn't been backed up yet, create one
+ for it. */
+
+static riscv_relax_backup_sec *
+riscv_relax_search_and_create_backup (struct bfd_link_info *info, bfd *abfd,
+ asection *sec)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+
+ /* Input bfds. */
+ riscv_relax_backup *backup = htab->relax_backups;
+ while (backup && backup->bfd != abfd)
+ backup = backup->next;
+ if (backup == NULL)
+ {
+ Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ backup = bfd_zmalloc (sizeof (*backup));
+ backup->bfd = abfd;
+ /* Symbols. */
+ backup->local_symcount = symtab_hdr->sh_info;
+ backup->global_symcount =
+ ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
+ - symtab_hdr->sh_info);
+ backup->local_symbols =
+ bfd_zmalloc (sizeof (*backup) * backup->local_symcount);
+ backup->global_symbols =
+ bfd_zmalloc (sizeof (*backup) * backup->global_symcount);
+ backup->next = htab->relax_backups;
+ htab->relax_backups = backup;
+ }
+
+ /* Sections. */
+ riscv_relax_backup_sec *backup_sec = backup->sections;
+ while (backup_sec && backup_sec->sec != sec)
+ backup_sec = backup_sec->next;
+ if (backup_sec == NULL)
+ {
+ struct bfd_elf_section_data *data = elf_section_data (sec);
+ backup_sec = bfd_zmalloc (sizeof (*backup_sec));
+ backup_sec->sec = sec;
+ /* Relocations. */
+ bfd_size_type size =
+ (bfd_size_type) sec->reloc_count * sizeof (Elf_Internal_Rela);
+ backup_sec->relocs = (Elf_Internal_Rela *) bfd_zmalloc (size);
+ memcpy (backup_sec->relocs, data->relocs, size);
+ /* Section size. */
+ backup_sec->size = sec->size;
+ /* Code. */
+ backup_sec->contents = bfd_zmalloc (sec->size);
+ memcpy (backup_sec->contents, data->this_hdr.contents, sec->size);
+ backup_sec->root = backup;
+ backup_sec->next = backup->sections;
+ backup->sections = backup_sec;
+ }
+
+ return backup_sec;
+}
+
+/* If RECOVER is true, recover information from backups so that can re-do the
+ whole relaxations; Otherwise just free the backups. */
+
+static void
+riscv_relax_recover_and_free_backup (struct bfd_link_info *info, bool recover)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+
+ /* Input bfds. */
+ riscv_relax_backup *p = htab->relax_backups;
+ unsigned int i;
+ while (p != NULL)
+ {
+ riscv_relax_backup *next_p = p->next;
+ /* Local symbols. */
+ if (p->local_symbols && p->local_symcount != 0)
+ {
+ for (i = 0; i < p->local_symcount && recover; i++)
+ {
+ riscv_relax_backup_sym *sym = &p->local_symbols[i];
+ Elf_Internal_Sym *isym = (Elf_Internal_Sym *) sym->symbol;
+ if (isym != NULL)
+ {
+ isym->st_value += sym->delta_val;
+ isym->st_size += sym->delta_size;
+ }
+ }
+ free (p->local_symbols);
+ }
+ /* Global symbols. */
+ if (p->global_symbols && p->global_symcount != 0)
+ {
+ for (i = 0; i < p->global_symcount && recover; i++)
+ {
+ riscv_relax_backup_sym *sym = &p->global_symbols[i];
+ struct elf_link_hash_entry *h =
+ (struct elf_link_hash_entry *) sym->symbol;
+ if (h != NULL)
+ {
+ h->root.u.def.value += sym->delta_val;
+ h->size += sym->delta_size;
+ }
+ }
+ free (p->global_symbols);
+ }
+ /* Sections. */
+ riscv_relax_backup_sec *s = p->sections;
+ while (s != NULL)
+ {
+ riscv_relax_backup_sec *next_s = s->next;
+ asection *sec = s->sec;
+ if (sec != NULL && s->contents != NULL && s->relocs != NULL)
+ {
+ if (recover)
+ {
+ struct bfd_elf_section_data *data = elf_section_data (sec);
+ /* Relocations. */
+ bfd_size_type size = (bfd_size_type)
+ (sec->reloc_count * sizeof (Elf_Internal_Rela));
+ memcpy (data->relocs, s->relocs, size);
+ /* Code. */
+ memcpy (data->this_hdr.contents, s->contents, s->size);
+ sec->size = s->size;
+ }
+ free (s->relocs);
+ free (s->contents);
+ }
+ free (s);
+ s = next_s;
+ }
+ free (p);
+ p = next_p;
+ }
+ htab->relax_backups = NULL;
+}
+
+/* Check if the relaxation overflows or not. If overflows, then marks the
+ corresponding R_RISCV_RELAX to R_RISCV_NONE in backups, to disable the
+ relaxation on the next rounds. */
+
+static bool
+_bfd_riscv_relax_check_overflow (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ asection *sym_sec ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info,
+ Elf_Internal_Rela *rel,
+ bfd_vma symval,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size,
+ bool *again,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+ bool undefined_weak,
+ riscv_relax_backup_sec *backup_sec)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
+ bfd_vma gp = htab->params->relax_gp
+ ? riscv_global_pointer_value (link_info)
+ : 0;
+ struct bfd_elf_section_data *data = elf_section_data (sec);
+ Elf_Internal_Rela *backup_relocs = backup_sec->relocs;
+ Elf_Internal_Rela *backup_rel = &backup_relocs[rel - data->relocs];
+ Elf_Internal_Rela *backup_relax_rel = backup_rel + 1;
+
+ switch (ELFNN_R_TYPE (rel->r_info))
+ {
+ case R_RISCV_GPREL_DELETE:
+ case R_RISCV_GPREL_I:
+ case R_RISCV_GPREL_S:
+ if (undefined_weak
+ /* The x0 base. */
+ || VALID_ITYPE_IMM (symval)
+ /* The gp base. */
+ || (symval >= gp && VALID_ITYPE_IMM (symval - gp + reserve_size))
+ || (symval < gp && VALID_ITYPE_IMM (symval - gp - reserve_size)))
+ return true;
+ goto disable_relax_reloc;
+ case R_RISCV_RVC_LUI:
+ if (VALID_CITYPE_LUI_IMM (RISCV_CONST_HIGH_PART (symval))
+ /* The perform_relocation will convert it to c.li. */
+ || RISCV_CONST_HIGH_PART (symval) == 0)
+ return true;
+ goto disable_relax_reloc;
+ case R_RISCV_JAL:
+ if (VALID_JTYPE_IMM (symval - (sec_addr (sec) + rel->r_offset)))
+ return true;
+ goto disable_relax_reloc;
+ case R_RISCV_RVC_JUMP:
+ if (VALID_CJTYPE_IMM (symval - (sec_addr (sec) + rel->r_offset)))
+ return true;
+ goto disable_relax_reloc;
+ case R_RISCV_LO12_I:
+ /* Near zero. */
+ if ((symval + RISCV_IMM_REACH / 2) < RISCV_IMM_REACH)
+ return true;
+ goto disable_relax_reloc;
+ case R_RISCV_TPREL_DELETE:
+ case R_RISCV_TPREL_I:
+ case R_RISCV_TPREL_S:
+ /* The tp base. */
+ if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) == 0)
+ return true;
+ goto disable_relax_reloc;
+ disable_relax_reloc:
+ /* Disable R_RISCV_RELAX in backups if overflows. */
+ backup_relax_rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+ *again = true;
+ break;
+ }
+
+ return true;
+}
+
/* Delete some bytes, adjust relcocations and symbol table from a section. */
static bool
@@ -4413,7 +4668,8 @@ _riscv_relax_delete_bytes (bfd *abfd,
struct bfd_link_info *link_info,
riscv_pcgp_relocs *p,
bfd_vma delete_total,
- bfd_vma toaddr)
+ bfd_vma toaddr,
+ riscv_relax_backup_sec *backup_sec)
{
unsigned int i, symcount;
struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
@@ -4422,6 +4678,7 @@ _riscv_relax_delete_bytes (bfd *abfd,
struct bfd_elf_section_data *data = elf_section_data (sec);
bfd_byte *contents = data->this_hdr.contents;
size_t bytes_to_move = toaddr - addr - count;
+ riscv_relax_backup_sym *backup_symbol = NULL;
/* Actually delete the bytes. */
sec->size -= count;
@@ -4448,10 +4705,20 @@ _riscv_relax_delete_bytes (bfd *abfd,
Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
if (sym->st_shndx == sec_shndx)
{
+ if (backup_sec && backup_sec->root)
+ {
+ backup_symbol = &backup_sec->root->local_symbols[i];
+ backup_symbol->symbol = (void *) sym;
+ }
+
/* If the symbol is in the range of memory we just moved, we
have to adjust its value. */
if (sym->st_value > addr && sym->st_value <= toaddr)
- sym->st_value -= count;
+ {
+ sym->st_value -= count;
+ if (backup_symbol)
+ backup_symbol->delta_val += count;
+ }
/* If the symbol *spans* the bytes we just deleted (i.e. its
*end* is in the moved bytes but its *start* isn't), then we
@@ -4465,7 +4732,11 @@ _riscv_relax_delete_bytes (bfd *abfd,
else if (sym->st_value <= addr
&& sym->st_value + sym->st_size > addr
&& sym->st_value + sym->st_size <= toaddr)
- sym->st_size -= count;
+ {
+ sym->st_size -= count;
+ if (backup_symbol)
+ backup_symbol->delta_size += count;
+ }
}
}
@@ -4510,16 +4781,30 @@ _riscv_relax_delete_bytes (bfd *abfd,
|| sym_hash->root.type == bfd_link_hash_defweak)
&& sym_hash->root.u.def.section == sec)
{
+ if (backup_sec && backup_sec->root)
+ {
+ backup_symbol = &backup_sec->root->global_symbols[i];
+ backup_symbol->symbol = (void *) sym_hash;
+ }
+
/* As above, adjust the value if needed. */
if (sym_hash->root.u.def.value > addr
&& sym_hash->root.u.def.value <= toaddr)
- sym_hash->root.u.def.value -= count;
+ {
+ sym_hash->root.u.def.value -= count;
+ if (backup_symbol)
+ backup_symbol->delta_val += count;
+ }
/* As above, adjust the size if needed. */
else if (sym_hash->root.u.def.value <= addr
&& sym_hash->root.u.def.value + sym_hash->size > addr
&& sym_hash->root.u.def.value + sym_hash->size <= toaddr)
- sym_hash->size -= count;
+ {
+ sym_hash->size -= count;
+ if (backup_symbol)
+ backup_symbol->delta_size += count;
+ }
}
}
@@ -4530,7 +4815,8 @@ typedef bool (*relax_delete_t) (bfd *, asection *,
bfd_vma, size_t,
struct bfd_link_info *,
riscv_pcgp_relocs *,
- Elf_Internal_Rela *);
+ Elf_Internal_Rela *,
+ riscv_relax_backup_sec *);
static relax_delete_t riscv_relax_delete_bytes;
@@ -4544,7 +4830,8 @@ _riscv_relax_delete_piecewise (bfd *abfd ATTRIBUTE_UNUSED,
size_t count,
struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
- Elf_Internal_Rela *rel)
+ Elf_Internal_Rela *rel,
+ riscv_relax_backup_sec *backup_sec ATTRIBUTE_UNUSED)
{
if (rel == NULL)
return false;
@@ -4563,12 +4850,13 @@ _riscv_relax_delete_immediate (bfd *abfd,
size_t count,
struct bfd_link_info *link_info,
riscv_pcgp_relocs *p,
- Elf_Internal_Rela *rel)
+ Elf_Internal_Rela *rel,
+ riscv_relax_backup_sec *backup_sec)
{
if (rel != NULL)
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
- return _riscv_relax_delete_bytes (abfd, sec, addr, count,
- link_info, p, 0, sec->size);
+ return _riscv_relax_delete_bytes (abfd, sec, addr, count, link_info, p, 0,
+ sec->size, backup_sec);
}
/* Delete the bytes for R_RISCV_DELETE relocs. */
@@ -4577,7 +4865,8 @@ static bool
riscv_relax_resolve_delete_relocs (bfd *abfd,
asection *sec,
struct bfd_link_info *link_info,
- Elf_Internal_Rela *relocs)
+ Elf_Internal_Rela *relocs,
+ riscv_relax_backup_sec *backup_sec)
{
bfd_vma delete_total = 0;
unsigned int i;
@@ -4609,11 +4898,13 @@ riscv_relax_resolve_delete_relocs (bfd *abfd,
bfd_vma toaddr = rel_next == NULL ? sec->size : rel_next->r_offset;
if (!_riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend,
- link_info, NULL, delete_total, toaddr))
+ link_info, NULL, delete_total, toaddr,
+ backup_sec))
return false;
delete_total += rel->r_addend;
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+ rel->r_addend = 0;
/* Skip ahead to the next delete reloc. */
i = rel_next != NULL ? (unsigned int) (rel_next - relocs - 1)
@@ -4628,7 +4919,8 @@ typedef bool (*relax_func_t) (bfd *, asection *, asection *,
Elf_Internal_Rela *,
bfd_vma, bfd_vma, bfd_vma, bool *,
riscv_pcgp_relocs *,
- bool undefined_weak);
+ bool undefined_weak,
+ riscv_relax_backup_sec *);
/* Relax AUIPC + JALR into JAL. */
@@ -4641,7 +4933,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bool *again,
riscv_pcgp_relocs *pcgp_relocs,
- bool undefined_weak ATTRIBUTE_UNUSED)
+ bool undefined_weak ATTRIBUTE_UNUSED,
+ riscv_relax_backup_sec *backup_sec)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma foff = symval - (sec_addr (sec) + rel->r_offset);
@@ -4653,7 +4946,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
cause the PC-relative offset to later increase, so we need to add in the
max alignment of any section inclusive from the call to the target.
Otherwise, we only need to use the alignment of the current section. */
- if (VALID_JTYPE_IMM (foff))
+ if (backup_sec == NULL && VALID_JTYPE_IMM (foff))
{
if (sym_sec->output_section == sec->output_section
&& sym_sec->output_section != bfd_abs_section_ptr)
@@ -4704,7 +4997,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
/* Delete unnecessary JALR and reuse the R_RISCV_RELAX reloc. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
- link_info, pcgp_relocs, rel + 1);
+ link_info, pcgp_relocs, rel + 1,
+ backup_sec);
}
/* Traverse all output sections and return the max alignment.
@@ -4747,7 +5041,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
bfd_vma reserve_size,
bool *again,
riscv_pcgp_relocs *pcgp_relocs,
- bool undefined_weak)
+ bool undefined_weak,
+ riscv_relax_backup_sec *backup_sec)
{
struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -4755,14 +5050,16 @@ _bfd_riscv_relax_lui (bfd *abfd,
bfd_vma gp = htab->params->relax_gp
? riscv_global_pointer_value (link_info)
: 0;
- bfd_vma data_segment_alignment = link_info->relro
- ? ELF_MAXPAGESIZE + ELF_COMMONPAGESIZE
- : ELF_MAXPAGESIZE;
+ bfd_vma data_segment_alignment = backup_sec == NULL
+ ? (link_info->relro
+ ? ELF_MAXPAGESIZE + ELF_COMMONPAGESIZE
+ : ELF_MAXPAGESIZE)
+ : 0;
int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
BFD_ASSERT (rel->r_offset + 4 <= sec->size);
- if (!undefined_weak && gp)
+ if (backup_sec == NULL && !undefined_weak && gp)
{
/* If gp and the symbol are in the same output section, which is not the
abs section, then consider only that output section's alignment. */
@@ -4817,10 +5114,16 @@ _bfd_riscv_relax_lui (bfd *abfd,
return true;
case R_RISCV_HI20:
- /* Delete unnecessary LUI and reuse the reloc. */
*again = true;
+ /* Replace the R_RISCV_HI20 reloc if there are backups. */
+ if (backup_sec != NULL)
+ rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_DELETE);
+ /* Delete unnecessary LUI, reuse the R_RISCV_HI20 reloc if there are
+ no backups, otherwise reuse the R_RISCV_RELAX reloc. */
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
- link_info, pcgp_relocs, rel);
+ link_info, pcgp_relocs,
+ backup_sec ? rel + 1 : rel,
+ backup_sec);
default:
abort ();
@@ -4853,7 +5156,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
/* Delete extra bytes and reuse the R_RISCV_RELAX reloc. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
- link_info, pcgp_relocs, rel + 1);
+ link_info, pcgp_relocs, rel + 1,
+ backup_sec);
}
return true;
@@ -4872,7 +5176,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bool *again,
riscv_pcgp_relocs *pcgp_relocs,
- bool undefined_weak ATTRIBUTE_UNUSED)
+ bool undefined_weak ATTRIBUTE_UNUSED,
+ riscv_relax_backup_sec *backup_sec)
{
/* See if this symbol is in range of tp. */
if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
@@ -4891,10 +5196,16 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_ADD:
- /* Delete unnecessary instruction and reuse the reloc. */
+ /* Replace the reloc if there are backups. */
+ if (backup_sec != NULL)
+ rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info),
+ R_RISCV_TPREL_DELETE);
+ /* Delete unnecessary instruction, reuse the reloc if there are no
+ backups, otherwise reuse the R_RISCV_RELAX reloc. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
- pcgp_relocs, rel);
+ pcgp_relocs, backup_sec ? rel + 1 : rel,
+ backup_sec);
default:
abort ();
@@ -4914,7 +5225,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bool *again ATTRIBUTE_UNUSED,
riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
- bool undefined_weak ATTRIBUTE_UNUSED)
+ bool undefined_weak ATTRIBUTE_UNUSED,
+ riscv_relax_backup_sec *backup_sec)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma alignment = 1, pos;
@@ -4926,7 +5238,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
bfd_vma nop_bytes = aligned_addr - symval;
/* Once we've handled an R_RISCV_ALIGN, we can't relax anything else. */
- sec->sec_flg0 = true;
+ if (backup_sec == NULL)
+ sec->sec_flg0 = true;
/* Make sure there are enough NOPs to actually achieve the alignment. */
if (rel->r_addend < nop_bytes)
@@ -4958,7 +5271,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
/* Delete excess bytes. */
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes,
rel->r_addend - nop_bytes, link_info,
- NULL, NULL);
+ NULL, NULL, backup_sec);
}
/* Relax PC-relative references to GP-relative references. */
@@ -4974,7 +5287,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
bfd_vma reserve_size,
bool *again,
riscv_pcgp_relocs *pcgp_relocs,
- bool undefined_weak)
+ bool undefined_weak,
+ riscv_relax_backup_sec *backup_sec)
{
struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
/* Can relax to x0 even when gp relaxation is disabled. */
@@ -5038,7 +5352,7 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
abort ();
}
- if (!undefined_weak && gp)
+ if (backup_sec == NULL && !undefined_weak && gp)
{
/* If gp and the symbol are in the same output section, which is not the
abs section, then consider only that output section's alignment. */
@@ -5102,10 +5416,16 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
ELFNN_R_SYM(rel->r_info),
sym_sec,
undefined_weak);
- /* Delete unnecessary AUIPC and reuse the reloc. */
+ /* Replace the R_RISCV_PCREL_HI20 reloc if there are backups. */
+ if (backup_sec != NULL)
+ rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info),
+ R_RISCV_GPREL_DELETE);
+ /* Delete unnecessary AUIPC, reuse the R_RISCV_PCREL_HI20 if there
+ are no backups, otherwise reuse the R_RISCV_RELAX reloc. */
*again = true;
riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
- pcgp_relocs, rel);
+ pcgp_relocs, backup_sec ? rel + 1 : rel,
+ backup_sec);
return true;
default:
@@ -5116,6 +5436,27 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
return true;
}
+/* Called by after_allocation to check if we need to run the whole
+ relaxations again. */
+
+bool
+bfd_elfNN_riscv_restart_relax_sections (struct bfd_link_info *info)
+{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+ bool restart = htab->restart_relax;
+ /* Reset the flag. */
+ htab->restart_relax = false;
+ return restart;
+}
+
+/* Called by after_allocation to free the relax backups at the end. */
+
+void
+bfd_elfNN_riscv_relax_free_backup (struct bfd_link_info *info)
+{
+ riscv_relax_recover_and_free_backup (info, false/* recover */);
+}
+
/* Called by after_allocation to set the information of data segment
before relaxing. */
@@ -5127,11 +5468,46 @@ bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
htab->data_segment_phase = data_segment_phase;
}
+/* Return the relax function according to the relocations. */
+
+static relax_func_t
+riscv_get_relax_func (struct bfd_link_info *info, int type, bool *relax_x0)
+{
+ relax_func_t t = NULL;
+ *relax_x0 = false;
+
+ if (type == R_RISCV_CALL
+ || type == R_RISCV_CALL_PLT)
+ t = _bfd_riscv_relax_call;
+ else if (type == R_RISCV_HI20
+ || type == R_RISCV_LO12_I
+ || type == R_RISCV_LO12_S)
+ t = _bfd_riscv_relax_lui;
+ else if (type == R_RISCV_TPREL_HI20
+ || type == R_RISCV_TPREL_ADD
+ || type == R_RISCV_TPREL_LO12_I
+ || type == R_RISCV_TPREL_LO12_S)
+ t = _bfd_riscv_relax_tls_le;
+ else if (!bfd_link_pic (info)
+ && (type == R_RISCV_PCREL_HI20
+ || type == R_RISCV_PCREL_LO12_I
+ || type == R_RISCV_PCREL_LO12_S))
+ t = _bfd_riscv_relax_pc;
+
+ if (t == _bfd_riscv_relax_lui
+ || t == _bfd_riscv_relax_pc)
+ *relax_x0 = true;
+
+ return t;
+}
+
/* Relax a section.
- Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
- deletes the obsolete bytes.
- Pass 1: Which cannot be disabled, handles code alignment directives. */
+ Pass 0: Recover symbols, relocations and codes according to backups.
+ Pass 1: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
+ deletes the obsolete bytes. Backup before doing any relaxation.
+ Pass 2: Which cannot be disabled, handles code alignment directives.
+ Pass 3: Check if any relaxation overflows. If so, re-do from Pass 0. */
static bool
_bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -5144,7 +5520,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
Elf_Internal_Rela *relocs;
bool ret = false;
unsigned int i;
- bfd_vma max_alignment, reserve_size = 0;
+ bfd_vma max_alignment = 0, reserve_size = 0;
riscv_pcgp_relocs pcgp_relocs;
static asection *first_section = NULL;
@@ -5156,19 +5532,15 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
|| (sec->flags & SEC_RELOC) == 0
|| (sec->flags & SEC_HAS_CONTENTS) == 0
|| (info->disable_target_specific_optimizations
- && info->relax_pass == 0)
+ && info->relax_pass == 1)
+ || (htab->params->relax_reserve
+ && (info->relax_pass == 0
+ || info->relax_pass == 3))
/* The exp_seg_relro_adjust is enum phase_enum (0x4),
and defined in ld/ldexp.h. */
|| *(htab->data_segment_phase) == 4)
return true;
- /* Record the first relax section, so that we can reset the
- max_alignment_for_gp for the repeated relax passes. */
- if (first_section == NULL)
- first_section = sec;
- else if (first_section == sec)
- htab->max_alignment_for_gp = -1;
-
riscv_init_pcgp_relocs (&pcgp_relocs);
/* Read this BFD's relocs if we haven't done so already. */
@@ -5177,14 +5549,46 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
else if (!(relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
info->keep_memory)))
goto fail;
+ data->relocs = relocs;
+
+ /* Read this BFD's contents if we haven't done so already. */
+ if (!data->this_hdr.contents
+ && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
+ goto fail;
+
+ /* Read this BFD's symbols if we haven't done so already. */
+ if (symtab_hdr->sh_info != 0
+ && !symtab_hdr->contents
+ && !(symtab_hdr->contents =
+ (unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ symtab_hdr->sh_info,
+ 0, NULL, NULL, NULL)))
+ goto fail;
+
+ riscv_relax_backup_sec *backup_sec = NULL;
+ if (info->relax_pass == 0)
+ riscv_relax_recover_and_free_backup (info, true/* recover */);
+ else if (!htab->params->relax_reserve)
+ /* Backup information before doing any relaxations. */
+ backup_sec = riscv_relax_search_and_create_backup (info, abfd, sec);
- /* Estimate the maximum alignment for all output sections once time
- should be enough. */
- max_alignment = htab->max_alignment;
- if (max_alignment == (bfd_vma) -1)
+ if (htab->params->relax_reserve)
{
- max_alignment = _bfd_riscv_get_max_alignment (sec, 0/* gp */);
- htab->max_alignment = max_alignment;
+ /* Record the first relax section, so that we can reset the
+ max_alignment_for_gp for the repeated relax passes. */
+ if (first_section == NULL)
+ first_section = sec;
+ else if (first_section == sec)
+ htab->max_alignment_for_gp = -1;
+
+ /* Estimate the maximum alignment for all output sections once time
+ should be enough. */
+ max_alignment = htab->max_alignment;
+ if (max_alignment == (bfd_vma) -1)
+ {
+ max_alignment = _bfd_riscv_get_max_alignment (sec, 0/* gp */);
+ htab->max_alignment = max_alignment;
+ }
}
/* Examine and consider relaxing each reloc. */
@@ -5197,65 +5601,51 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
bfd_vma symval;
char symtype;
bool undefined_weak = false;
+ bool relax_x0 = false;
relax_func = NULL;
riscv_relax_delete_bytes = NULL;
- if (info->relax_pass == 0)
+ if (info->relax_pass == 1)
{
- if (type == R_RISCV_CALL
- || type == R_RISCV_CALL_PLT)
- relax_func = _bfd_riscv_relax_call;
- else if (type == R_RISCV_HI20
- || type == R_RISCV_LO12_I
- || type == R_RISCV_LO12_S)
- relax_func = _bfd_riscv_relax_lui;
- else if (type == R_RISCV_TPREL_HI20
- || type == R_RISCV_TPREL_ADD
- || type == R_RISCV_TPREL_LO12_I
- || type == R_RISCV_TPREL_LO12_S)
- relax_func = _bfd_riscv_relax_tls_le;
- else if (!bfd_link_pic (info)
- && (type == R_RISCV_PCREL_HI20
- || type == R_RISCV_PCREL_LO12_I
- || type == R_RISCV_PCREL_LO12_S))
- relax_func = _bfd_riscv_relax_pc;
- else
- continue;
+ relax_func = riscv_get_relax_func (info, type, &relax_x0);
riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
-
- /* Only relax this reloc if it is paired with R_RISCV_RELAX. */
- if (i == sec->reloc_count - 1
+ if (relax_func == NULL
+ /* Only relax this reloc if it is paired with R_RISCV_RELAX. */
+ || i == sec->reloc_count - 1
|| ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
|| rel->r_offset != (rel + 1)->r_offset)
continue;
-
/* Skip over the R_RISCV_RELAX. */
i++;
}
- else if (info->relax_pass == 1 && type == R_RISCV_ALIGN)
+ else if (info->relax_pass == 2 && type == R_RISCV_ALIGN)
{
relax_func = _bfd_riscv_relax_align;
riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
}
+ else if (info->relax_pass == 3
+ && backup_sec != NULL
+ && backup_sec->sec != NULL
+ && backup_sec->relocs != NULL)
+ {
+ Elf_Internal_Rela *backup_rel = backup_sec->relocs + i;
+ int backup_type = ELFNN_R_TYPE (backup_rel->r_info);
+ relax_func = riscv_get_relax_func (info, backup_type, &relax_x0);
+ if (relax_func == NULL
+ /* Only check this reloc if it is paired with R_RISCV_RELAX. */
+ || i == backup_sec->sec->reloc_count - 1
+ || ELFNN_R_TYPE ((backup_rel + 1)->r_info) != R_RISCV_RELAX
+ || backup_rel->r_offset != (backup_rel + 1)->r_offset
+ /* Didn't relax. */
+ || backup_type == type)
+ continue;
+ relax_func = _bfd_riscv_relax_check_overflow;
+ /* Skip over the R_RISCV_RELAX. */
+ i++;
+ }
else
continue;
- data->relocs = relocs;
-
- /* Read this BFD's contents if we haven't done so already. */
- if (!data->this_hdr.contents
- && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
- goto fail;
-
- /* Read this BFD's symbols if we haven't done so already. */
- if (symtab_hdr->sh_info != 0
- && !symtab_hdr->contents
- && !(symtab_hdr->contents =
- (unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
- symtab_hdr->sh_info,
- 0, NULL, NULL, NULL)))
- goto fail;
-
/* Get the value of the symbol referred to by the reloc. */
if (ELFNN_R_SYM (rel->r_info) < symtab_hdr->sh_info)
{
@@ -5263,7 +5653,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
Elf_Internal_Sym *isym = ((Elf_Internal_Sym *) symtab_hdr->contents
+ ELFNN_R_SYM (rel->r_info));
reserve_size = (isym->st_size - rel->r_addend) > isym->st_size
- ? 0 : isym->st_size - rel->r_addend;
+ ? 0 : isym->st_size - rel->r_addend;
/* Relocate against local STT_GNU_IFUNC symbol. we have created
a fake global symbol entry for this, so deal with the local ifunc
@@ -5312,8 +5702,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
/* The linker_def symbol like __ehdr_start that may be undefweak
for now, but will be guaranteed to be defined later. */
&& !h->root.linker_def
- && (relax_func == _bfd_riscv_relax_lui
- || relax_func == _bfd_riscv_relax_pc))
+ && relax_x0)
{
/* For the lui and auipc relaxations, since the symbol
value of an undefined weak symbol is always be zero,
@@ -5396,12 +5785,13 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
max_alignment, reserve_size, again,
- &pcgp_relocs, undefined_weak))
+ &pcgp_relocs, undefined_weak, backup_sec))
goto fail;
}
/* Resolve R_RISCV_DELETE relocations. */
- if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, relocs))
+ if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, data->relocs,
+ backup_sec))
goto fail;
ret = true;
@@ -5411,6 +5801,13 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
free (relocs);
riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec);
+ /* Need to re-do the relaxations from pass 0. */
+ if (info->relax_pass == 3 && *again)
+ {
+ htab->restart_relax = true;
+ *again = false;
+ }
+
return ret;
}
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index a6511f6558d..10160a387c6 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -958,6 +958,11 @@ static reloc_howto_type howto_table_internal[] =
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
+
+ /* R_RISCV_GPREL_DELETE. */
+ EMPTY_HOWTO (6),
+ /* R_RISCV_TPREL_DELETE. */
+ EMPTY_HOWTO (7),
};
/* A mapping from BFD reloc types to RISC-V ELF reloc types. */
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 49be71746b9..248a62765de 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -31,6 +31,8 @@ struct riscv_elf_params
{
/* Whether to relax code sequences to GP-relative addressing. */
bool relax_gp;
+ /* Whether to reserve the size when doing relaxations. */
+ bool relax_reserve;
/* Whether to check if SUB_ULEB128 relocation has non-zero addend. */
bool check_uleb128;
};
@@ -128,3 +130,13 @@ 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 *);
+
+extern bool
+bfd_elf32_riscv_restart_relax_sections (struct bfd_link_info *);
+extern bool
+bfd_elf64_riscv_restart_relax_sections (struct bfd_link_info *);
+
+extern void
+bfd_elf32_riscv_relax_free_backup (struct bfd_link_info *);
+extern void
+bfd_elf64_riscv_relax_free_backup (struct bfd_link_info *);
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 24903c04d91..0ce01263d9d 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -45,6 +45,7 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
RELOC_NUMBER (R_RISCV_TLS_TPREL32, 10)
RELOC_NUMBER (R_RISCV_TLS_TPREL64, 11)
RELOC_NUMBER (R_RISCV_TLSDESC, 12)
+ /* Reserved 13-15. */
/* Relocation types not used by the dynamic linker. */
RELOC_NUMBER (R_RISCV_BRANCH, 16)
@@ -72,14 +73,11 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
RELOC_NUMBER (R_RISCV_SUB16, 38)
RELOC_NUMBER (R_RISCV_SUB32, 39)
RELOC_NUMBER (R_RISCV_SUB64, 40)
+ /* Reserved 41-42. */
RELOC_NUMBER (R_RISCV_ALIGN, 43)
RELOC_NUMBER (R_RISCV_RVC_BRANCH, 44)
RELOC_NUMBER (R_RISCV_RVC_JUMP, 45)
- RELOC_NUMBER (R_RISCV_RVC_LUI, 46)
- RELOC_NUMBER (R_RISCV_GPREL_I, 47)
- RELOC_NUMBER (R_RISCV_GPREL_S, 48)
- RELOC_NUMBER (R_RISCV_TPREL_I, 49)
- RELOC_NUMBER (R_RISCV_TPREL_S, 50)
+ /* Reserved 46-50. */
RELOC_NUMBER (R_RISCV_RELAX, 51)
RELOC_NUMBER (R_RISCV_SUB6, 52)
RELOC_NUMBER (R_RISCV_SET6, 53)
@@ -104,6 +102,8 @@ END_RELOC_NUMBERS (R_RISCV_max)
#define R_RISCV_GPREL_S (R_RISCV_max + 3)
#define R_RISCV_TPREL_I (R_RISCV_max + 4)
#define R_RISCV_TPREL_S (R_RISCV_max + 5)
+#define R_RISCV_GPREL_DELETE (R_RISCV_max + 6)
+#define R_RISCV_TPREL_DELETE (R_RISCV_max + 7)
/* Processor specific flags for the ELF header e_flags field. */
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index afc43ed55dc..576f814ae9b 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -26,6 +26,7 @@ fragment <<EOF
#include "elfxx-riscv.h"
static struct riscv_elf_params params = { .relax_gp = 1,
+ .relax_reserve = 1,
.check_uleb128 = 0};
EOF
@@ -34,6 +35,8 @@ EOF
PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
{ "relax-gp", no_argument, NULL, OPTION_RELAX_GP },
{ "no-relax-gp", no_argument, NULL, OPTION_NO_RELAX_GP },
+ { "relax-reserve", no_argument, NULL, OPTION_RELAX_RESERVE },
+ { "no-relax-reserve", no_argument, NULL, OPTION_NO_RELAX_RESERVE },
{ "check-uleb128", no_argument, NULL, OPTION_CHECK_ULEB128 },
{ "no-check-uleb128", no_argument, NULL, OPTION_NO_CHECK_ULEB128 },
'
@@ -41,6 +44,14 @@ PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
fprintf (file, _(" --relax-gp Perform GP relaxation\n"));
fprintf (file, _(" --no-relax-gp Don'\''t perform GP relaxation\n"));
+ fprintf (file, _(" --relax-reserve Reserve enough space when doing relaxations\n"
+ " in case the code won'\''t be moved forward, and\n"
+ " then cause truncated errors when resolving\n"
+ " relocations\n"));
+ fprintf (file, _(" --no-relax-reserve Don'\''t reserve space when doing relaxations\n"
+ " but backup information before relaxing, so that\n"
+ " can disable the truncated patterns, recover\n"
+ " stuffs, and then re-do the whole relaxations\n"));
fprintf (file, _(" --check-uleb128 Check if SUB_ULEB128 has non-zero addend\n"));
fprintf (file, _(" --no-check-uleb128 Don'\''t check if SUB_ULEB128 has non-zero addend\n"));
'
@@ -54,6 +65,14 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
params.relax_gp = 0;
break;
+ case OPTION_RELAX_RESERVE:
+ params.relax_reserve = 1;
+ break;
+
+ case OPTION_NO_RELAX_RESERVE:
+ params.relax_reserve = 0;
+ break;
+
case OPTION_CHECK_ULEB128:
params.check_uleb128 = 1;
break;
@@ -81,7 +100,7 @@ riscv_elf_before_allocation (void)
ENABLE_RELAXATION;
}
- link_info.relax_pass = 2;
+ link_info.relax_pass = 4;
}
static void
@@ -115,7 +134,12 @@ gld${EMULATION_NAME}_after_allocation (void)
enum phase_enum *phase = &(expld.dataseg.phase);
bfd_elf${ELFSIZE}_riscv_set_data_segment_info (&link_info, (int *) phase);
- ldelf_map_segments (need_layout);
+ do
+ {
+ ldelf_map_segments (need_layout);
+ }
+ while (bfd_elf${ELFSIZE}_riscv_restart_relax_sections (&link_info));
+ bfd_elf${ELFSIZE}_riscv_relax_free_backup (&link_info);
}
/* This is a convenient point to tell BFD about target specific flags.
diff --git a/ld/ldlex.h b/ld/ldlex.h
index bb431101fb2..c40dc60cd81 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -425,6 +425,8 @@ enum option_values
/* Used by emultempl/riscvelf.em. */
OPTION_RELAX_GP,
OPTION_NO_RELAX_GP,
+ OPTION_RELAX_RESERVE,
+ OPTION_NO_RELAX_RESERVE,
OPTION_CHECK_ULEB128,
OPTION_NO_CHECK_ULEB128,
/* Used by emultempl/rxelf.em. */
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
index ce6ca691fa7..2340b634437 100644
--- a/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
+++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
@@ -20,6 +20,7 @@ bar:
lw x1, %pcrel_lo (.L1) (x1)
.endif
+ .option norelax
.L2:
auipc x2, %pcrel_hi (foo_addr)
.ifdef __64_bit__
@@ -27,6 +28,7 @@ bar:
.else
lw x2, %pcrel_lo (.L2) (x2)
.endif
+ .option relax
ret
.size bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
index c3022be0e08..024bd73e8dd 100644
--- a/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
@@ -20,6 +20,7 @@ bar:
lw x1, %pcrel_lo (.L1) (x1)
.endif
+ .option norelax
.L2:
auipc x2, %pcrel_hi (foo_addr)
.ifdef __64_bit__
@@ -27,6 +28,7 @@ bar:
.else
lw x2, %pcrel_lo (.L2) (x2)
.endif
+ .option relax
.L3:
auipc x3, %pcrel_hi (foo)
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
index b49bda1279d..d75959c3909 100644
--- a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
@@ -12,6 +12,7 @@ foo_resolver:
.globl bar
.type bar, @function
bar:
+ .option norelax
.L1:
auipc x1, %pcrel_hi (foo_addr)
.ifdef __64_bit__
@@ -19,6 +20,7 @@ bar:
.else
lw x1, %pcrel_lo (.L1) (x1)
.endif
+ .option relax
ret
.size bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 5270addde26..527abbffebc 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -167,7 +167,14 @@ if [istarget "riscv*-*-*"] {
run_dump_test "attr-merge-stack-align-failed"
run_dump_test "attr-phdr"
run_dump_test "relax-max-align-gp"
- run_dump_test "relax-data-segment-align"
+ run_dump_test "relax-truncated-fixed-addr"
+ run_dump_test "relax-truncated-fixed-addr-no-reserve"
+ run_dump_test "relax-truncated-align-addr"
+ run_dump_test "relax-truncated-align-addr-no-reserve"
+ run_dump_test "relax-truncated-align-between-output-sec"
+ run_dump_test "relax-truncated-align-between-output-sec-no-reserve"
+ run_dump_test "relax-truncated-data-segment-align"
+ run_dump_test "relax-truncated-data-segment-align-no-reserve"
run_dump_test "uleb128"
run_dump_test "pr31179"
run_dump_test "pr31179-r"
diff --git a/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.s b/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.s
index 50cdccceed9..71e52a906a8 100644
--- a/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.s
+++ b/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.s
@@ -1,4 +1,5 @@
.text
+ .option norelax
.globl _start
_start:
auipc ra, %pcrel_hi(tdata)
diff --git a/ld/testsuite/ld-riscv-elf/relax-data-segment-align.d b/ld/testsuite/ld-riscv-elf/relax-data-segment-align.d
deleted file mode 100644
index 22aeb4c3f90..00000000000
--- a/ld/testsuite/ld-riscv-elf/relax-data-segment-align.d
+++ /dev/null
@@ -1,8 +0,0 @@
-#source: relax-data-segment-align.s
-#ld:
-#objdump: -d
-
-#failif
-#...
-.*gp.*
-#...
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-no-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-no-reserve.d
new file mode 100644
index 00000000000..2a2ceb97d09
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-no-reserve.d
@@ -0,0 +1,26 @@
+#source: relax-truncated.s
+#ld: --no-relax-reserve -Trelax-truncated-align-addr.ld
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ ]+[0-9a-f]+[ ]+jal[ ]+.*<bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+.*
+.*:[ ]+[0-9a-f]+[ ]+jalr[ ]+.*<baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ ]+[0-9a-f]+[ ]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ ]+[0-9a-f]+[ ]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d
new file mode 100644
index 00000000000..4bbb82ebeeb
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d
@@ -0,0 +1,27 @@
+#source: relax-truncated.s
+#ld: --relax-reserve -Trelax-truncated-align-addr.ld
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+.*
+.*:[ ]+[0-9a-f]+[ ]+jalr[ ]+.*<bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+.*
+.*:[ ]+[0-9a-f]+[ ]+jalr[ ]+.*<baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ ]+[0-9a-f]+[ ]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ ]+[0-9a-f]+[ ]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld
new file mode 100644
index 00000000000..af9ea425fd3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld
@@ -0,0 +1,5 @@
+SECTIONS {
+ .text.a 0x0 : { *(.text.a) }
+ .text.b : { *(.text.b) }
+ .text.c : ALIGN(0x100400) { *(.text.c) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-no-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-no-reserve.d
new file mode 100644
index 00000000000..c13230bc706
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-no-reserve.d
@@ -0,0 +1,26 @@
+#source: relax-truncated.s
+#ld: --no-relax-reserve -Trelax-truncated-align-between-output-sec.ld
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ ]+[0-9a-f]+[ ]+jal[ ]+[0-9a-f]+ <bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+ra,.*
+.*:[ ]+[0-9a-f]+[ ]+jalr[ ]+ra # [0-9a-f]+ <baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ ]+[0-9a-f]+[ ]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ ]+[0-9a-f]+[ ]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d
new file mode 100644
index 00000000000..7fb8eb60c59
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d
@@ -0,0 +1,3 @@
+#source: relax-truncated.s
+#ld: --relax-reserve -Trelax-truncated-align-between-output-sec.ld
+#error: .*relocation truncated to fit: R_RISCV_JAL against `baz'.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld
new file mode 100644
index 00000000000..eec27f23d4a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld
@@ -0,0 +1,6 @@
+SECTIONS {
+ .text.a 0x0 : { *(.text.a) }
+ .text.b : { *(.text.b) }
+ . = ALIGN (0x100400);
+ .text.c : { *(.text.c) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-no-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-no-reserve.d
new file mode 100644
index 00000000000..eaa9af10d1d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-no-reserve.d
@@ -0,0 +1,18 @@
+#source: relax-truncated-data-segment-align.s
+#ld: --no-relax-reserve -Ttext 0x10094
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <_start>:
+.*:[ ]+[0-9a-f]+[ ]+jal[ ]+.*
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+a0,.*
+.*:[ ]+[0-9a-f]+[ ]+addi[ ]+a0,a0,.*
+.*:[ ]+[0-9a-f]+[ ]+lui[ ]+a0,.*
+.*:[ ]+[0-9a-f]+[ ]+addi[ ]+a0,a0,.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d
new file mode 100644
index 00000000000..9398a49cbb8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d
@@ -0,0 +1,18 @@
+#source: relax-truncated-data-segment-align.s
+#ld: --relax-reserve -Ttext 0x10094
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <_start>:
+.*:[ ]+[0-9a-f]+[ ]+jal[ ]+.*
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+a0,.*
+.*:[ ]+[0-9a-f]+[ ]+addi[ ]+a0,a0,.*
+.*:[ ]+[0-9a-f]+[ ]+lui[ ]+a0,.*
+.*:[ ]+[0-9a-f]+[ ]+addi[ ]+a0,a0,.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-data-segment-align.s b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s
similarity index 69%
rename from ld/testsuite/ld-riscv-elf/relax-data-segment-align.s
rename to ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s
index 98718922fb5..c483c5ddf9d 100644
--- a/ld/testsuite/ld-riscv-elf/relax-data-segment-align.s
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s
@@ -2,8 +2,13 @@
.globl _start
_start:
.rept 6000
- lla a0, symbol
+ call _start
.endr
+ .globl foo
+foo:
+ lla a0, symbol
+ lui a0, %hi(symbol)
+ addi a0, a0, %lo(symbol)
.section .rodata
.set symbol, . + 4598
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-no-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-no-reserve.d
new file mode 100644
index 00000000000..669c21392f7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-no-reserve.d
@@ -0,0 +1,26 @@
+#source: relax-truncated.s
+#ld: --no-relax-reserve -Trelax-truncated-fixed-addr.ld
+#objdump: -d
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ ]+[0-9a-f]+[ ]+jal[ ]+.*<bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ ]+[0-9a-f]+[ ]+auipc[ ]+.*
+.*:[ ]+[0-9a-f]+[ ]+jalr[ ]+.*<baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ ]+[0-9a-f]+[ ]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ ]+[0-9a-f]+[ ]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d
new file mode 100644
index 00000000000..cf3523acb48
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d
@@ -0,0 +1,3 @@
+#source: relax-truncated.s
+#ld: --relax-reserve -Trelax-truncated-fixed-addr.ld
+#error: .*relocation truncated to fit: R_RISCV_JAL against `baz'.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld
new file mode 100644
index 00000000000..ad93798e76d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld
@@ -0,0 +1,5 @@
+SECTIONS {
+ .text.a 0x0 : { *(.text.a) }
+ .text.b : { *(.text.b) }
+ .text.c 0x100400 : { *(.text.c) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated.s b/ld/testsuite/ld-riscv-elf/relax-truncated.s
new file mode 100644
index 00000000000..fc1382618a6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated.s
@@ -0,0 +1,20 @@
+ .section .text.a, "ax"
+ .align 2
+ .globl _start
+_start:
+ .rept 0x100
+ call bar
+ .endr
+ .globl foo
+foo:
+ call baz
+
+ .section .text.b, "ax"
+ .align 2
+bar:
+ nop
+
+ .section .text.c, "ax"
+ .align 2
+baz:
+ nop
--
2.39.3 (Apple Git-146)
More information about the Binutils
mailing list