[PATCH] RISC-V: Add --relax-verbose option for debuging linker relaxation
Kito Cheng
kito.cheng@sifive.com
Mon Jun 2 14:05:30 GMT 2025
More information about the Binutils mailing list
Mon Jun 2 14:05:30 GMT 2025
- Previous message (by thread): [PATCH v2] aarch64: Support for FEAT_SVE_F16F32MM, FEAT_F8F16M, FEAT_F8F32MM
- Next message (by thread): [PATCH] config: Update obsolete macro in pkg.m4
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
We need spend lots of time to figure out what happened when we found a symbol
isn't get relaxed, so...why not add a verbose option for linker relaxation?
It's not cover all kind of relaxation yet, but covered most common use case:
function call relaxation and GP relaxation.
This would be useful for debugging linker relaxation issues, especially
when we doing Zcmt and landing pad relaxation...(those are not
upstreamed yet.).
Output example:
$ riscv64-unknown-elf-gcc ~/hello.c -Wl,--relax-verbose
...
relax verbose: libc_a-puts.o: .text._puts_r Symbol strlen reserve_size is 0, symbol size is 154.
relax verbose: libc_a-puts.o: .text._puts_r Function call relaxation success, relax to j[al] (target: strlen, offset: 1386).
relax verbose: libc_a-puts.o: .text._puts_r Symbol .LC0 reserve_size is 0, symbol size is 0.
relax verbose: libc_a-puts.o: .text._puts_r GP relaxation success(target: .LC0).
relax verbose: libc_a-puts.o: .text._puts_r Symbol .LC0 reserve_size is 0, symbol size is 0.
relax verbose: libc_a-puts.o: .text._puts_r Symbol __sfvwrite_r reserve_size is 0, symbol size is 914.
relax verbose: libc_a-puts.o: .text._puts_r Function call relaxation success, relax to j[al] (target: __sfvwrite_r, offset: 5198).
relax verbose: libc_a-puts.o: .text._puts_r Symbol __sinit reserve_size is 0, symbol size is 22.
relax verbose: libc_a-puts.o: .text._puts_r Function call relaxation success, relax to j[al] (target: __sinit, offset: -542).
relax verbose: libc_a-puts.o: .text.puts Symbol _impure_ptr reserve_size is 8, symbol size is 8.
relax verbose: libc_a-puts.o: .text.puts GP relaxation success(target: _impure_ptr).
relax verbose: libc_a-puts.o: .text.puts Symbol _impure_ptr reserve_size is 8, symbol size is 8.
relax verbose: libc_a-puts.o: .text.puts Symbol _puts_r reserve_size is 8, symbol size is 132.
...
---
bfd/elfnn-riscv.c | 195 ++++++++++++++++++++++++++++++++++++++-
bfd/elfxx-riscv.h | 2 +
ld/emultempl/riscvelf.em | 6 ++
ld/ldlex.h | 1 +
4 files changed, 201 insertions(+), 3 deletions(-)
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 1c494f5f986..9623e6833f6 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -252,11 +252,15 @@ struct riscv_elf_link_hash_table
&& elf_hash_table_id (elf_hash_table (p)) == RISCV_ELF_DATA) \
? (struct riscv_elf_link_hash_table *) (p)->hash : NULL)
+/* Print verbose info when linker relaxation. */
+static bool relax_verbose = false;
+
void
riscv_elfNN_set_options (struct bfd_link_info *link_info,
struct riscv_elf_params *params)
{
riscv_elf_hash_table (link_info)->params = params;
+ relax_verbose = params->relax_verbose;
}
static bool
@@ -292,6 +296,85 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
? (MINUS_ONE << howto->bitsize) : (bfd_vma)0)) != 0);
}
+/* Return true if the given symbol index is a local symbol. */
+static inline bool
+riscv_is_local_symbol (Elf_Internal_Shdr *symtab_hdr, unsigned long symndx)
+{
+ /* Document:
+ https://docs.oracle.com/cd/E19683-01/816-7529/chapter6-79797/index.html
+
+ > a symbol table section's sh_info section header member holds the
+ > symbol table index for the first non-local symbol. */
+ return symndx < symtab_hdr->sh_info;
+}
+
+static size_t
+riscv_get_symbol_size (bfd *abfd, unsigned long symndx)
+{
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+
+ if (!symtab_hdr->contents)
+ return 0;
+
+ if (symndx < symtab_hdr->sh_info)
+ {
+ /* A local symbol. */
+ Elf_Internal_Sym *sym
+ = ((Elf_Internal_Sym *) symtab_hdr->contents + symndx);
+ return sym->st_size;
+ }
+ else
+ {
+ struct elf_link_hash_entry *h;
+ unsigned indx = symndx - symtab_hdr->sh_info;
+ h = elf_sym_hashes (abfd)[indx];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ if (h != NULL && h->type != STT_GNU_IFUNC)
+ return h->size;
+ else
+ /* We do not handle STT_GNU_IFUNC currently. */
+ return 0;
+ }
+}
+
+static const char *
+riscv_get_symbol_name (bfd *abfd, unsigned long symndx)
+{
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+ const char *name;
+
+ if (!symtab_hdr->contents)
+ return NULL;
+
+ if (symndx < symtab_hdr->sh_info)
+ {
+ /* A local symbol. */
+ Elf_Internal_Sym *sym
+ = ((Elf_Internal_Sym *) symtab_hdr->contents + symndx);
+ name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+ }
+ else
+ {
+ struct elf_link_hash_entry *h;
+ unsigned indx = symndx - symtab_hdr->sh_info;
+ h = elf_sym_hashes (abfd)[indx];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ if (h != NULL && h->type != STT_GNU_IFUNC)
+ name = h->root.root.string;
+ else
+ /* We do not handle STT_GNU_IFUNC currently. */
+ return NULL;
+ }
+
+ return name;
+}
+
/* PLT/GOT stuff. */
#define PLT_HEADER_INSNS 8
#define PLT_ENTRY_INSNS 4
@@ -4284,6 +4367,24 @@ riscv_elf_obj_attrs_handle_unknown (bfd *abfd, int tag)
return true;
}
+/* Relax verbose function. */
+static void
+_riscv_verbose_relax (bfd *abfd, asection *sec, const char *fmt, ...)
+{
+ if (!relax_verbose)
+ return;
+
+ va_list args;
+ va_start (args, fmt);
+ fprintf (stderr, "relax verbose: %s: %s ", bfd_get_filename (abfd),
+ bfd_section_name (sec));
+
+ vfprintf (stderr, fmt, args);
+
+ fprintf (stderr, "\n");
+ va_end (args);
+}
+
/* A second format for recording PC-relative hi relocations. This stores the
information required to relax them to GP-relative addresses. */
@@ -4690,6 +4791,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
bool near_zero = (symval + RISCV_IMM_REACH / 2) < RISCV_IMM_REACH;
bfd_vma auipc, jalr;
int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
+ unsigned symidx = ELFNN_R_SYM (rel->r_info);
+ const char *sym_str = riscv_get_symbol_name (abfd, symidx);
/* If the call crosses section boundaries, an alignment directive could
cause the PC-relative offset to later increase, so we need to add in the
@@ -4705,7 +4808,15 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
/* See if this function call can be shortened. */
if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero))
- return true;
+ {
+ uint64_t pc = sec_addr (sec) + rel->r_offset;
+ _riscv_verbose_relax (
+ abfd, sec,
+ "Function call relaxation fail due to range too far "
+ "(pc: %" PRIx64 " target: %s (%" PRIx64 "), offset: %" PRId64 ").",
+ (uint64_t) pc, sym_str, (uint64_t) symval, (int64_t) foff);
+ return true;
+ }
/* Shorten the function call. */
BFD_ASSERT (rel->r_offset + 8 <= sec->size);
@@ -4718,24 +4829,28 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
/* C.J exists on RV32 and RV64, but C.JAL is RV32-only. */
rvc = rvc && (rd == 0 || (rd == X_RA && ARCH_SIZE == 32));
+ const char *relax_type;
if (rvc)
{
/* Relax to C.J[AL] rd, addr. */
r_type = R_RISCV_RVC_JUMP;
auipc = rd == 0 ? MATCH_C_J : MATCH_C_JAL;
len = 2;
+ relax_type = "c.j[al]";
}
else if (VALID_JTYPE_IMM (foff))
{
/* Relax to JAL rd, addr. */
r_type = R_RISCV_JAL;
auipc = MATCH_JAL | (rd << OP_SH_RD);
+ relax_type = "j[al]";
}
else
{
/* Near zero, relax to JALR rd, x0, addr. */
r_type = R_RISCV_LO12_I;
auipc = MATCH_JALR | (rd << OP_SH_RD);
+ relax_type = "zero page relaxation";
}
/* Replace the R_RISCV_CALL reloc. */
@@ -4745,6 +4860,12 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
/* Delete unnecessary JALR and reuse the R_RISCV_RELAX reloc. */
*again = true;
+
+ _riscv_verbose_relax (abfd, sec,
+ "Function call relaxation success, relax to %s "
+ "(target: %s, offset: %" PRId64 ").",
+ relax_type, sym_str, (int64_t) foff);
+
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
link_info, pcgp_relocs, rel + 1);
}
@@ -4836,6 +4957,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
? data_segment_alignment : max_alignment;
}
+ unsigned symidx = ELFNN_R_SYM (rel->r_info);
+ const char *sym_str = riscv_get_symbol_name (abfd, symidx);
/* Is the reference in range of x0 or gp?
Valid gp range conservatively because of alignment issue.
@@ -4861,6 +4984,10 @@ _bfd_riscv_relax_lui (bfd *abfd,
case R_RISCV_HI20:
/* Delete unnecessary LUI and reuse the reloc. */
*again = true;
+ _riscv_verbose_relax (abfd, sec,
+ "GP relaxation success"
+ "(target: %s).",
+ sym_str);
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
link_info, pcgp_relocs, rel);
@@ -4894,6 +5021,10 @@ _bfd_riscv_relax_lui (bfd *abfd,
/* Delete extra bytes and reuse the R_RISCV_RELAX reloc. */
*again = true;
+ _riscv_verbose_relax (abfd, sec,
+ "GP relaxation success"
+ "(target: %s).",
+ sym_str);
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
link_info, pcgp_relocs, rel + 1);
}
@@ -5029,6 +5160,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
BFD_ASSERT (rel->r_offset + 4 <= sec->size);
+ unsigned symidx = ELFNN_R_SYM (rel->r_info);
+ const char *sym_str = riscv_get_symbol_name (abfd, symidx);
/* Chain the _LO relocs to their cooresponding _HI reloc to compute the
actual target address. */
riscv_pcgp_hi_reloc hi_reloc;
@@ -5067,12 +5200,31 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
/* Mergeable symbols and code might later move out of range. */
if (! undefined_weak
&& sym_sec->flags & (SEC_MERGE | SEC_CODE))
- return true;
+ {
+ if (sym_sec->flags & SEC_MERGE)
+ _riscv_verbose_relax (
+ abfd, sec, "GP relaxation failed due to `%s` is SEC_MERGE.",
+ sym_str);
+ if (sym_sec->flags & SEC_CODE)
+ _riscv_verbose_relax (
+ abfd, sec,
+ "GP relaxation failed due to `%s` the symbol is SEC_CODE.",
+ sym_str);
+
+ return true;
+ }
/* If the cooresponding lo relocation has already been seen then it's not
safe to relax this relocation. */
if (riscv_find_pcgp_lo_reloc (pcgp_relocs, rel->r_offset))
- return true;
+ {
+ _riscv_verbose_relax (
+ abfd, sec,
+ "GP relaxation failed due to R_RISCV_PCREL_LO12_* "
+ "before R_RISCV_PCREL_HI20");
+
+ return true;
+ }
break;
@@ -5148,6 +5300,11 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
*again = true;
riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
pcgp_relocs, rel);
+ _riscv_verbose_relax (abfd, sec,
+ "GP relaxation success"
+ "(target: %s, offset: %" PRId64 ").",
+ sym_str, 0);
+
return true;
default:
@@ -5155,6 +5312,29 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
}
}
+ if (gp && VALID_ITYPE_IMM (symval - gp))
+ {
+ _riscv_verbose_relax (
+ abfd, sec,
+ "GP relaxation failed due to max_alignment or reserve_size"
+ "(target: %s, gp: 0x%" PRIx64 " sym addr: 0x%" PRIx64 ", "
+ "distance: %" PRId64 ", max_alignment: %" PRId64
+ ", reserve_size: %" PRId64 ").",
+ sym_str, gp, symval, gp - symval, max_alignment, reserve_size);
+ }
+ else if (gp && !VALID_ITYPE_IMM (symval - gp))
+ _riscv_verbose_relax (abfd, sec,
+ "GP relaxation failed due to far from GP"
+ "(target: %s, gp: 0x%" PRIx64 " sym addr: 0x%" PRIx64
+ ", distance: %" PRId64 ").",
+ sym_str, gp, symval, gp - symval);
+ else
+ _riscv_verbose_relax (abfd, sec,
+ "GP relaxation failed due to unknwon reason"
+ "(target: %s, gp: 0x%" PRIx64 " sym addr: 0x%" PRIx64
+ ", distance: %" PRId64 ").",
+ sym_str, gp, symval, gp - symval);
+
return true;
}
@@ -5435,6 +5615,15 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
symval += rel->r_addend;
symval += sec_addr (sym_sec);
+ unsigned symidx = ELFNN_R_SYM (rel->r_info);
+ const char *sym_str = riscv_get_symbol_name (abfd, symidx);
+ size_t *sym_size = riscv_get_symbol_size (abfd, symidx);
+
+ _riscv_verbose_relax (abfd, sec,
+ "Symbol %s "
+ "reserve_size is %" PRId64
+ ", symbol size is %" PRId64 ".",
+ sym_str, reserve_size, sym_size);
if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
max_alignment, reserve_size, again,
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 1ce682a97ac..03bdb7d26f7 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -33,6 +33,8 @@ struct riscv_elf_params
bool relax_gp;
/* Whether to check if SUB_ULEB128 relocation has non-zero addend. */
bool check_uleb128;
+ /* Verbose mode for linker relaxation. */
+ bool relax_verbose;
};
extern void riscv_elf32_set_options (struct bfd_link_info *,
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index 78e1fcde68e..cf75e474e90 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -36,6 +36,7 @@ PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
{ "no-relax-gp", no_argument, NULL, OPTION_NO_RELAX_GP },
{ "check-uleb128", no_argument, NULL, OPTION_CHECK_ULEB128 },
{ "no-check-uleb128", no_argument, NULL, OPTION_NO_CHECK_ULEB128 },
+ { "relax-verbose", no_argument, NULL, OPTION_RELAX_VERBOSE },
'
PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
@@ -43,6 +44,7 @@ PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
fprintf (file, _(" --no-relax-gp Don'\''t perform GP relaxation\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"));
+ fprintf (file, _(" --relax-verbose Verbose output for linker relaxation\n"));
'
PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
@@ -61,6 +63,10 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
case OPTION_NO_CHECK_ULEB128:
params.check_uleb128 = 0;
break;
+
+ case OPTION_RELAX_VERBOSE:
+ params.relax_verbose = 1;
+ break;
'
fragment <<EOF
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 815da76a4c0..72db6fbb08c 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -427,6 +427,7 @@ enum option_values
OPTION_NO_RELAX_GP,
OPTION_CHECK_ULEB128,
OPTION_NO_CHECK_ULEB128,
+ OPTION_RELAX_VERBOSE,
/* Used by emultempl/rxelf.em. */
OPTION_NO_FLAG_MISMATCH_WARNINGS,
OPTION_IGNORE_LMA,
--
2.34.1
- Previous message (by thread): [PATCH v2] aarch64: Support for FEAT_SVE_F16F32MM, FEAT_F8F16M, FEAT_F8F32MM
- Next message (by thread): [PATCH] config: Update obsolete macro in pkg.m4
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Binutils mailing list