aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-riscv.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config/tc-riscv.c')
-rw-r--r--gas/config/tc-riscv.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index d3b6543..10f1ac7 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1587,6 +1587,55 @@ init_opcode_hash (const struct riscv_opcode *opcodes,
return hash;
}
+/* Record all PC-relative high-part relocation that we have encountered to
+ help us resolve the corresponding low-part relocation later. */
+typedef struct
+{
+ bfd_vma address;
+ symbolS *symbol;
+ bfd_vma target;
+} riscv_pcrel_hi_fixup;
+
+/* Handle of the pcrel_hi hash table. */
+static htab_t riscv_pcrel_hi_fixup_hash;
+
+/* Get the key of a entry from the pcrel_hi hash table. */
+
+static hashval_t
+riscv_pcrel_fixup_hash (const void *entry)
+{
+ const riscv_pcrel_hi_fixup *e = entry;
+ return (hashval_t) (e->address);
+}
+
+/* Compare the keys between two entries fo the pcrel_hi hash table. */
+
+static int
+riscv_pcrel_fixup_eq (const void *entry1, const void *entry2)
+{
+ const riscv_pcrel_hi_fixup *e1 = entry1, *e2 = entry2;
+ return e1->address == e2->address;
+}
+
+/* Record the pcrel_hi relocation. */
+
+static bool
+riscv_record_pcrel_fixup (htab_t p, bfd_vma address, symbolS *symbol,
+ bfd_vma target)
+{
+ riscv_pcrel_hi_fixup entry = {address, symbol, target};
+ riscv_pcrel_hi_fixup **slot =
+ (riscv_pcrel_hi_fixup **) htab_find_slot (p, &entry, INSERT);
+ if (slot == NULL)
+ return false;
+
+ *slot = (riscv_pcrel_hi_fixup *) xmalloc (sizeof (riscv_pcrel_hi_fixup));
+ if (*slot == NULL)
+ return false;
+ **slot = entry;
+ return true;
+}
+
/* This function is called once, at assembler startup time. It should set up
all the tables, etc. that the MD part of the assembler will need. */
@@ -1623,6 +1672,11 @@ md_begin (void)
opcode_names_hash = str_htab_create ();
init_opcode_names_hash ();
+ /* Create pcrel_hi hash table to resolve the relocation while with
+ -mno-relax. */
+ riscv_pcrel_hi_fixup_hash = htab_create (1024, riscv_pcrel_fixup_hash,
+ riscv_pcrel_fixup_eq, free);
+
/* Set the default alignment for the text section. */
record_alignment (text_section, riscv_opts.rvc ? 1 : 2);
}
@@ -4281,8 +4335,54 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
break;
case BFD_RELOC_RISCV_PCREL_HI20:
+ /* Record and evaluate the pcrel_hi relocation with local symbol.
+ Fill in a tentative value to improve objdump readability for -mrelax,
+ and set fx_done for -mno-relax. */
+ if (fixP->fx_addsy
+ && S_IS_LOCAL (fixP->fx_addsy)
+ && S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ {
+ bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
+ bfd_vma value = target - md_pcrel_from (fixP);
+
+ /* Record PCREL_HI20. */
+ if (!riscv_record_pcrel_fixup (riscv_pcrel_hi_fixup_hash,
+ md_pcrel_from (fixP),
+ fixP->fx_addsy,
+ target))
+ as_warn (_("too many pcrel_hi"));
+
+ bfd_putl32 (bfd_getl32 (buf)
+ | ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)),
+ buf);
+ if (!riscv_opts.relax)
+ fixP->fx_done = 1;
+ }
+ relaxable = true;
+ break;
+
case BFD_RELOC_RISCV_PCREL_LO12_S:
case BFD_RELOC_RISCV_PCREL_LO12_I:
+ /* Resolve the pcrel_lo relocation with local symbol.
+ Fill in a tentative value to improve objdump readability for -mrelax,
+ and set fx_done for -mno-relax. */
+ {
+ bfd_vma location_pcrel_hi = S_GET_VALUE (fixP->fx_addsy) + *valP;
+ riscv_pcrel_hi_fixup search = {location_pcrel_hi, 0, 0};
+ riscv_pcrel_hi_fixup *entry = htab_find (riscv_pcrel_hi_fixup_hash,
+ &search);
+ if (entry && entry->symbol
+ && S_IS_LOCAL (entry->symbol)
+ && S_GET_SEGMENT (entry->symbol) == seg)
+ {
+ bfd_vma target = entry->target;
+ bfd_vma value = target - entry->address;
+ bfd_putl32 (bfd_getl32 (buf) | ENCODE_ITYPE_IMM (value), buf);
+ /* Relaxations should never be enabled by `.option relax'. */
+ if (!riscv_opts.relax)
+ fixP->fx_done = 1;
+ }
+ }
relaxable = true;
break;
@@ -5050,6 +5150,14 @@ riscv_md_finish (void)
bfd_map_over_sections (stdoutput, riscv_insert_uleb128_fixes, NULL);
}
+/* Called just before the assembler exits. */
+
+void
+riscv_md_end (void)
+{
+ htab_delete (riscv_pcrel_hi_fixup_hash);
+}
+
/* Adjust the symbol table. */
void