[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-05-06 14:53:29 +01:00
parent 8f7aa292aa
commit 4bef4c8069
4 changed files with 73 additions and 66 deletions

View File

@@ -5,7 +5,7 @@
# prefix code. # prefix code.
# #
CFLAGS += -mcmodel=medany -fpie CFLAGS += -mcmodel=medany -fpie
LDFLAGS += -pie --no-dynamic-linker LDFLAGS += -pie --no-dynamic-linker -z combreloc
# Linker script # Linker script
# #

View File

@@ -32,14 +32,23 @@
.section ".note.GNU-stack", "", @progbits .section ".note.GNU-stack", "", @progbits
.text .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 * Apply compressed relocation records to fix up iPXE to run at its
* current address. * current virtual address.
* *
* This function must run before .bss is zeroed (since the relocation * This function must run before .bss is zeroed (since the relocation
* records are overlaid with .bss). It does not require a valid stack * records are overlaid with .bss). It does not require a valid stack
@@ -51,58 +60,63 @@
* *
*/ */
/* Relative relocation type */ /** Number of bits in a skip value */
#define R_RISCV_RELATIVE 3 #define ZREL_SKIP_BITS 19
/* 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
.section ".prefix.apply_relocs", "ax", @progbits .section ".prefix.apply_relocs", "ax", @progbits
.globl apply_relocs .globl apply_relocs
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 */ /* Calculate relocation addend */
la t0, _reloc la t0, prefix_virt
la t1, _ereloc LOADN a0, (t0)
sub a0, a1, a0
/* Determine current location */ /* Skip applying relocations if addend is zero */
la t2, reloc_base beqz a0, apply_relocs_done
LOADN t2, (t2)
sub t2, t0, t2
1: /* Read relocation record */ apply_relocs_loop:
LOADN t3, rela_offset(t0) /* Read new relocation record */
LOADN t4, rela_type(t0) LOADN a3, (a2)
LOADN t5, rela_addend(t0) addi a2, a2, ( __riscv_xlen / 8 )
li a4, ( __riscv_xlen - 1 )
/* Check relocation type */ /* Consume and apply skip, if present (i.e. if MSB=0) */
addi t4, t4, -R_RISCV_RELATIVE bltz a3, 1f
bnez t4, 2f 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 */ /* Loop until we have reached a terminator record (MSB=0, offset=0) */
add t3, t3, t2 bnez a3, apply_relocs_loop
add t5, t5, t2
STOREN t5, (t3)
2:
/* Loop until done */
addi t0, t0, rela_len
blt t0, t1, 1b
apply_relocs_done:
/* Return to caller */ /* Return to caller */
ret ret
.size apply_relocs, . - apply_relocs .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 * Enable paging
@@ -187,12 +201,6 @@ reloc_base:
.globl enable_paging .globl enable_paging
.equ enable_paging, _C2 ( enable_paging_, __riscv_xlen ) .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 * Enable 64-bit paging

View File

@@ -41,6 +41,9 @@
#define SBI_SRST_SYSTEM_RESET 0x00 #define SBI_SRST_SYSTEM_RESET 0x00
#define SBI_RESET_COLD 0x00000001 #define SBI_RESET_COLD 0x00000001
/* ELF machine type */
#define EM_RISCV 243
/* /*
* Display progress message via debug console * Display progress message via debug console
*/ */
@@ -127,5 +130,12 @@ _sbi_start:
.section ".zinfo", "a", @progbits .section ".zinfo", "a", @progbits
.ascii "COPY" .ascii "COPY"
.word 0 .word 0
.word _sbi_filesz .word _filesz
.word 1 .word 1
.ascii "BASE"
.word 0
.dword _base
.ascii "ZREL"
.word _reloc_offset
.word _reloc_filesz
.word EM_RISCV

View File

@@ -61,7 +61,6 @@ SECTIONS {
/* Runtime relocations (discarded after use) */ /* Runtime relocations (discarded after use) */
.rela.dyn { .rela.dyn {
_reloc = .;
*(.rela) *(.rela)
*(.rela.dyn) *(.rela.dyn)
} }
@@ -88,25 +87,15 @@ SECTIONS {
} }
} }
/* Absolute link-time address of _reloc /* Base virtual address */
* _base = ABSOLUTE ( _prefix );
* 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 );
/* Calculate end of relocations /* Relocations */
* _reloc_offset = ( LOADADDR ( .rela.dyn ) - LOADADDR ( .prefix ) );
* This cannot be done by placing "_ereloc = .;" inside the _reloc_filesz = SIZEOF ( .rela.dyn );
* .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 );
/* Length of initialised data */ /* Length of initialised data */
_sbi_filesz = ( ABSOLUTE ( _ereloc ) - ABSOLUTE ( _prefix ) ); _filesz = ( ABSOLUTE ( _edata ) - ABSOLUTE ( _prefix ) );
/* Unwanted sections */ /* Unwanted sections */
/DISCARD/ : { /DISCARD/ : {