[PATCH] riscv: Build SPL with -fno-pic

Heinrich Schuchardt xypron.glpk at gmx.de
Fri Jan 2 11:53:04 CET 2026


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 <i at nathaniel.land>
> Closes: https://lore.kernel.org/all/932979cb47c4fded7ac19216ca172504@nathaniel.land/
> Link: https://lore.kernel.org/all/9c0038ed1494e9f293bf4faaf5e66ed8@nathaniel.land/ # [1]
> Signed-off-by: Yao Zi <me at ziyao.cc>
> ---
> 
> 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



More information about the U-Boot mailing list