From 4bef4c80699da2ae75895035644ddadfdc1a62f8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 May 2025 14:53:29 +0100 Subject: [PATCH] [riscv] Use compressed relocation records Use compressed relocation records instead of raw Elf_Rela records. This saves around 15% of the total binary size for the all-drivers image bin-riscv64/ipxe.sbi. Signed-off-by: Michael Brown --- src/arch/riscv/Makefile.sbi | 2 +- src/arch/riscv/prefix/libprefix.S | 102 ++++++++++++++++-------------- src/arch/riscv/prefix/sbiprefix.S | 12 +++- src/arch/riscv/scripts/sbi.lds | 23 ++----- 4 files changed, 73 insertions(+), 66 deletions(-) diff --git a/src/arch/riscv/Makefile.sbi b/src/arch/riscv/Makefile.sbi index dee1b6e5d..a10680eaf 100644 --- a/src/arch/riscv/Makefile.sbi +++ b/src/arch/riscv/Makefile.sbi @@ -5,7 +5,7 @@ # prefix code. # CFLAGS += -mcmodel=medany -fpie -LDFLAGS += -pie --no-dynamic-linker +LDFLAGS += -pie --no-dynamic-linker -z combreloc # Linker script # diff --git a/src/arch/riscv/prefix/libprefix.S b/src/arch/riscv/prefix/libprefix.S index f268def1a..43a923303 100644 --- a/src/arch/riscv/prefix/libprefix.S +++ b/src/arch/riscv/prefix/libprefix.S @@ -32,14 +32,23 @@ .section ".note.GNU-stack", "", @progbits .text + /* Virtual address of _prefix + * + * This will be updated if runtime relocations are applied. + */ + .section ".rodata.prefix_virt", "a", @progbits +prefix_virt: + .dword _prefix + .size prefix_virt, . - prefix_virt + /***************************************************************************** * - * Apply relocation records + * Apply compressed relocation records * ***************************************************************************** * - * Apply relocation records from .rel.dyn to fix up iPXE to run at its - * current address. + * Apply compressed relocation records to fix up iPXE to run at its + * current virtual address. * * This function must run before .bss is zeroed (since the relocation * records are overlaid with .bss). It does not require a valid stack @@ -51,58 +60,63 @@ * */ -/* Relative relocation type */ -#define R_RISCV_RELATIVE 3 - - /* Layout of a relocation record */ - .struct 0 -rela_offset: .space ( __riscv_xlen / 8 ) -rela_type: .space ( __riscv_xlen / 8 ) -rela_addend: .space ( __riscv_xlen / 8 ) -rela_len: - .previous +/** Number of bits in a skip value */ +#define ZREL_SKIP_BITS 19 .section ".prefix.apply_relocs", "ax", @progbits .globl apply_relocs apply_relocs: + /* Register usage: + * + * a0 - relocation addend + * a1 - current relocation target address + * a2 - current relocation record pointer + * a3 - current relocation record value + * a4 - number of bits remaining in current relocation record + */ + la a1, _prefix + la a2, _edata - /* Get relocation records */ - la t0, _reloc - la t1, _ereloc + /* Calculate relocation addend */ + la t0, prefix_virt + LOADN a0, (t0) + sub a0, a1, a0 - /* Determine current location */ - la t2, reloc_base - LOADN t2, (t2) - sub t2, t0, t2 + /* Skip applying relocations if addend is zero */ + beqz a0, apply_relocs_done -1: /* Read relocation record */ - LOADN t3, rela_offset(t0) - LOADN t4, rela_type(t0) - LOADN t5, rela_addend(t0) +apply_relocs_loop: + /* Read new relocation record */ + LOADN a3, (a2) + addi a2, a2, ( __riscv_xlen / 8 ) + li a4, ( __riscv_xlen - 1 ) - /* Check relocation type */ - addi t4, t4, -R_RISCV_RELATIVE - bnez t4, 2f + /* Consume and apply skip, if present (i.e. if MSB=0) */ + bltz a3, 1f + addi a4, a4, -ZREL_SKIP_BITS + srli t0, a3, ( __riscv_xlen - ( ZREL_SKIP_BITS + 1 ) ) + slli t0, t0, ( ( __riscv_xlen / 32 ) + 1 ) + add a1, a1, t0 +1: + /* Apply relocations corresponding to set bits in record */ +1: andi t0, a3, 1 + beqz t0, 2f + LOADN t1, (a1) + add t1, t1, a0 + STOREN t1, (a1) +2: addi a1, a1, ( __riscv_xlen / 8 ) + srli a3, a3, 1 + addi a4, a4, -1 + bnez a4, 1b - /* Apply relocation */ - add t3, t3, t2 - add t5, t5, t2 - STOREN t5, (t3) -2: - /* Loop until done */ - addi t0, t0, rela_len - blt t0, t1, 1b + /* Loop until we have reached a terminator record (MSB=0, offset=0) */ + bnez a3, apply_relocs_loop +apply_relocs_done: /* Return to caller */ ret .size apply_relocs, . - apply_relocs - /* Link-time address of _reloc */ - .section ".rodata", "a", @progbits -reloc_base: - .dword _reloc_base - .size reloc_base, . - reloc_base - /***************************************************************************** * * Enable paging @@ -187,12 +201,6 @@ reloc_base: .globl enable_paging .equ enable_paging, _C2 ( enable_paging_, __riscv_xlen ) - /* Link-time address of _prefix */ - .section ".rodata.prefix_virt", "a", @progbits -prefix_virt: - .dword _prefix - .size prefix_virt, . - prefix_virt - /***************************************************************************** * * Enable 64-bit paging diff --git a/src/arch/riscv/prefix/sbiprefix.S b/src/arch/riscv/prefix/sbiprefix.S index ccf8b0743..f590b324b 100644 --- a/src/arch/riscv/prefix/sbiprefix.S +++ b/src/arch/riscv/prefix/sbiprefix.S @@ -41,6 +41,9 @@ #define SBI_SRST_SYSTEM_RESET 0x00 #define SBI_RESET_COLD 0x00000001 +/* ELF machine type */ +#define EM_RISCV 243 + /* * Display progress message via debug console */ @@ -127,5 +130,12 @@ _sbi_start: .section ".zinfo", "a", @progbits .ascii "COPY" .word 0 - .word _sbi_filesz + .word _filesz .word 1 + .ascii "BASE" + .word 0 + .dword _base + .ascii "ZREL" + .word _reloc_offset + .word _reloc_filesz + .word EM_RISCV diff --git a/src/arch/riscv/scripts/sbi.lds b/src/arch/riscv/scripts/sbi.lds index 21fba5d3d..efda0aa81 100644 --- a/src/arch/riscv/scripts/sbi.lds +++ b/src/arch/riscv/scripts/sbi.lds @@ -61,7 +61,6 @@ SECTIONS { /* Runtime relocations (discarded after use) */ .rela.dyn { - _reloc = .; *(.rela) *(.rela.dyn) } @@ -88,25 +87,15 @@ SECTIONS { } } - /* Absolute link-time address of _reloc - * - * The runtime relocator needs to know the link-time addresses. - * Since RISC-V uses .rela (rather than .rel), the only way to - * expose this to the relocator is via an absolute symbol. - */ - _reloc_base = ABSOLUTE ( _reloc ); + /* Base virtual address */ + _base = ABSOLUTE ( _prefix ); - /* Calculate end of relocations - * - * This cannot be done by placing "_ereloc = .;" inside the - * .rela.dyn section, since the dynamic relocations are not - * present in the input sections but are instead generated during - * linking. - */ - _ereloc = ( _reloc + __load_stop_reladyn - __load_start_reladyn ); + /* Relocations */ + _reloc_offset = ( LOADADDR ( .rela.dyn ) - LOADADDR ( .prefix ) ); + _reloc_filesz = SIZEOF ( .rela.dyn ); /* Length of initialised data */ - _sbi_filesz = ( ABSOLUTE ( _ereloc ) - ABSOLUTE ( _prefix ) ); + _filesz = ( ABSOLUTE ( _edata ) - ABSOLUTE ( _prefix ) ); /* Unwanted sections */ /DISCARD/ : {