Skip to content

Commit

Permalink
elfloader: improve EFI support
Browse files Browse the repository at this point in the history
Rather than using a bunch of assembly code to relocate the
ELF loader, we use gnu-efi[1] to build an EFI executable.
This approach handles things like nested structs better, by including
a "proper" way to handle ELF relocations.

[1]: https://sourceforge.net/projects/gnu-efi/
  • Loading branch information
fourkbomb committed Dec 12, 2019
1 parent f715d15 commit 030d83b
Show file tree
Hide file tree
Showing 18 changed files with 782 additions and 332 deletions.
57 changes: 41 additions & 16 deletions elfloader-tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,18 +181,29 @@ if(KernelArchARM)
endif()

if(ElfloaderImageEFI)
file(GLOB efi_files src/binaries/efi/*.c)
list(APPEND files "src/binaries/efi/efi_head.S" ${efi_files})
# We cannot control where EFI loads the image and so we must make it relocatable
add_compile_options(-fpic)
if(KernelSel4ArchAarch32)
set(gnuefiArch "arm")
# on aarch32 building with -fno-pie results in the compiler generating
# movt/movw pairs that we can't easily relocate.
add_compile_options(-fpie -mno-single-pic-base)
else()
set(gnuefiArch "aarch64")
# on aarch64 building with -fno-pie will just use pc-relative addressing.
add_compile_options(-fno-pie)
add_compile_options(-fno-pie -fPIC)
endif()

file(GLOB efi_files src/binaries/efi/*.c)
list(
APPEND
files
${efi_files}
src/binaries/efi/gnuefi/crt0-efi-${gnuefiArch}.S
src/binaries/efi/gnuefi/reloc_${gnuefiArch}.c
)
# We use gnu-efi's linker script on EFI.
set(linkerScript ${CMAKE_CURRENT_LIST_DIR}/src/binaries/efi/gnuefi/elf_${gnuefiArch}_efi.lds)
else()
add_compile_options(-fno-pic)
add_compile_options(-fno-pie)
Expand Down Expand Up @@ -308,7 +319,33 @@ add_custom_command(
add_custom_target(elfloader_linker DEPENDS linker.lds_pp)

add_executable(elfloader EXCLUDE_FROM_ALL ${files} archive.o)
set_target_properties(elfloader PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp)
if(ElfloaderImageEFI)
set_property(TARGET elfloader APPEND_STRING PROPERTY LINK_FLAGS " -pie ")
set_target_properties(elfloader PROPERTIES LINK_DEPENDS ${linkerScript})
set_property(
TARGET elfloader
APPEND_STRING
PROPERTY
LINK_FLAGS
# -Bsymbolic forces symbols to bind to their definitions within the elfloader
# EFI_SUBSYSTEM=0xa indicates that we're building an EFI application.
" -T ${linkerScript} -nostdlib -shared -Wl,-Bsymbolic,--defsym=EFI_SUBSYSTEM=0xa -Wl,--build-id=none"
)
else()
set_target_properties(
elfloader
PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp
)
add_dependencies(elfloader elfloader_linker)
set_property(
TARGET elfloader
APPEND_STRING
PROPERTY
LINK_FLAGS
" -T ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp -nostdlib -static -Wl,--build-id=none"
)
endif()

target_include_directories(
elfloader
PRIVATE
Expand Down Expand Up @@ -336,15 +373,3 @@ target_link_libraries(
elfloader_Config
sel4_autoconf
)
add_dependencies(elfloader elfloader_linker)
set_property(
TARGET elfloader
APPEND_STRING
PROPERTY
LINK_FLAGS
" -T ${CMAKE_CURRENT_BINARY_DIR}/linker.lds_pp -nostdlib -static -Wl,--build-id=none"
)

if(ElfloaderImageEFI)
set_property(TARGET elfloader APPEND_STRING PROPERTY LINK_FLAGS " -pie ")
endif()
27 changes: 27 additions & 0 deletions elfloader-tool/include/binaries/elf/elf32.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@

#include <types.h>

#define ELF32_R_TYPE(val) ((val) & 0xff)
#define R_ARM_NONE 0 /* No reloc */
#define R_ARM_RELATIVE 23 /* Adjust by program base */
/*
* File header
*/
Expand Down Expand Up @@ -146,6 +149,30 @@ struct Elf32_Phdr {
* memory */
};

/*
* Dynamic header
*/
struct Elf32_Dyn
{
uint32_t d_tag; /* Dynamic entry type */
union
{
uint32_t d_val; /* Integer value */
uint32_t d_ptr; /* Address value */
} d_un;
};


/*
* Relocation
*/
struct Elf32_Rel
{
uint32_t r_offset; /* Address */
uint32_t r_info; /* Relocation type and symbol index */
};


int elf32_checkFile(struct Elf32_Header *file);
struct Elf32_Phdr * elf32_getProgramSegmentTable(struct Elf32_Header *file);
unsigned elf32_getNumSections(struct Elf32_Header *file);
Expand Down
9 changes: 9 additions & 0 deletions elfloader-tool/include/binaries/elf/elf64.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@

#include <types.h>

#define R_AARCH64_NONE 0 /* No relocation. */
#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
/*
* File header
*/
Expand Down Expand Up @@ -157,6 +160,12 @@ struct Elf64_Dyn {
} d_un;
};

