More examples

This commit is contained in:
bzt 2021-02-20 16:24:46 +01:00
parent 81a82cf366
commit d6ecf787f7
15 changed files with 502 additions and 0 deletions

View file

@ -0,0 +1,9 @@
TARGET = elfload.efi
SRCS = elfload.c
EXTRA = kernel.elf
#USE_GCC=1
include uefi/Makefile
kernel.elf:
@make -C kernel all USE_GCC=$(USE_GCC)

View file

@ -0,0 +1,20 @@
ELF loader
==========
This is an example ELF loader. Note that UEFI wastes lots and lots of memory, so be sure you link your ELF
at an address which isn't used by UEFI. The safest would be 64M, but using 32M looks ok.
Compilation
-----------
To compile the loader
```sh
$ make
```
To compile the "kernel":
```sh
$ make -f Makefile.kernel kernel.elf
```
First compile the loader, then the kernel. This is tricky because compiling the kernel includes the normal Makefile to
get gcc options and flags. Normally you should have the kernel in completely separated repository with its own Makefile.

View file

@ -0,0 +1,114 @@
#include <uefi.h>
/*** ELF64 defines and structs ***/
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASS64 2 /* 64-bit objects */
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ET_EXEC 2 /* Executable file */
#define PT_LOAD 1 /* Loadable program segment */
#ifdef __x86_64__
#define EM_MACH 62 /* AMD x86-64 architecture */
#endif
#ifdef __aarch64__
#define EM_MACH 183 /* ARM aarch64 architecture */
#endif
typedef struct
{
uint8_t e_ident[16]; /* Magic number and other info */
uint16_t e_type; /* Object file type */
uint16_t e_machine; /* Architecture */
uint32_t e_version; /* Object file version */
uint64_t e_entry; /* Entry point virtual address */
uint64_t e_phoff; /* Program header table file offset */
uint64_t e_shoff; /* Section header table file offset */
uint32_t e_flags; /* Processor-specific flags */
uint16_t e_ehsize; /* ELF header size in bytes */
uint16_t e_phentsize; /* Program header table entry size */
uint16_t e_phnum; /* Program header table entry count */
uint16_t e_shentsize; /* Section header table entry size */
uint16_t e_shnum; /* Section header table entry count */
uint16_t e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
typedef struct
{
uint32_t p_type; /* Segment type */
uint32_t p_flags; /* Segment flags */
uint64_t p_offset; /* Segment file offset */
uint64_t p_vaddr; /* Segment virtual address */
uint64_t p_paddr; /* Segment physical address */
uint64_t p_filesz; /* Segment size in file */
uint64_t p_memsz; /* Segment size in memory */
uint64_t p_align; /* Segment alignment */
} Elf64_Phdr;
/**
* Load an ELF executable
*/
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
FILE *f;
char *buff;
long int size;
Elf64_Ehdr *elf;
Elf64_Phdr *phdr;
uintptr_t entry;
int i, j;
/* load the file */
if((f = fopen("\\0E_elfload\\kernel.elf", "r"))) {
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
buff = malloc(size + 1);
if(!buff) {
fprintf(stderr, "unable to allocate memory\n");
return 1;
}
fread(buff, size, 1, f);
fclose(f);
} else {
fprintf(stderr, "Unable to open file\n");
return 0;
}
/* is it a valid ELF executable for this architecture? */
elf = (Elf64_Ehdr *)buff;
if(!memcmp(elf->e_ident, ELFMAG, SELFMAG) && /* magic match? */
elf->e_ident[EI_CLASS] == ELFCLASS64 && /* 64 bit? */
elf->e_ident[EI_DATA] == ELFDATA2LSB && /* LSB? */
elf->e_type == ET_EXEC && /* executable object? */
elf->e_machine == EM_MACH && /* architecture match? */
elf->e_phnum > 0) { /* has program headers? */
/* load segments */
for(phdr = (Elf64_Phdr *)(buff + elf->e_phoff), i = 0;
i < elf->e_phnum;
i++, phdr = (Elf64_Phdr *)((uint8_t *)phdr + elf->e_phentsize)) {
if(phdr->p_type == PT_LOAD) {
printf("ELF segment %p %d bytes (bss %d bytes)\n", phdr->p_vaddr, phdr->p_filesz,
phdr->p_memsz - phdr->p_filesz);
memcpy((void*)phdr->p_vaddr, buff + phdr->p_offset, phdr->p_filesz);
memset((void*)(phdr->p_vaddr + phdr->p_filesz), 0, phdr->p_memsz - phdr->p_filesz);
}
}
entry = elf->e_entry;
} else {
fprintf(stderr, "not a valid ELF executable for this architecture\n");
return 0;
}
/* free resources */
free(buff);
/* execute the "kernel" */
printf("ELF entry point %p\n", entry);
i = (*((int(* __attribute__((sysv_abi)))(void))(entry)))();
printf("ELF returned %d\n", i);
return 0;
}

View file

