[PATCHv2] xtensa: add R_XTENSA_PDIFF_ULEB128 for DWARF uleb128 diffs
Alexey Lapshin
alexey.lapshin@espressif.com
Mon Aug 10 11:50:23 GMT 2026
Max, sorry for the inconvenience. I resent the patch for the master branch
(the previous patch was for 2.46).
Also, I'm sending the patch as a file for you. I'm not sure if my mail
client sends messages as plain text even though I chose this method.
On Mon, 2026-08-10 at 18:46 +0700, Alexey Lapshin wrote:
> DWARF 5 location and range lists encode many same-section symbol
> differences as uleb128. Today the assembler must mark all covered code
> no_transform so linker relaxation cannot change those differences, which
> effectively disables relaxation for nearly every function that has DWARF 5
> debug info.
>
> Emit R_XTENSA_PDIFF_ULEB128 instead: like R_XTENSA_PDIFF32 the difference
> stays in the section contents and the reloc records the subtracted symbol,
> but the assembler pads the uleb128 to a fixed width (minimal encoding plus
> one spare byte) and the linker rewrites the value in place after
> relaxation.
>
> sleb128 and cross-section differences still fall back to no_transform.
> Growth that needs more than the reserved spare byte is reported as a
> relocation overflow.
>
> bfd/
> * bfd-in2.h: Regenerated.
> * elf32-xtensa.c (elf_howto_table): New entry for
> R_XTENSA_PDIFF_ULEB128.
> (elf_xtensa_reloc_type_lookup, elf_xtensa_do_reloc): Handle it.
> (relax_section): Rewrite padded uleb128 differences in place.
> * libbfd.h (bfd_reloc_code_real_names): Add
> BFD_RELOC_XTENSA_PDIFF_ULEB128.
> * reloc.c: Document BFD_RELOC_XTENSA_PDIFF_ULEB128.
>
> binutils/
> * readelf.c (is_none_reloc): Recognize R_XTENSA_PDIFF_ULEB128.
>
> gas/
> * write.c (cvt_frag_to_fill): Pad leb128 frags to the reserved
> size when md_leb128_frag_size grew them.
> (relax_segment): Call md_leb128_frag_size if defined.
> * config/tc-xtensa.h (xtensa_symfield_type): Add
> has_leb128_diff_reloc.
> (md_leb128_frag_size): Define.
> * config/tc-xtensa.c (xtensa_md_finish): Insert uleb128 fixes.
> (xtensa_insert_uleb128_fixes, xtensa_leb128_frag_size): New.
> (xtensa_mark_difference_of_two_symbols): Skip relocated diffs.
> (md_apply_fix): Ignore BFD_RELOC_XTENSA_PDIFF_ULEB128.
> * testsuite/gas/xtensa/pdiff-uleb128.s: New test.
> * testsuite/gas/xtensa/pdiff-uleb128.d: New test.
> * testsuite/gas/xtensa/pdiff-uleb128-norelax.d: New test.
>
> include/
> * elf/xtensa.h (elf_xtensa_reloc_type): Add
> R_XTENSA_PDIFF_ULEB128.
>
> ld/
> * testsuite/ld-xtensa/relax-uleb128.s: New test.
> * testsuite/ld-xtensa/relax-uleb128.d: New test.
> * testsuite/ld-xtensa/relax-uleb128-2.s: New test.
> * testsuite/ld-xtensa/relax-uleb128-2.d: New test.
> * testsuite/ld-xtensa/xtensa.exp: Run them.
> ---
> bfd/bfd-in2.h | 10 +++
> bfd/elf32-xtensa.c | 80 +++++++++++++++++
> bfd/libbfd.h | 1 +
> bfd/reloc.c | 12 +++
> binutils/readelf.c | 3 +-
> gas/config/tc-xtensa.c | 90 ++++++++++++++++++-
> gas/config/tc-xtensa.h | 5 ++
> .../gas/xtensa/pdiff-uleb128-norelax.d | 20 +++++
> gas/testsuite/gas/xtensa/pdiff-uleb128.d | 12 +++
> gas/testsuite/gas/xtensa/pdiff-uleb128.s | 7 ++
> gas/write.c | 20 +++++
> include/elf/xtensa.h | 3 +
> ld/testsuite/ld-xtensa/relax-uleb128-2.d | 12 +++
> ld/testsuite/ld-xtensa/relax-uleb128-2.s | 18 ++++
> ld/testsuite/ld-xtensa/relax-uleb128.d | 11 +++
> ld/testsuite/ld-xtensa/relax-uleb128.s | 16 ++++
> ld/testsuite/ld-xtensa/xtensa.exp | 2 +
> 17 files changed, 318 insertions(+), 4 deletions(-)
> create mode 100644 gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d
> create mode 100644 gas/testsuite/gas/xtensa/pdiff-uleb128.d
> create mode 100644 gas/testsuite/gas/xtensa/pdiff-uleb128.s
> create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128-2.d
> create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128-2.s
> create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128.d
> create mode 100644 ld/testsuite/ld-xtensa/relax-uleb128.s
>
> diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
> index 8d12ddf6723..84b21cb78b8 100644
> --- a/bfd/bfd-in2.h
> +++ b/bfd/bfd-in2.h
> @@ -6168,6 +6168,16 @@ enum bfd_reloc_code_real
> BFD_RELOC_XTENSA_NDIFF16,
> BFD_RELOC_XTENSA_NDIFF32,
>
> + /* Xtensa relocation to mark a positive difference of two local symbols
> + encoded as an unsigned LEB128 value. This works like
> + BFD_RELOC_XTENSA_PDIFF32, but the linker rewrites the difference in
> + place using the number of bytes the assembler originally emitted, so
> + that the size of the containing section never changes. Without it the
> + assembler would have to inhibit linker relaxation of all code covered
> + by such a difference, which DWARF 5 location and range lists produce
> + for virtually every function. */
> + BFD_RELOC_XTENSA_PDIFF_ULEB128,
> +
> /* 8 bit signed offset in (ix+d) or (iy+d). */
> BFD_RELOC_Z80_DISP8,
>
> diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c
> index b1733345fc8..2c279bcbe95 100644
> --- a/bfd/elf32-xtensa.c
> +++ b/bfd/elf32-xtensa.c
> @@ -346,6 +346,8 @@ static reloc_howto_type elf_howto_table[] =
> bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF16", false, 0, 0xffff, false),
> HOWTO (R_XTENSA_NDIFF32, 0, 4, 32, false, 0, complain_overflow_bitfield,
> bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF32", false, 0, 0xffffffff, false),
> + HOWTO (R_XTENSA_PDIFF_ULEB128, 0, 0, 0, false, 0, complain_overflow_dont,
> + bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF_ULEB128", false, 0, 0, false),
> };
>
> #if DEBUG_GEN_RELOC
> @@ -409,6 +411,10 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
> TRACE ("BFD_RELOC_XTENSA_NDIFF32");
> return &elf_howto_table[(unsigned) R_XTENSA_NDIFF32 ];
>
> + case BFD_RELOC_XTENSA_PDIFF_ULEB128:
> + TRACE ("BFD_RELOC_XTENSA_PDIFF_ULEB128");
> + return &elf_howto_table[(unsigned) R_XTENSA_PDIFF_ULEB128 ];
> +
> case BFD_RELOC_XTENSA_RTLD:
> TRACE ("BFD_RELOC_XTENSA_RTLD");
> return &elf_howto_table[(unsigned) R_XTENSA_RTLD ];
> @@ -1891,6 +1897,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
> case R_XTENSA_NDIFF8:
> case R_XTENSA_NDIFF16:
> case R_XTENSA_NDIFF32:
> + case R_XTENSA_PDIFF_ULEB128:
> case R_XTENSA_TLS_FUNC:
> case R_XTENSA_TLS_ARG:
> case R_XTENSA_TLS_CALL:
> @@ -9650,6 +9657,79 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
> r_reloc new_reloc;
> target_sec = translate_reloc (&r_rel, &new_reloc, target_sec);
>
> + if (r_type == R_XTENSA_PDIFF_ULEB128)
> + {
> + bfd_vma diff_value, new_end_offset;
> + unsigned int len, new_len = 0;
> + bfd_byte *p, *q, *endp;
> +
> + if (sec_size <= old_source_offset)
> + {
> + (*link_info->callbacks->reloc_dangerous)
> + (link_info, _("invalid relocation address"),
> + abfd, sec, old_source_offset);
> + goto error_return;
> + }
> +
> + /* The value is read back from the section contents, so
> + it must not be trusted to be a well formed uleb128
> + that ends before the end of the section. */
> + p = q = &contents[old_source_offset];
> + diff_value = _bfd_safe_read_leb128 (abfd, &q, false,
> + contents + sec_size);
> + len = q - p;
> + if (len == 0 || (q[-1] & 0x80) != 0)
> + {
> + (*link_info->callbacks->reloc_dangerous)
> + (link_info, _("invalid uleb128 difference"),
> + abfd, sec, old_source_offset);
> + goto error_return;
> + }
> +
> + new_end_offset = offset_with_removed_text_map
> + (&target_relax_info->action_list,
> + r_rel.target_offset + diff_value);
> + if (new_end_offset < new_reloc.target_offset)
> + {
> + (*link_info->callbacks->reloc_dangerous)
> + (link_info, _("uleb128 difference is negative after "
> + "relaxation"),
> + abfd, sec, old_source_offset);
> + goto error_return;
> + }
> + diff_value = new_end_offset - new_reloc.target_offset;
> +
> + /* Byte length of the minimal uleb128 encoding of
> + DIFF_VALUE. */
> + for (bfd_vma v = diff_value; new_len++, v >>= 7; )
> + ;
> + /* The assembler reserves one spare byte for these
> + differences, but reject growth that still needs more
> + bytes than were emitted. */
> + if (new_len > len)
> + {
> + (*link_info->callbacks->reloc_dangerous)
> + (link_info, _("uleb128 difference overflows after "
> + "relaxation"),
> + abfd, sec, old_source_offset);
> + goto error_return;
> + }
> +
> + /* Rewrite the difference in place, always using the
> + number of bytes the assembler emitted so that the size
> + of this section does not change. A value that now
> + needs fewer bytes is padded with uleb128 zeroes. */
> + endp = p + len - 1;
> + memset (p, 0x80, len - 1);
> + *endp = 0;
> + p = _bfd_write_unsigned_leb128 (p, endp, diff_value);
> + BFD_ASSERT (p);
> + if (--p < endp)
> + *p |= 0x80;
> +
> + pin_contents (sec, contents);
> + }
> +
> if (r_type == R_XTENSA_DIFF8
> || r_type == R_XTENSA_DIFF16
> || r_type == R_XTENSA_DIFF32
> diff --git a/bfd/libbfd.h b/bfd/libbfd.h
> index 6e737e468df..72e7d60e903 100644
> --- a/bfd/libbfd.h
> +++ b/bfd/libbfd.h
> @@ -2797,6 +2797,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
> "BFD_RELOC_XTENSA_NDIFF8",
> "BFD_RELOC_XTENSA_NDIFF16",
> "BFD_RELOC_XTENSA_NDIFF32",
> + "BFD_RELOC_XTENSA_PDIFF_ULEB128",
> "BFD_RELOC_Z80_DISP8",
> "BFD_RELOC_Z80_BYTE0",
> "BFD_RELOC_Z80_BYTE1",
> diff --git a/bfd/reloc.c b/bfd/reloc.c
> index 29288f8099d..0e2cb187e34 100644
> --- a/bfd/reloc.c
> +++ b/bfd/reloc.c
> @@ -6031,6 +6031,18 @@ ENUMDOC
> differences. The difference value is treated as unsigned with these
> relocation types, giving full 8/16 value ranges.
>
> +ENUM
> + BFD_RELOC_XTENSA_PDIFF_ULEB128
> +ENUMDOC
> + Xtensa relocation to mark a positive difference of two local symbols
> + encoded as an unsigned LEB128 value. This works like
> + BFD_RELOC_XTENSA_PDIFF32, but the linker rewrites the difference in
> + place using the number of bytes the assembler originally emitted, so
> + that the size of the containing section never changes. Without it the
> + assembler would have to inhibit linker relaxation of all code covered
> + by such a difference, which DWARF 5 location and range lists produce
> + for virtually every function.
> +
> ENUM
> BFD_RELOC_Z80_DISP8
> ENUMDOC
> diff --git a/binutils/readelf.c b/binutils/readelf.c
> index b5865d9717e..e82a90e605c 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -16853,7 +16853,8 @@ is_none_reloc (Filedata * filedata, unsigned int reloc_type)
> || reloc_type == 59 /* R_XTENSA_PDIFF32. */
> || reloc_type == 60 /* R_XTENSA_NDIFF8. */
> || reloc_type == 61 /* R_XTENSA_NDIFF16. */
> - || reloc_type == 62 /* R_XTENSA_NDIFF32. */);
> + || reloc_type == 62 /* R_XTENSA_NDIFF32. */
> + || reloc_type == 63 /* R_XTENSA_PDIFF_ULEB128. */);
> }
> return false;
> }
> diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
> index 397744f742b..631b61c5128 100644
> --- a/gas/config/tc-xtensa.c
> +++ b/gas/config/tc-xtensa.c
> @@ -6104,6 +6104,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
> break;
>
> case BFD_RELOC_XTENSA_ASM_EXPAND:
> + case BFD_RELOC_XTENSA_PDIFF_ULEB128:
> case BFD_RELOC_XTENSA_TLS_FUNC:
> case BFD_RELOC_XTENSA_TLS_ARG:
> case BFD_RELOC_XTENSA_TLS_CALL:
> @@ -7361,6 +7362,7 @@ static void xtensa_fix_target_frags (void);
> static void xtensa_mark_narrow_branches (void);
> static void xtensa_mark_zcl_first_insns (void);
> static void xtensa_mark_difference_of_two_symbols (void);
> +static void xtensa_insert_uleb128_fixes (bfd *, asection *, void *);
> static void xtensa_fix_a0_b_retw_frags (void);
> static void xtensa_fix_b_j_loop_end_frags (void);
> static void xtensa_fix_close_loop_end_frags (void);
> @@ -7401,6 +7403,9 @@ xtensa_md_finish (void)
> xtensa_add_config_info ();
>
> xtensa_check_frag_count ();
> +
> + if (linkrelax)
> + bfd_map_over_sections (stdoutput, xtensa_insert_uleb128_fixes, NULL);
> }
>
> struct trampoline_chain_entry
> @@ -8234,11 +8239,87 @@ xtensa_mark_zcl_first_insns (void)
> }
> }
>
> +/* A difference of two symbols encoded as a uleb128 value can be handed to
> + the linker as a BFD_RELOC_XTENSA_PDIFF_ULEB128 relocation: the difference
> + itself stays in the section contents and the relocation records the
> + address of the subtracted symbol, which is all the linker needs to
> + rewrite the value after relaxation. DWARF 5 location and range lists
> + are made almost entirely of such differences, so without this the code
> + covered by them could never be relaxed.
> +
> + This has to run before relaxation turns the rs_leb128 frags into plain
> + data. Differences that cannot be represented this way are left to
> + xtensa_mark_difference_of_two_symbols.
> +
> + Mark the expression symbol so xtensa_leb128_frag_size can reserve one
> + spare byte: Xtensa linker relaxation can grow code, which may push a
> + difference across a uleb128 byte boundary. */
> +
> +static void
> +xtensa_insert_uleb128_fixes (bfd *abfd ATTRIBUTE_UNUSED,
> + asection *sec,
> + void *unused ATTRIBUTE_UNUSED)
> +{
> + segment_info_type *seginfo = seg_info (sec);
> + fragS *fragP;
> +
> + if (seginfo == NULL || seginfo->frchainP == NULL)
> + return;
> +
> + subseg_set (sec, 0);
> +
> + for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
> + {
> + expressionS *exp;
> + symbolS *op_sym;
> +
> + /* Non-zero fr_subtype is sleb128 (no relocation). */
> + if (fragP->fr_type != rs_leb128 || fragP->fr_subtype != 0
> + || fragP->fr_symbol == NULL)
> + continue;
> +
> + exp = symbol_get_value_expression (fragP->fr_symbol);
> + if (exp->X_op != O_subtract)
> + continue;
> +
> + op_sym = exp->X_op_symbol;
> +
> + /* R_XTENSA_PDIFF_ULEB128 only encodes same-section differences. */
> + if (S_GET_SEGMENT (exp->X_add_symbol) != S_GET_SEGMENT (op_sym))
> + continue;
> +
> + /* Only code sections are relaxed. */
> + if (!(bfd_section_flags (S_GET_SEGMENT (op_sym)) & SEC_CODE))
> + continue;
> +
> + fix_new (fragP, fragP->fr_fix, 0, op_sym, 0, 0,
> + BFD_RELOC_XTENSA_PDIFF_ULEB128);
> + symbol_get_tc (fragP->fr_symbol)->has_leb128_diff_reloc = 1;
> + }
> +}
> +
> +/* For uleb128 diffs with R_XTENSA_PDIFF_ULEB128, reserve one spare byte so
> + linker relaxation can grow the value across one 7-bit boundary without
> + overflowing. Growth that needs more than one extra byte still errors in
> + the linker. frag_var already allocated sizeof_leb128 (~0, 0) bytes;
> + clamp if the value already needs that many. */
> +
> +offsetT
> +xtensa_leb128_frag_size (const fragS *fragP, offsetT size)
> +{
> + if (fragP->fr_symbol != NULL
> + && symbol_get_tc (fragP->fr_symbol)->has_leb128_diff_reloc
> + && size < (offsetT) sizeof_leb128 (~(valueT) 0, 0))
> + size++;
> + return size;
> +}
> +
>
> /* When a difference-of-symbols expression is encoded as a uleb128 or
> - sleb128 value, the linker is unable to adjust that value to account for
> - link-time relaxation. Mark all the code between such symbols so that
> - its size cannot be changed by linker relaxation. */
> + sleb128 value and no relocation can express it, the linker is unable to
> + adjust that value to account for link-time relaxation. Mark all the code
> + between such symbols so that its size cannot be changed by linker
> + relaxation. */
>
> static void
> xtensa_mark_difference_of_two_symbols (void)
> @@ -8250,6 +8331,9 @@ xtensa_mark_difference_of_two_symbols (void)
> {
> expressionS *exp = symbol_get_value_expression (expr_sym);
>
> + if (symbol_get_tc (expr_sym)->has_leb128_diff_reloc)
> + continue;
> +
> if (exp->X_op == O_subtract)
> {
> symbolS *left = exp->X_add_symbol;
> diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
> index 64d735144d7..afd7478dd8d 100644
> --- a/gas/config/tc-xtensa.h
> +++ b/gas/config/tc-xtensa.h
> @@ -297,6 +297,9 @@ typedef struct xtensa_symfield_type
> {
> unsigned int is_loop_target : 1;
> unsigned int is_branch_target : 1;
> + /* Set when a uleb128 difference got R_XTENSA_PDIFF_ULEB128, so code
> + between the symbols may still be relaxed. */
> + unsigned int has_leb128_diff_reloc : 1;
> symbolS *next_expr_symbol;
> } xtensa_symfield_type;
>
> @@ -341,6 +344,7 @@ extern int xtensa_unrecognized_line (int);
> extern bool xtensa_check_inside_bundle (void);
> extern void xtensa_handle_align (fragS *);
> extern char *xtensa_section_rename (const char *);
> +extern offsetT xtensa_leb128_frag_size (const fragS *, offsetT);
>
> /* We need to set the target endianness in xtensa_init and not in md_begin.
> This is because xtensa_target_format is called before md_begin, and we
> @@ -374,6 +378,7 @@ extern void xtensa_init (int, char **);
> #define md_elf_section_change_hook xtensa_elf_section_change_hook
> #define md_finish xtensa_md_finish
> #define md_flush_pending_output() xtensa_flush_pending_output ()
> +#define md_leb128_frag_size(frag, size) xtensa_leb128_frag_size (frag, size)
> #define md_operand(x)
> #define TEXT_SECTION_NAME xtensa_section_rename (".text")
> #define DATA_SECTION_NAME xtensa_section_rename (".data")
> diff --git a/gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d b/gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d
> new file mode 100644
> index 00000000000..dcd4641083f
> --- /dev/null
> +++ b/gas/testsuite/gas/xtensa/pdiff-uleb128-norelax.d
> @@ -0,0 +1,20 @@
> +#as: --no-link-relax
> +#source: pdiff-uleb128.s
> +#objdump: -r -s -j .debug_info -j .xt.prop
> +#name: uleb128 difference without link-relax
> +
> +# Without link-relax, no R_XTENSA_PDIFF_ULEB128 is emitted. The assembler
> +# falls back to marking the covered code no_transform via .xt.prop, and the
> +# uleb128 uses the minimal encoding (no spare byte).
> +
> +.*: +file format .*xtensa.*
> +
> +RELOCATION RECORDS FOR \[.xt.prop\]:
> +OFFSET +TYPE +VALUE
> +0+ R_XTENSA_32 +.text.*
> +
> +Contents of section .debug_info:
> + 0000 c801.*
> +#...
> +Contents of section .xt.prop:
> + 0000 .*
> diff --git a/gas/testsuite/gas/xtensa/pdiff-uleb128.d b/gas/testsuite/gas/xtensa/pdiff-uleb128.d
> new file mode 100644
> index 00000000000..5c38f56ca66
> --- /dev/null
> +++ b/gas/testsuite/gas/xtensa/pdiff-uleb128.d
> @@ -0,0 +1,12 @@
> +#objdump: -r -s -j .debug_info
> +#name: uleb128 difference reloc
> +
> +.*: +file format .*xtensa.*
> +
> +RELOCATION RECORDS FOR \[.debug_info\]:
> +OFFSET +TYPE +VALUE
> +0+ R_XTENSA_PDIFF_ULEB128 +.text.*
> +
> +# Difference is 200 (0xc8 0x01) plus one spare byte reserved for growth.
> +Contents of section .debug_info:
> + 0000 c88100.*
> diff --git a/gas/testsuite/gas/xtensa/pdiff-uleb128.s b/gas/testsuite/gas/xtensa/pdiff-uleb128.s
> new file mode 100644
> index 00000000000..0b431a3ac26
> --- /dev/null
> +++ b/gas/testsuite/gas/xtensa/pdiff-uleb128.s
> @@ -0,0 +1,7 @@
> + .text
> +.L1:
> + .space 200
> +.L2:
> +
> + .section .debug_info, "", @progbits
> + .uleb128 .L2 - .L1
> diff --git a/gas/write.c b/gas/write.c
> index cd02d8c7fb8..467b51be7f8 100644
> --- a/gas/write.c
> +++ b/gas/write.c
> @@ -461,6 +461,23 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
> size = output_leb128 (fragP->fr_literal + fragP->fr_fix, value,
> fragP->fr_subtype);
>
> + /* Relaxation may have reserved more room than the shortest encoding
> + needs (see md_leb128_frag_size). Keep the reserved size by adding
> + redundant high-order continuation bytes; inert unless a target
> + hook grew fr_offset above the minimal encoding. */
> + if ((offsetT) size < fragP->fr_offset)
> + {
> + char *p = fragP->fr_literal + fragP->fr_fix;
> + char pad = fragP->fr_subtype && (offsetT) value < 0 ? 0x7f : 0;
> +
> + do
> + {
> + p[size - 1] |= 0x80;
> + p[size++] = pad;
> + }
> + while ((offsetT) size < fragP->fr_offset);
> + }
> +
> fragP->fr_fix += size;
> fragP->fr_type = rs_fill;
> fragP->fr_var = 0;
> @@ -3124,6 +3141,9 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
>
> value = resolve_symbol_value (fragP->fr_symbol);
> size = sizeof_leb128 (value, fragP->fr_subtype);
> +#ifdef md_leb128_frag_size
> + size = md_leb128_frag_size (fragP, size);
> +#endif
> growth = size - fragP->fr_offset;
> fragP->fr_offset = size;
> }
> diff --git a/include/elf/xtensa.h b/include/elf/xtensa.h
> index 1a9bdf95cf7..032b94ef94b 100644
> --- a/include/elf/xtensa.h
> +++ b/include/elf/xtensa.h
> @@ -93,6 +93,9 @@ START_RELOC_NUMBERS (elf_xtensa_reloc_type)
> RELOC_NUMBER (R_XTENSA_NDIFF8, 60)
> RELOC_NUMBER (R_XTENSA_NDIFF16, 61)
> RELOC_NUMBER (R_XTENSA_NDIFF32, 62)
> + /* Positive same-section difference encoded as uleb128; rewritten
> + in place after relaxation without changing the section size. */
> + RELOC_NUMBER (R_XTENSA_PDIFF_ULEB128, 63)
> END_RELOC_NUMBERS (R_XTENSA_max)
>
> /* Processor-specific flags for the ELF header e_flags field. */
> diff --git a/ld/testsuite/ld-xtensa/relax-uleb128-2.d b/ld/testsuite/ld-xtensa/relax-uleb128-2.d
> new file mode 100644
> index 00000000000..c0f1c9ca378
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/relax-uleb128-2.d
> @@ -0,0 +1,12 @@
> +#source: relax-uleb128-2.s
> +#source: lcall2.s
> +#ld: -T lcall.t
> +#objdump: -s -j .debug_info
> +#name: uleb128 difference shrinking below 128
> +
> +# The difference is 156 before relaxation and 108 after it, so the value no
> +# longer needs two bytes. The assembler reserved one spare byte (three
> +# total), and the linker must keep that fixed width with uleb128 padding.
> +#...
> + 0000 ec8000.*
> +#...
> diff --git a/ld/testsuite/ld-xtensa/relax-uleb128-2.s b/ld/testsuite/ld-xtensa/relax-uleb128-2.s
> new file mode 100644
> index 00000000000..c587ed8bb06
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/relax-uleb128-2.s
> @@ -0,0 +1,18 @@
> + .globl _start
> + .globl foo
> + .text
> + .align 4
> +_start:
> + .literal .Lunused, 0xffffffff
> + entry a5, 16
> +.L1:
> + .space 60
> + .begin longcalls
> + .rept 16
> + call4 foo
> + .endr
> + .end longcalls
> +.L2:
> +
> + .section .debug_info, "", @progbits
> + .uleb128 .L2 - .L1
> diff --git a/ld/testsuite/ld-xtensa/relax-uleb128.d b/ld/testsuite/ld-xtensa/relax-uleb128.d
> new file mode 100644
> index 00000000000..08b017984d8
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/relax-uleb128.d
> @@ -0,0 +1,11 @@
> +#source: relax-uleb128.s
> +#source: lcall2.s
> +#ld: -T lcall.t
> +#objdump: -s -j .debug_info
> +#name: uleb128 difference after longcall relax
> +
> +# Longcall relaxes from 6 bytes to 3; with the trailing nop the difference
> +# is 5. Assembler reserved one spare byte (two total).
> +#...
> + 0000 8500.*
> +#...
> diff --git a/ld/testsuite/ld-xtensa/relax-uleb128.s b/ld/testsuite/ld-xtensa/relax-uleb128.s
> new file mode 100644
> index 00000000000..8e1c0058976
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/relax-uleb128.s
> @@ -0,0 +1,16 @@
> + .globl _start
> + .globl foo
> + .text
> + .align 4
> +_start:
> + .literal .Lunused, 0xffffffff
> + entry a5, 16
> +.L1:
> + .begin longcalls
> + call4 foo
> + .end longcalls
> + nop
> +.L2:
> +
> + .section .debug_info, "", @progbits
> + .uleb128 .L2 - .L1
> diff --git a/ld/testsuite/ld-xtensa/xtensa.exp b/ld/testsuite/ld-xtensa/xtensa.exp
> index 7173c6b21b2..66a6b7a60da 100644
> --- a/ld/testsuite/ld-xtensa/xtensa.exp
> +++ b/ld/testsuite/ld-xtensa/xtensa.exp
> @@ -30,6 +30,8 @@ run_dump_test "lcall"
> run_dump_test "relax-diff1"
> run_dump_test "relax-loc"
> run_dump_test "relax-ndiff"
> +run_dump_test "relax-uleb128"
> +run_dump_test "relax-uleb128-2"
>
> run_dump_test "relax-static-pie"
> run_dump_test "relax-static-local-pie"
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-xtensa-add-R_XTENSA_PDIFF_ULEB128-for-DWARF-uleb128-.patch
Type: text/x-patch
Size: 23128 bytes
Desc: 0001-xtensa-add-R_XTENSA_PDIFF_ULEB128-for-DWARF-uleb128-.patch
URL: <https://sourceware.org/pipermail/binutils/attachments/20260810/4bc1602e/attachment-0001.bin>
More information about the Binutils
mailing list