On 12/15/25 18:22, Yao Zi wrote:
For RISC-V ports, both SPL and proper U-Boot are unconditionally built
with -fpic, i.e. position-independent code, which means symbols may be
addressed through GOT. This is useful for proper U-Boot, where we have
relocate_code in arch/riscv/cpu/start.S and could relocate the GOT
entries and other .data members to run at any address.

But for SPL, there's no self-relocating mechanism, all we could do is
relying on the linker to fill correct addresses into the binary at link
time, forming a position-dependent image. This effectively makes the
GOT, which is generated by usage of -fpic and couldn't be optimized out
at link time, an unnecessary size burden.

The assumption "for SPL there's no self-relocating mechanism" is wrong.

We can build RISC-V with CONFIG_SPL_RELOC_LOADER=y. And it may make sense to do so if U-Boot SPL is located in flash memory. Please, do not drop this capability.


Moreover, our current linkscript for SPL doesn't take .got section into
account, allowing the linker to put the GOT at unexpected positions,
like after _end/_image_binary_end symbols. This has caused real-world
boot failures[1] when SPL is linked by LLD.

Adding the .got section to the linker script would resolve this issue.

Best regards

Heinrich


These two reasons make building SPL with -fno-pic good choice,
eliminating the usage of GOT at the first place. This patch replaces
-fpic with -fno-pic in PLATFORM_CPPFLAGS when building SPL.

We also need to force medany code model for SPL to allow putting the
executable at any contiguous 4GiB memory range, instead of only the
lowest and highest 2GiB when using medlow. This wans't an issue
previously, since GCC ignores -mcmodel=medlow when -fpic is specified,
but is problematic with Clang, or without -fpic.

Building position-dependent code also produces bss/data/rodata section
variants with "s" prefix on RISC-V, i.e. sbss/sdata/srodata. These are
"small" sections which compilers expect to be put together, addressable
through a single instruction relative to $gp register and thus saving
code size. This is called GP-relaxation[2]. However, U-Boot takes $gp
for global data pointer, making it impossible, so these sections are
merged into their "larger" variants in linkscript.

Reported-by: Nathaniel Hourt <[email protected]>
Closes: 
https://lore.kernel.org/all/[email protected]/
Link: 
https://lore.kernel.org/all/[email protected]/ # 
[1]
Signed-off-by: Yao Zi <[email protected]>
---

I've tested the patch with

        starfive_visionfive2_defconfig (StarFive VisionFive2)
        sifive_unleashed_defconfig (QEMU -machine sifive_u)
        th1520_lpi4a_defconfig (Lichee Pi 4A 16GiB)

built with either GCC or Clang, all these ports boot well. When building
with GCC, this also shrinks SPL of starfive_visionfive2_defconfig by
1.5KiB (1%),

without patch;
        -rw-r--r-- 1 ziyao ziyao 149169 Dec 15 11:56 u-boot-spl.bin

with patch:
        -rw-r--r-- 1 ziyao ziyao 147731 Dec 15 11:57 u-boot-spl.bin

This is a relatively large change to RISC-V SPL, so I'm willing to wait
for some time for more comments and wider testing. Thanks for your time!

  arch/riscv/Makefile           | 5 ++++-
  arch/riscv/config.mk          | 8 +++++++-
  arch/riscv/cpu/u-boot-spl.lds | 3 +++
  3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index fdda6da1df32..90d98eaba6bc 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -32,13 +32,16 @@ endif
  ifeq ($(CONFIG_RISCV_ISA_ZBB),y)
        ARCH_ZBB = _zbb
  endif
+ifeq ($(CONFIG_XPL_BUILD),y)
+       CMODEL = medany
+else
  ifeq ($(CONFIG_CMODEL_MEDLOW),y)
        CMODEL = medlow
  endif
  ifeq ($(CONFIG_CMODEL_MEDANY),y)
        CMODEL = medany
  endif
-
+endif
RISCV_MARCH = $(ARCH_BASE)$(ARCH_A)$(ARCH_F)$(ARCH_D)$(ARCH_C)$(ARCH_ZBB)
  ABI = $(ABI_BASE)$(ABI_D)
diff --git a/arch/riscv/config.mk b/arch/riscv/config.mk
index eddd6a3b9a29..4d6fd7434171 100644
--- a/arch/riscv/config.mk
+++ b/arch/riscv/config.mk
@@ -35,7 +35,13 @@ EFI_LDS                      := elf_riscv64_efi.lds
  PLATFORM_ELFFLAGS     += -B riscv -O elf64-$(large-endian)riscv
  endif
-PLATFORM_CPPFLAGS += -ffixed-x3 -fpic
+ifdef CONFIG_XPL_BUILD
+PLATFORM_CPPFLAGS      += -fno-pic
+else
+PLATFORM_CPPFLAGS      += -fpic
+endif
+
+PLATFORM_CPPFLAGS      += -ffixed-x3
  PLATFORM_RELFLAGS     += -fno-common -ffunction-sections -fdata-sections
  LDFLAGS_u-boot                += --gc-sections -static -pie
diff --git a/arch/riscv/cpu/u-boot-spl.lds b/arch/riscv/cpu/u-boot-spl.lds
index 0717833df556..dc028702a5d8 100644
--- a/arch/riscv/cpu/u-boot-spl.lds
+++ b/arch/riscv/cpu/u-boot-spl.lds
@@ -25,11 +25,13 @@ SECTIONS
        . = ALIGN(4);
        .rodata : {
                *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
+               *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.srodata*)))
        } > .spl_mem
. = ALIGN(4);
        .data : {
                *(.data*)
+               *(.sdata*)
        } > .spl_mem
        . = ALIGN(4);
@@ -52,6 +54,7 @@ SECTIONS
        .bss : {
                __bss_start = .;
                *(.bss*)
+               *(.sbss*)
                . = ALIGN(8);
                __bss_end = .;
        } > .bss_mem

Reply via email to