struct Elf64_Rela {
uint64_t r_offset; /* Address */
uint64_t r_info; /* Relocation type and symbol index */
uint64_t r_addend; /* Addend */
};

int elf64_checkFile(void *elfFile);
struct Elf64_Phdr * elf64_getProgramSegmentTable(void *elfFile);
unsigned elf64_getNumSections(void *elfFile);
Expand Down
2 changes: 1 addition & 1 deletion elfloader-tool/include/elfloader_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extern struct image_info user_info;
extern void *dtb;

/* Symbols defined in linker scripts. */
extern char _start[];
extern char _text[];
extern char _end[];
extern char _archive_start[];
extern char _archive_start_end[];
Expand Down
111 changes: 26 additions & 85 deletions elfloader-tool/src/arch-arm/32/crt0.S
Original file line number Diff line number Diff line change
Expand Up @@ -64,98 +64,42 @@ LC0:
.word core_stack_alloc + 0xff0 // r5
.size LC0, . - LC0

BEGIN_FUNC(arch_early_relocate)
/* At this point, we're running on the EFI-provided stack, which is not a problem.
* Before we can save the EFI arguments in efi_early_init, we need to relocate
* the global offset table (GOT) entries.
*/

push {r0, r1} // save EFI arguments
adr r0, LC1 // get actual address of LC1
ldmia r0, {r4, r6, r7, r8}

/*
* r4 = unrelocated LC1 address
* r6 = GOT start (unrelocated)
* r7 = GOT end (unrelocated)
* r8 = vector literal pool (unrelocated)
*/

sub r0, r0, r4 // get relocation offset
add r6, r6, r0 // relocate GOT start
add r7, r7, r0 // relocate GOT end
add r8, r8, r0 // relocate vector literal pool