@ -0,0 +1,21 @@
ARCH = x86_64
TARGET = ../kernel.elf
SRCS = kernel.c
all: $(TARGET)
ifeq ($(USE_GCC),)
CC = clang -target $(ARCH)-elf
LD = ld.lld
else
CC = $(ARCH)-elf-gcc
LD = $(ARCH)-elf-ld
endif
# UEFI wastes lots and lots of memory. Link our "kernel" at an address (32M) which isn't used by UEFI
$(TARGET): kernel.c
$(CC) -ffreestanding -fno-stack-protector -fno-stack-check -D__$(ARCH)__ -I. -c $< -o kernel.o
$(LD) -nostdlib -z max-page-size=0x1000 -Ttext=0x01000000 kernel.o -o $(TARGET)
clean:
rm *.o $(TARGET) 2>/dev/null || true

View file

@ -0,0 +1,7 @@
/**
* Example "kernel"
*/
int _start()
{
return 42;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

1
examples/0E_elfload/uefi Symbolic link
View file

@ -0,0 +1 @@
../../uefi

View file

@ -0,0 +1,9 @@
TARGET = exit_bs.efi
SRCS = exit_bs.c
EXTRA = kernel.elf
USE_GCC=1
include uefi/Makefile
kernel.elf:
@make -C kernel all USE_GCC=$(USE_GCC)

47
examples/0F_exit_bs/elf.h Normal file
View file

@ -0,0 +1,47 @@
/*** ELF64 defines and structs ***/
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASS64 2 /* 64-bit objects */
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ET_EXEC 2 /* Executable file */
#define PT_LOAD 1 /* Loadable program segment */
#ifdef __x86_64__
#define EM_MACH 62 /* AMD x86-64 architecture */
#endif
#ifdef __aarch64__
#define EM_MACH 183 /* ARM aarch64 architecture */
#endif
typedef struct
{
uint8_t e_ident[16]; /* Magic number and other info */
uint16_t e_type; /* Object file type */
uint16_t e_machine; /* Architecture */
uint32_t e_version; /* Object file version */
uint64_t e_entry; /* Entry point virtual address */
uint64_t e_phoff; /* Program header table file offset */
uint64_t e_shoff; /* Section header table file offset */
uint32_t e_flags; /* Processor-specific flags */
uint16_t e_ehsize; /* ELF header size in bytes */
uint16_t e_phentsize; /* Program header table entry size */
uint16_t e_phnum; /* Program header table entry count */
uint16_t e_shentsize; /* Section header table entry size */
uint16_t e_shnum; /* Section header table entry count */
uint16_t e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
typedef struct
{
uint32_t p_type; /* Segment type */
uint32_t p_flags; /* Segment flags */
uint64_t p_offset; /* Segment file offset */
uint64_t p_vaddr; /* Segment virtual address */
uint64_t p_paddr; /* Segment physical address */
uint64_t p_filesz; /* Segment size in file */
uint64_t p_memsz; /* Segment size in memory */
uint64_t p_align; /* Segment alignment */
} Elf64_Phdr;

View file

@ -0,0 +1,155 @@
#include <uefi.h>
#include "kernel/bootparam.h"
/*** ELF64 defines and structs ***/
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASS64 2 /* 64-bit objects */
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ET_EXEC 2 /* Executable file */
#define PT_LOAD 1 /* Loadable program segment */
#ifdef __x86_64__
#define EM_MACH 62 /* AMD x86-64 architecture */
#endif
#ifdef __aarch64__
#define EM_MACH 183 /* ARM aarch64 architecture */
#endif
typedef struct
{
uint8_t e_ident[16]; /* Magic number and other info */
uint16_t e_type; /* Object file type */
uint16_t e_machine; /* Architecture */
uint32_t e_version; /* Object file version */
uint64_t e_entry; /* Entry point virtual address */
uint64_t e_phoff; /* Program header table file offset */
uint64_t e_shoff; /* Section header table file offset */
uint32_t e_flags; /* Processor-specific flags */
uint16_t e_ehsize; /* ELF header size in bytes */
uint16_t e_phentsize; /* Program header table entry size */
uint16_t e_phnum; /* Program header table entry count */
uint16_t e_shentsize; /* Section header table entry size */
uint16_t e_shnum; /* Section header table entry count */
uint16_t e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
typedef struct
{
uint32_t p_type; /* Segment type */
uint32_t p_flags; /* Segment flags */
uint64_t p_offset; /* Segment file offset */
uint64_t p_vaddr; /* Segment virtual address */
uint64_t p_paddr; /* Segment physical address */
uint64_t p_filesz; /* Segment size in file */
uint64_t p_memsz; /* Segment size in memory */
uint64_t p_align; /* Segment alignment */
} Elf64_Phdr;
/**
* Load an ELF executable and pass control over for good
*/
int main(int argc, char **argv)
{
FILE *f;
char *buff;
long int size;
Elf64_Ehdr *elf;
Elf64_Phdr *phdr;
uintptr_t entry;
bootparam_t bootp;
efi_status_t status;
efi_guid_t gopGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
efi_gop_t *gop = NULL;
efi_gop_mode_info_t *info = NULL;
uintn_t isiz = sizeof(efi_gop_mode_info_t);
int i, j;
/* load the file */
if((f = fopen("\\0F_exit_bs\\kernel.elf", "r"))) {
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
buff = malloc(size + 1);
if(!buff) {
fprintf(stderr, "unable to allocate memory\n");
return 1;
}
fread(buff, size, 1, f);
fclose(f);
} else {
fprintf(stderr, "Unable to open file\n");
return 0;
}
/* set up boot parameters passed to the "kernel" */
memset(&bootp, 0, sizeof(bootparam_t));
status = BS->LocateProtocol(&gopGuid, NULL, (void**)&gop);
if(!EFI_ERROR(status) && gop) {
status = gop->SetMode(gop, 0);
ST->ConOut->Reset(ST->ConOut, 0);
ST->StdErr->Reset(ST->StdErr, 0);
if(EFI_ERROR(status)) {
fprintf(stderr, "unable to set video mode\n");
return 0;
}
bootp.framebuffer = (unsigned int*)gop->Mode->FrameBufferBase;
bootp.width = gop->Mode->Information->HorizontalResolution;
bootp.height = gop->Mode->Information->VerticalResolution;
bootp.pitch = sizeof(unsigned int) * gop->Mode->Information->PixelsPerScanLine;
} else {
fprintf(stderr, "unable to get graphics output protocol\n");
return 0;
}
if(argc > 1) {
bootp.argc = argc - 1;
bootp.argv = (char**)malloc(argc * sizeof(char*));
if(bootp.argv) {
for(i = 0; i < bootp.argc; i++)
if((bootp.argv[i] = (char*)malloc(strlen(argv[i + 1]) + 1)))
strcpy(bootp.argv[i], argv[i + 1]);
bootp.argv[i] = NULL;
}
}
/* is it a valid ELF executable for this architecture? */
elf = (Elf64_Ehdr *)buff;
if(!memcmp(elf->e_ident, ELFMAG, SELFMAG) && /* magic match? */
elf->e_ident[EI_CLASS] == ELFCLASS64 && /* 64 bit? */
elf->e_ident[EI_DATA] == ELFDATA2LSB && /* LSB? */
elf->e_type == ET_EXEC && /* executable object? */
elf->e_machine == EM_MACH && /* architecture match? */
elf->e_phnum > 0) { /* has program headers? */
/* load segments */
for(phdr = (Elf64_Phdr *)(buff + elf->e_phoff), i = 0;
i < elf->e_phnum;
i++, phdr = (Elf64_Phdr *)((uint8_t *)phdr + elf->e_phentsize)) {
if(phdr->p_type == PT_LOAD) {
memcpy((void*)phdr->p_vaddr, buff + phdr->p_offset, phdr->p_filesz);
memset((void*)(phdr->p_vaddr + phdr->p_filesz), 0, phdr->p_memsz - phdr->p_filesz);
}
}
entry = elf->e_entry;
} else {
fprintf(stderr, "not a valid ELF executable for this architecture\n");
return 0;
}
/* free resources */
free(buff);
/* exit this UEFI bullshit */
if(exit_bs()) {
fprintf(stderr,
"Ph'nglui mglw'nafh Chtulu R'lyeh wgah'nagl fhtagn\n"
"(Hastur has a hold on us and won't let us go)\n");
return 0;
}
/* execute the "kernel" */
(*((void(* __attribute__((sysv_abi)))(bootparam_t *))(entry)))(&bootp);
/* failsafe, should never return just in case */
while(1);
return 0;
}

View file

@ -0,0 +1,21 @@
ARCH = x86_64
TARGET = ../kernel.elf
SRCS = kernel.c
all: $(TARGET)
ifeq ($(USE_GCC),)
CC = clang -target $(ARCH)-elf
LD = ld.lld
else
CC = $(ARCH)-elf-gcc
LD = $(ARCH)-elf-ld
endif
# UEFI wastes lots and lots of memory. Link our "kernel" at an address (32M) which isn't used by UEFI
$(TARGET): kernel.c
$(CC) -ffreestanding -fno-stack-protector -fno-stack-check -D__$(ARCH)__ -I. -c $< -o kernel.o
$(LD) -nostdlib -z max-page-size=0x1000 -Ttext=0x01000000 kernel.o -o $(TARGET)
clean:
rm *.o $(TARGET) 2>/dev/null || true

View file

@ -0,0 +1,11 @@
/**
* Struct passed to the "kernel" from the exit_bs loader
*/
typedef struct {
unsigned int *framebuffer;
unsigned int width;
unsigned int height;
unsigned int pitch;
int argc;
char **argv;
} bootparam_t;

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

1
examples/0F_exit_bs/uefi Symbolic link
View file

@ -0,0 +1 @@
../../uefi