[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.
#
CFLAGS += -mcmodel=medany -fpie
LDFLAGS += -pie --no-dynamic-linker
LDFLAGS += -pie --no-dynamic-linker -z combreloc
# Linker script
#

View File

@@ -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

View File

@@ -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

View File

@@ -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/ : {