/* relocate vector table literals */
1: ldr r1, [r8, #0] // load table entry
cmp r1, #0 // zero => end of literal pool
beq 2f
add r1, r1, r0 // relocate
str r1, [r8], #4 // put back
b 1b
2:
/* relocate GOT entries - fix up C global variables. */
1: ldr r1, [r6, #0] // load table entry
add r1, r1, r0 // relocate
str r1, [r6], #4 // put back
cmp r6, r7
blo 1b

pop {r0, r1} // restore EFI arguments
mov pc, lr
END_FUNC(arch_early_relocate)
.align 2
.type LC1, #object
LC1:
.word LC1
.word _got_start
.word _got_end
.word arm_vector_literals
.size LC1, . - LC1

/* Move the elf loader out of the kernel's way */
BEGIN_FUNC(finish_relocation)
#ifdef CONFIG_IMAGE_EFI
#if CONFIG_IMAGE_EFI
// r0 = offset between current base address and new base address
// r1 = _DYNAMIC pointer (current base address)
// r2 = total offset (new_base)
mov r8, r1 // move to r8

// figure out relocated address of continue_boot
adr r1, LC2
ldmia r1, {r2, r3, r4, r5, r6, r7}
ldmia r1, {r3, r4, r5}

sub r1, r1, r2 // r1 = current relocation offset
sub r1, r1, r0 // total relocation offset (i.e. to final address)
add sp, r3, r1 // new sp
add r4, r4, r1 // continue_boot address in new stack
// r3 = stack pointer (in current program)
// r4 = continue_boot (in current program)
// r5 = current base address (_text, the start of the binary)

add r5, r5, r1 // new GOT start
add r6, r6, r1 // new GOT end
add r7, r7, r1 // vector function table
sub sp, r3, r0 // point sp to new stack
sub r4, r4, r0 // continue_boot address in relocated elfloader

/*
* relocate the vector table's literal pool
* to make exception vectors work, and not
* just jump to a random location in memory.
*/
1: ldr r3, [r7, #0]
cmp r3, #0
beq 2f
sub r3, r3, r0
str r3, [r7], #4
b 1b
2:
/*
* note that we already relocated the GOT addresses to the
* current base address, so we use the offset in r0 (not r1!)
* when relocating them again.
*/
// self_reloc expects:
// r0 = current base address
// r1 = dynamic section pointer
// r2 = new base address
// r3 = 0

mov r0, r5 // current base address
mov r1, r8 // put _DYNAMIC back
// r2 is new base address, passed from relocate_below_kernel()
mov r3, #0 // NULL r3

push {r4} // save continue_boot address

/* relocate GOT entries - fix up C global variables. */
1: ldr r1, [r5, #0] // load table entry
sub r1, r1, r0 // relocate
str r1, [r5], #4 // put back
cmp r5, r6
blo 1b
bl _relocate

pop {r4}
mov r0, #1
blx r4
b abort // should never get here!
#else
Expand All @@ -169,12 +113,9 @@ END_FUNC(finish_relocation)
.align 2
.type LC2, #object
LC2:
.word LC2 // r2
.word core_stack_alloc + 0xff0 // r3
.word continue_boot // r4
.word _got_start // r5
.word _got_end // r6
.word arm_vector_literals // r7
.word _text // r5
.size LC2, . - LC2

#if CONFIG_MAX_NUM_NODES > 1
Expand Down
30 changes: 1 addition & 29 deletions elfloader-tool/src/arch-arm/linker.lds
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,23 @@

#include "image_start_addr.h"

PECOFF_FILE_ALIGNMENT = 0x200;

SECTIONS
{
.efi_text :
{
*(.text.efi.header)
}
.interp : { *(.interp) }
}
INSERT BEFORE .hash;

SECTIONS
{
#ifndef CONFIG_IMAGE_EFI
. = IMAGE_START_ADDR;
#endif
_text = .;
.start :
{
*(.text.start)
}
}
INSERT BEFORE .text;

#ifdef CONFIG_IMAGE_EFI
SECTIONS
{
.pecoff_edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGNMENT); }
}
INSERT AFTER .data;
#endif

SECTIONS
{
.got.plt : { *(.got.plt) }
/*
* Put the GOT in its own section, with symbols around it
* so that ASM code can easily find it and relocate addresses in it.
*/
_got_start = .;
.got : { *(.got) }
_got_end = .;
}
INSERT AFTER .rodata;

SECTIONS
{
. = ALIGN(8);
Expand Down
Loading

0 comments on commit 030d83b

Please sign in to comment.