diff options
author | Nelson Chu <nelson@rivosinc.com> | 2025-08-14 12:01:38 +0800 |
---|---|---|
committer | Nelson Chu <nelson@rivosinc.com> | 2025-09-04 16:32:18 +0800 |
commit | cea397848b868c62993864dbef684877bceef1fc (patch) | |
tree | 8861ccc9123ec84c6eeb93de892d859506f5f3e9 | |
parent | 8b91a77eda91d4ce5a582647795032dec6899e4e (diff) | |
download | binutils-cea397848b868c62993864dbef684877bceef1fc.zip binutils-cea397848b868c62993864dbef684877bceef1fc.tar.gz binutils-cea397848b868c62993864dbef684877bceef1fc.tar.bz2 |
RISC-V: Fixed missed GOT relocation against a symbol that has a defined value
SImilar to aarch64, commit eac4eb8ecb26
There are two problems when GOT relocation against a symbol that has a defined
value,
1. Pesudo la with pic and pseudo lga lost the relocations.
2. %got_pcrel_hi generates R_RISCV_GOT_HI20 with addend, which is wrong since
commit 50331d64f108.
The solution is to use deferred_expression for GOT relocation. Maybe other
relocations also have same problem and need the deferred_expression, but we can
add in the future patches.
-rw-r--r-- | gas/config/tc-riscv.c | 85 | ||||
-rw-r--r-- | gas/testsuite/gas/riscv/force_reloc.d | 47 | ||||
-rw-r--r-- | gas/testsuite/gas/riscv/force_reloc.s | 22 |
3 files changed, 117 insertions, 37 deletions
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index 8a33568..df60c20 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -2518,13 +2518,16 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc, } static void -my_getExpression (expressionS *ep, char *str) +my_getExpression (expressionS *ep, char *str, bool defer) { char *save_in; save_in = input_line_pointer; input_line_pointer = str; - expression (ep); + if (defer) + deferred_expression (ep); + else + expression (ep); expr_parse_end = input_line_pointer; input_line_pointer = save_in; } @@ -2543,6 +2546,7 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, size_t reloc_index; unsigned crux_depth, str_depth; bool orig_probing = probing_insn_operands; + bool force_reloc = false; char *crux; /* Search for the start of the main expression. @@ -2582,7 +2586,10 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, if (str_depth || reloc_index) probing_insn_operands = false; - my_getExpression (ep, crux); + if (*reloc == BFD_RELOC_RISCV_GOT_HI20) + force_reloc = true; + + my_getExpression (ep, crux, force_reloc); str = expr_parse_end; probing_insn_operands = orig_probing; @@ -2674,7 +2681,7 @@ my_getVsetvliExpression (expressionS *ep, char *str) } else { - my_getExpression (ep, str); + my_getExpression (ep, str, false/* defer */); str = expr_parse_end; } } @@ -2727,7 +2734,7 @@ my_getThVsetvliExpression (expressionS *ep, char *str) } else { - my_getExpression (ep, str); + my_getExpression (ep, str, false/* defer */); str = expr_parse_end; } } @@ -2851,6 +2858,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, error.missing_ext = NULL; /* Indicate we are assembling instruction with CSR. */ bool insn_with_csr = false; + bool force_reloc = false; /* Parse the name of the instruction. Terminate the string if whitespace is found so that str_hash_find only sees the name part of the string. */ @@ -3353,7 +3361,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'i': /* vector arith signed immediate */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if (imm_expr->X_add_number > 15 || imm_expr->X_add_number < -16) @@ -3365,7 +3373,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'j': /* vector arith unsigned immediate */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if (imm_expr->X_add_number < 0 || imm_expr->X_add_number >= 32) @@ -3377,7 +3385,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'k': /* vector arith signed immediate, minus 1 */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if (imm_expr->X_add_number > 16 || imm_expr->X_add_number < -15) @@ -3389,7 +3397,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'l': /* 6-bit vector arith unsigned immediate */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if (imm_expr->X_add_number < 0 || imm_expr->X_add_number >= 64) @@ -3453,7 +3461,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, break; case '<': /* Shift amount, 0 - 31. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, false); if ((unsigned long) imm_expr->X_add_number > 31) as_bad (_("improper shift amount (%"PRIu64")"), @@ -3464,7 +3472,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case '>': /* Shift amount, 0 - (XLEN-1). */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, false); if ((unsigned long) imm_expr->X_add_number >= xlen) as_bad (_("improper shift amount (%"PRIu64")"), @@ -3475,7 +3483,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'Z': /* CSRRxI immediate. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, false); if ((unsigned long) imm_expr->X_add_number > 31) as_bad (_("improper CSRxI immediate (%"PRIu64")"), @@ -3492,7 +3500,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, INSERT_OPERAND (CSR, *ip, regno); else { - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, true); if ((unsigned long) imm_expr->X_add_number > 0xfff) as_bad (_("improper CSR address (%"PRIu64")"), @@ -3591,7 +3599,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, break; case 'I': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); if (imm_expr->X_op != O_big && imm_expr->X_op != O_constant) break; @@ -3600,7 +3608,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'A': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); normalize_constant_expr (imm_expr); /* The 'A' format specifier must be a symbol. */ if (imm_expr->X_op != O_symbol) @@ -3610,7 +3618,10 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'B': - my_getExpression (imm_expr, asarg); + if (ip->insn_mo->mask == M_LGA + || (riscv_opts.pic && ip->insn_mo->mask == M_LA)) + force_reloc = true; + my_getExpression (imm_expr, asarg, force_reloc); normalize_constant_expr (imm_expr); /* The 'B' format specifier must be a symbol or a constant. */ if (imm_expr->X_op != O_symbol && imm_expr->X_op != O_constant) @@ -3662,7 +3673,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, case 'p': /* PC-relative offset. */ branch: *imm_reloc = BFD_RELOC_12_PCREL; - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); asarg = expr_parse_end; continue; @@ -3685,13 +3696,13 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, case 'a': /* 20-bit PC-relative offset. */ jump: - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); asarg = expr_parse_end; *imm_reloc = BFD_RELOC_RISCV_JMP; continue; case 'c': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); asarg = expr_parse_end; if (strcmp (asarg, "@plt") == 0) asarg += 4; @@ -3792,7 +3803,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, break; case 'y': /* bs immediate */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number > 3) as_bad(_("Improper bs immediate (%lu)"), @@ -3803,7 +3814,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case 'Y': /* rnum immediate */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number > 10) as_bad(_("Improper rnum immediate (%lu)"), @@ -3833,7 +3844,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, pseudo S-type but lower 5-bits zero. */ if (riscv_handle_implicit_zero_offset (imm_expr, asarg)) continue; - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, false); if (((unsigned) (imm_expr->X_add_number) & 0x1fU) || imm_expr->X_add_number >= RISCV_IMM_REACH / 2 @@ -4025,7 +4036,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, s = strtol (oparg + 1, (char **)&oparg, 10); oparg--; - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, false); if (!sign) { @@ -4053,7 +4064,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, switch (*++oparg) { case '2': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; if (imm_expr->X_add_number<0 @@ -4063,7 +4074,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, |= ENCODE_CV_IS2_UIMM5 (imm_expr->X_add_number); continue; case '3': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; if (imm_expr->X_add_number < 0 @@ -4073,7 +4084,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, |= ENCODE_CV_IS3_UIMM5 (imm_expr->X_add_number); continue; case '4': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; if (imm_expr->X_add_number < -16 @@ -4083,7 +4094,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, |= ENCODE_CV_IS2_UIMM5 (imm_expr->X_add_number); continue; case '5': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; if (imm_expr->X_add_number < -32 @@ -4093,7 +4104,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, |= ENCODE_CV_SIMD_IMM6 (imm_expr->X_add_number); continue; case '6': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; if (imm_expr->X_add_number < 0 @@ -4103,7 +4114,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, |= ENCODE_CV_BITMANIP_UIMM5 (imm_expr->X_add_number); continue; case '7': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; if (imm_expr->X_add_number < 0 @@ -4113,7 +4124,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, |= ENCODE_CV_BITMANIP_UIMM2 (imm_expr->X_add_number); continue; case '8': - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); asarg = expr_parse_end; ++oparg; @@ -4194,7 +4205,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, switch (*++oparg) { case '@': /* hint 0 - 31. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number > 31) as_bad(_("Improper hint amount (%lu)"), @@ -4205,7 +4216,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case '#': /* immediate 0 - 511. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number > 511) as_bad(_("Improper immediate amount (%lu)"), @@ -4216,7 +4227,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case '$': /* LDP offset 0 to (1<<7)-8. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number >= (1 << 7) || ((unsigned long)imm_expr->X_add_number & 0x7) != 0) @@ -4229,7 +4240,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case '%': /* LWP offset 0 to (1<<7)-4. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number >= (1 << 7) || ((unsigned long)imm_expr->X_add_number & 0x3) != 0) @@ -4242,7 +4253,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case '^': /* SDP offset 0 to (1<<7)-8. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number >= (1 << 7) || ((unsigned long)imm_expr->X_add_number & 0x7) != 0) @@ -4257,7 +4268,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, continue; case '&': /* SWP offset 0 to (1<<7)-4. */ - my_getExpression (imm_expr, asarg); + my_getExpression (imm_expr, asarg, force_reloc); check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long)imm_expr->X_add_number >= (1 << 7) || ((unsigned long)imm_expr->X_add_number & 0x3) != 0) @@ -4613,7 +4624,7 @@ bool riscv_parse_name (const char *name, struct expressionS *ep, if (!probing_insn_operands) return false; - gas_assert (mode == expr_normal); + gas_assert (mode == expr_normal || expr_defer_p (mode)); regno = reg_lookup_internal (name, RCLASS_GPR); if (regno == -1u) diff --git a/gas/testsuite/gas/riscv/force_reloc.d b/gas/testsuite/gas/riscv/force_reloc.d new file mode 100644 index 0000000..df37a60 --- /dev/null +++ b/gas/testsuite/gas/riscv/force_reloc.d @@ -0,0 +1,47 @@ +#as: +#objdump: -dr -Mnumeric,no-aliases + +.*:[ ]+file format .* + + +Disassembly of section .text: + +0+ <_start>: +[ ]+0:[ ]+[0-9a-f]+[ ]+auipc[ ]+x1,0x0 +[ ]+0:[ ]+R_RISCV_GOT_HI20[ ]+sym_abs_before +[ ]+0:[ ]+R_RISCV_RELAX.* +[ ]+4:[ ]+[0-9a-f]+[ ]+(lw|ld)[ ]+x1,0\(x1\) # 0 .* +[ ]+4:[ ]+R_RISCV_PCREL_LO12_I[ ]+.L0.* +[ ]+4:[ ]+R_RISCV_RELAX.* +[ ]+8:[ ]+[0-9a-f]+[ ]+auipc[ ]+x2,0x0 +[ ]+8:[ ]+R_RISCV_GOT_HI20[ ]+sym_abs_before +[ ]+8:[ ]+R_RISCV_RELAX.* +[ ]+c:[ ]+[0-9a-f]+[ ]+(lw|ld)[ ]+x2,0\(x2\) # 8 .* +[ ]+c:[ ]+R_RISCV_PCREL_LO12_I[ ]+.L0.* +[ ]+c:[ ]+R_RISCV_RELAX.* + +0+10 <.L1>: +[ ]+10:[ ]+[0-9a-f]+[ ]+auipc[ ]+x3,0x0 +[ ]+10:[ ]+R_RISCV_GOT_HI20[ ]+sym_abs_before +[ ]+14:[ ]+[0-9a-f]+[ ]+addi[ ]+x3,x3,0 # 10 .* +[ ]+14:[ ]+R_RISCV_PCREL_LO12_I[ ]+.L1.* +[ ]+14:[ ]+R_RISCV_RELAX.* +[ ]+18:[ ]+[0-9a-f]+[ ]+auipc[ ]+x4,0x0 +[ ]+18:[ ]+R_RISCV_GOT_HI20[ ]+sym_abs_after +[ ]+18:[ ]+R_RISCV_RELAX.* +[ ]+1c:[ ]+[0-9a-f]+[ ]+(lw|ld)[ ]+x4,0\(x4\) # 18 .* +[ ]+1c:[ ]+R_RISCV_PCREL_LO12_I[ ]+.L0.* +[ ]+1c:[ ]+R_RISCV_RELAX.* +[ ]+20:[ ]+[0-9a-f]+[ ]+auipc[ ]+x5,0x0 +[ ]+20:[ ]+R_RISCV_GOT_HI20[ ]+sym_abs_after +[ ]+20:[ ]+R_RISCV_RELAX.* +[ ]+24:[ ]+[0-9a-f]+[ ]+(lw|ld)[ ]+x5,0\(x5\) # 20 .* +[ ]+24:[ ]+R_RISCV_PCREL_LO12_I[ ]+.L0.* +[ ]+24:[ ]+R_RISCV_RELAX.* + +0+28 <.L2>: +[ ]+28:[ ]+[0-9a-f]+[ ]+auipc[ ]+x6,0x0 +[ ]+28:[ ]+R_RISCV_GOT_HI20[ ]+sym_abs_after +[ ]+2c:[ ]+[0-9a-f]+[ ]+addi[ ]+x6,x6,0 # 28 .* +[ ]+2c:[ ]+R_RISCV_PCREL_LO12_I[ ]+.L2.* +[ ]+2c:[ ]+R_RISCV_RELAX.* diff --git a/gas/testsuite/gas/riscv/force_reloc.s b/gas/testsuite/gas/riscv/force_reloc.s new file mode 100644 index 0000000..787c419 --- /dev/null +++ b/gas/testsuite/gas/riscv/force_reloc.s @@ -0,0 +1,22 @@ +.option pic +.option norvc + +.global sym_abs_before +.set sym_abs_before, 42 + +.text +.global _start +_start: +# Need defer expression +la x1, sym_abs_before +lga x2, sym_abs_before +.L1: auipc x3, %got_pcrel_hi (sym_abs_before) + addi x3, x3, %pcrel_lo (.L1) +# This is fine +la x4, sym_abs_after +lga x5, sym_abs_after +.L2: auipc x6, %got_pcrel_hi (sym_abs_after) + addi x6, x6, %pcrel_lo (.L2) + +.global sym_abs_after +.set sym_abs_after, 42 |