[PATCH] RISC-V: Also output undefined symbols as dynamic symbols
Vivian Wang
wangruikang@iscas.ac.cn
Thu May 14 10:39:12 GMT 2026
On 5/13/26 23:10, Nelson Chu wrote:
> On Mon, May 11, 2026 at 12:28 PM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>> Consider this program, which refers to an undefined symbol foo:
>>
>> int foo(void);
>> int main() { foo(); }
>>
>> If linking with --unresolved-symbols=ignore-in-object-file, what should
>> happen is the symbol foo is left undefined and left for the dynamic
>> linker to resolve. (See commit 4295841be439 ("RISC-V: Go PLT for
>> CALL/JUMP/RVC_JUMP if `h->plt.offset' isn't -1").) Instead, with
>> binutils 2.46:
>>
>> 1. With -no-pie, ld generates an auipc/jalr call to address 0, crashing
>> at runtime instead of generating an undefined symbol message.
>>
>> 2. With -pie, ld generates this bogus error message (-fPIC does not
>> change this, and R_RISCV_CALL_PLT is correct here) and stops, failing
>> to produce an executable:
>>
>> ld: relocation R_RISCV_CALL_PLT against `foo' which may bind
>> externally can not be used when making a shared object; recompile
>> with -fPIC
>>
>> Bisection leads to commit 9e10fcf71c11 ("RISC-V: Fix the assert fail
>> when linking discarded sections under -pie for got"), which stopped
>> outputting dynamic symbols for non-undefweak symbols. Therefore, in
>> order to fix the issue, also output undefined symbols as dynamic
>> symbols.
> So do you think we should also stop output dynamic symbols for
> undefined symbols? umm maybe you are right, I am not sure.
FWIW, I can't really claim that this handling of undefined symbols is
what *should* be done, just that this restores some slightly earlier
version's behavior for riscv, and gets rid of the bogus error message
from ld.
Maybe I have misunderstood the purpose of the "Go PLT" commit
9e10fcf71c11? Do we just want to get rid of the segfault? In that case
the behavior on at least x86 and arm is to give *this* error message
instead on runtime:
./foo: error while loading shared libraries: unexpected PLT reloc type 0x00
And in the executable, the PLT entry is created but with zeros instead
of actual instructions, the undefined symbol is not emitted in .dynsym,
and probably other stuff are missing as well. So the executable is still
*broken*, but doesn't segfault trying to call address 0.
If that's what we want for riscv then what we want is to allocate a PLT
entry even for an undefined symbol. I think what we need is to fix the
conditional after to be like x86/arm:
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index eb3d7926c..c352e5eba 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -1446,7 +1446,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
&& !bfd_elf_link_record_dynamic_symbol (info, h))
return false;
- if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
+ if (bfd_link_pic (info)
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
{
asection *s = htab->elf.splt;
I'm not sure what the difference is though. The latter first (I think)
appeared back in 2003 (26e415943a5d) and the description was just
"Adjust WILL_CALL_FINISH_DYNAMIC_SYMBOL uses, and optimize." Across BFD
we have 9 instances of if (WILL_CALL_FINISH_DYNAMIC_SYMBOL) and 12
instances of if (bfd_link_pic || WILL_CALL_FINISH_DYNAMIC_SYMBOL), and
some variants like whatever loongarch is doing. But at least I think
this is probably right for riscv.
So, again, that's just an idea. Do you think this fix makes more sense?
> Just
> curious, it seems other targets don't need to handle
> bfd_link_hash_undefined in the following codes, so why and what's the
> difference between riscv and others? I think we might miss some
> codes, rather than the codes here, just guess.
I don't think I missed anything for *this* case, since it was three
occurrences of the same code and same comments. There may be other
situations regarding undefined and undefweak that are not handled. But
also, as said above, the approach could be wrong.
Vivian "dramforever" Wang
> [...]
More information about the Binutils
mailing list