[PATCH] RISC-V: Clarify the behaviors of SET/ADD/SUB relocations.
Charlie Jenkins
charlie@rivosinc.com
Fri Oct 20 17:58:55 GMT 2023
On Thu, Oct 19, 2023 at 05:17:26PM +0800, Nelson Chu wrote:
> We are used to generate these kinds of relocations by data directives.
> Considering the following example,
> .word (A + 3) - (B + 2)
> The GAS will generate a pair of ADD/SUB for this,
> R_RISCV_ADD, A + 1
> R_RISCV_SUB, 0
>
> The addend of R_RISCV_SUB will always be zero, and the summary of the
> constants will be stored in the addend of R_RISCV_ADD/SET. Therefore,
> we can always add the addend of these data relocations when doing relocations.
>
> But unfortunately, I had heard that if we are using .reloc to generate
> the data relocations will make the relocations failed. Refer to this,
> .reloc offset, R_RISCV_ADD32, A + 3
> .reloc offset, R_RISCV_SUB32, B + 2
> .word 0
> Then we can get the relocations as follows,
> R_RISCV_ADD, A + 3
> R_RISCV_SUB, B + 2
> Then... Current LD does the relocation, B - A + 3 + 2, which is wrong
> obviously...
>
> So first of all, this patch fixes the wrong relocation behavior of
> R_RISCV_SUB* relocations.
>
> Afterwards, considering the uleb128 direcitve, we will get a pair of
> SET_ULEB128/SUB_ULEB128 relocations for it for now,
> .uleb128 (A + 3) - (B + 2)
> R_RISCV_SET_ULEB128, A + 1
> R_RISCV_SUB_ULEB128, B + 1
>
> Which looks also wrong obviously, the summary of the constants should only
> be stored into the addend of SET_ULEB128, and the addend of SUB_ULEB128 should
> be zero like other SUB relocations. But the current LD will still get the right
> relocation values since we only add the addend of SUB_ULEB128 by accident...
> Anyway, this patch also fixes the behaviors above, to make sure that no matter
> using .uleb128 or .reloc directives, we should always get the right values.
>
> bfd/
> * elfnn-riscv.c (perform_relocation): Clarify that SUB relocations
> should substract the addend, rather than add.
> (riscv_elf_relocate_section): Since SET_ULEB128 won't go into
> perform_relocation, we should add it's addend here in advance.
> gas/
> * config/tc-riscv.c (riscv_insert_uleb128_fixes): Set the addend of
> SUB_ULEB128 to zero since it should already be added into the addend
> of SET_ULEB128.
> ---
> bfd/elfnn-riscv.c | 22 ++++++++++++++++------
> gas/config/tc-riscv.c | 1 +
> 2 files changed, 17 insertions(+), 6 deletions(-)
>
> diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
> index 3edf68e3e30..00e5c69dbec 100644
> --- a/bfd/elfnn-riscv.c
> +++ b/bfd/elfnn-riscv.c
> @@ -1737,7 +1737,20 @@ perform_relocation (const reloc_howto_type *howto,
> {
> if (howto->pc_relative)
> value -= sec_addr (input_section) + rel->r_offset;
> - value += rel->r_addend;
> +
> + switch (ELFNN_R_TYPE (rel->r_info))
> + {
> + case R_RISCV_SUB6:
> + case R_RISCV_SUB8:
> + case R_RISCV_SUB16:
> + case R_RISCV_SUB32:
> + case R_RISCV_SUB64:
> + case R_RISCV_SUB_ULEB128:
> + value -= rel->r_addend;
> + break;
> + default:
> + value += rel->r_addend;
> + }
>
> switch (ELFNN_R_TYPE (rel->r_info))
> {
> @@ -1818,10 +1831,7 @@ perform_relocation (const reloc_howto_type *howto,
> value = ENCODE_CITYPE_LUI_IMM (RISCV_CONST_HIGH_PART (value));
> break;
>
> - /* SUB_ULEB128 must be applied after SET_ULEB128, so we only write the
> - value back for SUB_ULEB128 should be enough. */
> - case R_RISCV_SET_ULEB128:
> - break;
> + /* R_RISCV_SET_ULEB128 won't go into here. */
> case R_RISCV_SUB_ULEB128:
> {
> unsigned int len = 0;
> @@ -2523,7 +2533,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
> if (uleb128_set_rel != NULL
> && uleb128_set_rel->r_offset == rel->r_offset)
> {
> - relocation = uleb128_set_vma - relocation;
> + relocation = uleb128_set_vma - relocation + uleb128_set_rel->r_addend;
> uleb128_set_vma = 0;
> uleb128_set_rel = NULL;
> }
> diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
> index 5759d3a5fc4..09f2ea17c17 100644
> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -4999,6 +4999,7 @@ riscv_insert_uleb128_fixes (bfd *abfd ATTRIBUTE_UNUSED,
> fix_new_exp (fragP, fragP->fr_fix, 0,
> exp_dup, 0, BFD_RELOC_RISCV_SET_ULEB128);
> exp_dup->X_add_symbol = exp->X_op_symbol;
> + exp_dup->X_add_number = 0; /* Set addend of SUB_ULEB128 to zero. */
> fix_new_exp (fragP, fragP->fr_fix, 0,
> exp_dup, 0, BFD_RELOC_RISCV_SUB_ULEB128);
> free ((void *) exp_dup);
> --
> 2.39.3 (Apple Git-145)
>
I am not familiar enough with the codebase to properly review, but this
appears to fix the problem that I was having.
Acked-by: Charlie Jenkins <charlie@rivosinc.com>
More information about the Binutils
mailing list