Browse Source

loading and running elf files

master
Mathieu Serandour 6 months ago
parent
commit
41435434ff
  1. 100
      kernel/entry.c
  2. 159
      kernel/lib/elf/elf.c
  3. 30
      kernel/lib/elf/elf.h
  4. 66
      kernel/lib/elf/spec.h
  5. 5
      programs/Makefile
  6. 7
      programs/prog0.c

100
kernel/entry.c

@ -28,22 +28,22 @@
#include "lib/sprintf.h"
#include "lib/string.h"
#include "lib/logging.h"
#include "lib/common.h"
#include "lib/registers.h"
#include "lib/dump.h"
#include "lib/stacktrace.h"
#include "lib/panic.h"
#include "lib/elf/elf.h"
#include "early_video.h"
// 8K stack
#define KERNEL_STACK_SIZE 8192
#define KERNEL_STACK_SIZE 8192 * 2
// accessible by other compilation units
// like panic.c
const size_t stack_size = KERNEL_STACK_SIZE;
uint8_t stack_base[KERNEL_STACK_SIZE] __attribute__((section(".stack"))) __align(16);
uint8_t stack_base[KERNEL_STACK_SIZE] __attribute__((section(".stack"))) __attribute__((aligned(16)));
#define INITIAL_STACK_PTR ((uintptr_t)(stack_base + KERNEL_STACK_SIZE))
@ -201,7 +201,51 @@ disk_part_t* find_main_part(struct stivale2_guid* part_guid) {
}
// Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling functio
static inline
void test_disk_overflow(void) {
file_handle_t* f = vfs_open_file("/////fs/boot/bg.bmp//");
const int bsize = 1024 * 1024 * 8;
const size_t size = 1024*1024*64;
uint8_t* buf = malloc(bsize);
for(int i = 0; i < bsize; i++)
buf[i] = i;
uint64_t time = clock();
for(int i = 0; i < size / bsize; i++) {
log_info("write %u (%u)", i * bsize, clock() - time);
time = clock();
size_t r = vfs_write_file(buf, bsize, 1, f);
assert(r == 1);
}
// check
//read
vfs_close_file(f);
f = vfs_open_file("/////fs/boot/bg.bmp//");
time = clock();
int rsize = bsize;
int i = 0;
while(vfs_read_file(buf, rsize, 1, f) == 1) {
int begin = i++ * rsize;
log_info("read %u (%u)", begin, clock() - time);
time = clock();
for(int j = begin; j < begin + rsize; j++)
assert(buf[j - begin] == (j & 0xff));
}
vfs_close_file(f);
free(buf);
}
// The following will be our kernel's entry point.
void _start(struct stivale2_struct *stivale2_struct) {
@ -292,14 +336,54 @@ void _start(struct stivale2_struct *stivale2_struct) {
disk_part_t* part = find_main_part((GUID*)&boot_volume_tag->part_guid);
assert(part);
assert(vfs_mount(part, "/fs/"));
int r = vfs_mount(part, "/fs/");
assert(r);
void* elf_file;
// open load and run elf file
file_handle_t* f = vfs_open_file("/fs/bin/prog0.elf");
assert(f);
vfs_seek_file(f, 0, SEEK_END);
size_t file_size = vfs_tell_file(f);
elf_file = malloc(file_size);
vfs_seek_file(f, 0, SEEK_SET);
int rd = vfs_read_file(elf_file, 1, file_size, f);
//sleep(10);
assert(rd == file_size);
vfs_close_file(f);
log_warn("fgr");
dump(
elf_file,
512,//file_size,
32,
DUMP_8
);
//test_disk_overflow();
elf_program_t* program = elf_load(elf_file, file_size);
assert(program);
int (*prog_entry)(int,char**) = program->main;
log_warn("main()=%u", prog_entry(0,NULL));
//printf("issou");
//log_info("%x allocated heap blocks", get_n_allocation());
log_info("%x allocated heap blocks", heap_get_n_allocation());
for(;;) {
asm volatile("hlt");

159
kernel/lib/elf/elf.c

@ -0,0 +1,159 @@
#include <stdint.h>
#include <stddef.h>
#include "elf.h"
#include "spec.h"
#include "../string.h"
#include "../logging.h"
#include "../dump.h"
#include "../../memory/paging.h"
#include "../../memory/heap.h"
/**
* @brief return 0 if invalid header
*
*/
static int check_elf_header(const ehdr_t* ehdr, size_t file_size) {
// file is too short
if(file_size < sizeof(ehdr_t))
return 0;
log_warn("checked %x", ehdr->magic);
dump(
ehdr,
sizeof(ehdr_t),
16,
DUMP_8
);
if(
ehdr->magic != 0x464c457f
|| ehdr->arch != 2
|| ehdr->endianess != 1
|| ehdr->header_version != 1
|| ehdr->abi != 0
|| ehdr->version != 1
|| ehdr->isa != 0x3E // x86-64
|| ehdr->ehdr_size != sizeof(ehdr_t)
|| ehdr->phdr_entry_size != sizeof(phdr_t)
)
return 0;
return 1;
}
static
unsigned convert_flags(unsigned p_flags) {
unsigned flags = 0;
if((p_flags & 1) == 0) {
// no execute
flags |= PL_XD;
}
if((p_flags & 2) == 0) {
// no write
flags |= PL_RW;
}
else if((p_flags & 4) == 0) {
// no read
log_info("ignoring elf not present readable flag");
}
return flags;
}
elf_program_t* elf_load(const void* file, size_t file_size) {
const ehdr_t* ehdr = file;
if(!check_elf_header(ehdr, file_size))
return NULL;
elf_program_t* prog = malloc(
sizeof(elf_program_t) +
sizeof(prog->segs[0]) * ehdr->phdr_table_size
);
prog->main = (void *)ehdr->program_entry;
unsigned j = 0;
assert(ehdr->phdr_entry_size == 56);
for(unsigned i = 0; i < ehdr->phdr_table_size; i++, j++) {
const phdr_t* phdr = file + ehdr->phdr_offset
+i * ehdr->phdr_entry_size;
log_warn("issou phdr->p_type=%u",phdr->p_type);
prog->segs[j].flags = convert_flags(phdr->flags);
prog->segs[j].length = phdr->p_memsz;
prog->segs[j].base = (void*)phdr->p_addr;
log_warn(
"i = %u\n"
"prog->segs[j].base=%lx \n"
"prog->segs[j].length >> 12=%lu \n"
"prog->segs[j].flag=%lu \n",
i,
prog->segs[j].base,
prog->segs[j].length >> 12,
prog->segs[j].flags
);
if(phdr->p_type != PT_LOAD) {
--j;
continue;
}
if(
phdr->p_offset + phdr->p_filesz > file_size
|| phdr->p_filesz > phdr->p_memsz
|| ((uint64_t)prog->segs[j].base & 0x0fff)
) {
// bad file
log_info("bad elf file");
free(prog);
return NULL;
}
alloc_pages(
prog->segs[j].base,
(prog->segs[j].length+0xfff) >> 12,
PRESENT_ENTRY//prog->segs[j].flags
);
memset(
prog->segs[j].base,
0,
phdr->p_memsz
);
memcpy(
prog->segs[j].base,
file + phdr->p_offset,
phdr->p_filesz
);
}
prog->n_segs = j;
prog = realloc(
prog,
sizeof(elf_program_t) +
sizeof(prog->segs[0]) * prog->n_segs
);
return prog;
}

30
kernel/lib/elf/elf.h

@ -0,0 +1,30 @@
#pragma once
// size of elf_program = sizeof(elf_program_t) + sizeof
typedef
struct elf_program {
struct segment {
void* base;
size_t length;
// page flags
// as defined in /memory/paging.h
unsigned flags;
};
// program entry
void* main;
size_t n_segs;
struct segment segs[0];
} elf_program_t;
elf_program_t* elf_load(const void* file, size_t file_size);

66
kernel/lib/elf/spec.h

@ -0,0 +1,66 @@
#pragma once
#include "../assert.h"
#define EI_NIDENT 16
typedef struct ehdr {
uint32_t magic; // 0-3 Magic number - 0x7F, then 'ELF' in ASCII
uint8_t arch; //1 = 32 bit, 2 = 64 bit
uint8_t endianess; //1 = little endian, 2 = big endian
uint8_t header_version; //ELF header version
uint8_t abi; // usually 0 for System V
uint8_t unused[8]; //
uint16_t type; //1 = relocatable, 2 = executable, 3 = shared, 4 = core
uint16_t isa; //Instruction set - see table below
uint32_t version; //ELF Version
uint64_t program_entry; //Program entry position
uint64_t phdr_offset; //Program header table position
uint64_t section_hdr_offset; //Section header table position
uint32_t flags; //Flags - architecture dependent; see note below
uint16_t ehdr_size; //Header size
uint16_t phdr_entry_size; //Size of an entry in the program header table
uint16_t phdr_table_size; //Number of entries in the program header table
uint16_t section_table_entry_size; //Size of an entry in the section header table
uint16_t section_table_size; //Number of entries in the section header table
uint16_t section_names_index; //Index in section header table with the section names
} __attribute__((packed)) ehdr_t;
typedef
struct phdr {
uint32_t p_type; //Type of segment
uint32_t flags; //Flags
uint64_t p_offset; //The offset in the file that the data for this segment can be found
uint64_t p_addr; //Where you should start to put this segment in virtual memory
uint64_t undefined; //Undefined for the System V ABI
uint64_t p_filesz; //Size of the segment in the file
uint64_t p_memsz; //Size of the segment in memory
uint64_t p_align; //The required alignment for this section (must be a power of 2)
} __attribute__((packed)) phdr_t;
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
/*
egment types: 0 = null - ignore the entry;
1 = load - clear p_memsz bytes at p_vaddr to 0, then copy p_filesz bytes from p_offset to p_vaddr;
2 = dynamic - requires dynamic linking;
3 = interp - contains a file path to an executable to use as an interpreter for the following segment;
4 = note section. There are more values, but mostly contain architecture/environment specific information, which is probably not required for the majority of ELF files.
Flags: 1 = executable, 2 = writable, 4 = readable.
*/
static_assert_equals(sizeof(ehdr_t), 64);
static_assert_equals(sizeof(phdr_t), 56);

5
programs/Makefile

@ -7,6 +7,7 @@ CFLAGS := -O0 -fno-inline -mno-red-zone -mgeneral-regs-only \
-ffreestanding
CFILES :=$(shell find -name "*.c")
EFILES := $(CFILES:.c=.elf)
@ -14,8 +15,8 @@ all: $(EFILES)
cp *.elf ../disk_root/bin/
%.elf: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(CC) $(CFLAGS) -c $< -o $@.o
$(LD) -static $@.o -o $@
clean:
rm -rf *.s *.elf

7
programs/prog0.c

@ -1,6 +1,9 @@
int x = 8;
const char* rodata = " zefgrbefvzegr ";
int g = 0;
int fibo(int v) {
if(v < 2)
@ -9,6 +12,6 @@ int fibo(int v) {
return fibo(v - 1);
}
int main(int argc, char** argv) {
return fibo(x);
int _start(int argc, char** argv) {
return 2;
}
Loading…
Cancel
Save