diff --git a/os/sb/host/sbelf.c b/os/sb/host/sbelf.c index 3b0331cd5..e020e58ee 100644 --- a/os/sb/host/sbelf.c +++ b/os/sb/host/sbelf.c @@ -38,6 +38,8 @@ /* Relevant section types.*/ #define SHT_PROGBITS 1U +#define SHT_SYMTAB 2U +#define SHT_NOBITS 8U #define SHT_REL 9U /* Relevant section flags.*/ @@ -46,6 +48,12 @@ #define SHF_EXECINSTR (1U << 2) #define SHF_INFO_LINK (1U << 6) +/* Special section indices.*/ +#define SHN_UNDEF 0U + +#define ELF32_R_SYM(v) ((v) >> 8) +#define ELF32_R_TYPE(v) ((v) & 0xFFU) + /*===========================================================================*/ /* Module exported variables. */ /*===========================================================================*/ @@ -54,6 +62,28 @@ /* Module local types. */ /*===========================================================================*/ +/** + * @brief Type of a section number. + */ +typedef unsigned elf_secnum_t; + +/** + * @brief Type of a symbol number. + */ +typedef unsigned elf_symnum_t; + +/** + * @brief Type of a loadable section info structure. + */ +typedef struct { + elf_secnum_t section; + uint32_t address; + size_t bits_size; + vfs_offset_t bits_off; + size_t rel_size; + vfs_offset_t rel_off; +} elf_loadable_info_t; + /** * @brief Type of an ELF loader context. */ @@ -63,10 +93,16 @@ typedef struct elf_load_context { uint8_t *next; uint32_t entry; - uint16_t shnum; - vfs_offset_t shoff; - vfs_offset_t stringsoff; - uint64_t loaded; + elf_secnum_t sections_num; + vfs_offset_t sections_off; + elf_symnum_t symbols_num; + vfs_offset_t symbols_off; + elf_loadable_info_t loadable_code; + elf_loadable_info_t loadable_const; + elf_loadable_info_t loadable_data; +#if 0 +// vfs_offset_t strings_off; +#endif } elf_load_context_t; /** @@ -102,6 +138,20 @@ typedef struct { uint32_t sh_entsize; } elf32_section_header_t; +typedef struct { + uint32_t r_offset; + uint32_t r_info; +} elf32_rel_t; + +typedef struct { + uint32_t st_name; + uint32_t st_value; + uint32_t st_size; + uint8_t st_info; + uint8_t st_other; + uint16_t st_shndx; +} elf32_symbol_t; + /*===========================================================================*/ /* Module local variables. */ /*===========================================================================*/ @@ -134,37 +184,218 @@ static msg_t read_section_name(elf_load_context_t *ctxp, static msg_t read_section_header(elf_load_context_t *ctxp, elf32_section_header_t *shp, - unsigned index) { + elf_secnum_t index) { msg_t ret; ret = vfsSetFilePosition(ctxp->fnp, - ctxp->shoff + ((vfs_offset_t)index * - (vfs_offset_t)sizeof (elf32_section_header_t)), + ctxp->sections_off + ((vfs_offset_t)index * + (vfs_offset_t)sizeof (elf32_section_header_t)), VFS_SEEK_SET); CH_RETURN_ON_ERROR(ret); return vfsReadFile(ctxp->fnp, (void *)shp, sizeof (elf32_section_header_t)); } -static msg_t load_section(elf_load_context_t *ctxp, - elf32_section_header_t *shp) { +static msg_t allocate_section(elf_load_context_t *ctxp, + elf32_section_header_t *shp) { + uint8_t *load_address; - (void)ctxp; - (void)shp; + /* Checking if the section can fit into the destination memory area.*/ + load_address = ctxp->map->base + shp->sh_addr; + if (!chMemIsAreaWithinX(ctxp->map, + (const void *)load_address, + (size_t)shp->sh_size)) { + return CH_RET_ENOMEM; + } + + return CH_RET_SUCCESS; +} + +#if 0 +static msg_t reloc_section(elf_load_context_t *ctxp, + elf32_section_header_t *shp) { + elf32_relocation_t *rbuf; + msg_t ret; + + rbuf = (elf32_relocation_t *)(void *)vfs_buffer_take(); + + do { + size_t size, remaining_size = (size_t)shp->sh_size; + + ret = vfsSetFilePosition(ctxp->fnp, (vfs_offset_t)shp->sh_offset, VFS_SEEK_SET); + CH_BREAK_ON_ERROR(ret); + + /* Reading the relocation section data.*/ + while (remaining_size > 0U) { + unsigned i, n; + + /* Reading relocation data using buffers in order to not make continuous + calls to the FS which could be unbuffered.*/ + if (remaining_size > VFS_BUFFERS_SIZE) { + size = VFS_BUFFERS_SIZE; + } + else { + size = remaining_size; + } + + ret = vfsReadFile(ctxp->fnp, (void *)rbuf, size); + if (CH_RET_IS_ERROR(ret)) { + goto yes_it_is_a_goto; + } + + /* Number of relocation entries in the buffer.*/ + n = (unsigned)ret / (unsigned)sizeof (elf32_relocation_t); + for (i = 0U; i < n; i++) { + ret = reloc_entry(ctxp, &rbuf[i]); + if (CH_RET_IS_ERROR(ret)) { + goto yes_it_is_a_goto; + } + } + + remaining_size -= size; + } + + ret = CH_RET_SUCCESS; + } while (false); + +yes_it_is_a_goto: + + vfs_buffer_release((char *)rbuf); + + return ret; +} +#endif + +static msg_t reloc_entry(elf_load_context_t *ctxp, + elf_loadable_info_t *lip, + elf32_rel_t *rp) { + vfs_offset_t oldoff, symoff; + elf_symnum_t symnum; + uint8_t *relocation_address; + msg_t ret; + elf32_symbol_t symbol; + + /* Saving current file position.*/ + oldoff = vfsGetFilePosition(ctxp->fnp); + + /* Checking for a symbol number overflow.*/ + symnum = (elf_symnum_t)ELF32_R_SYM(rp->r_info); + if (symnum > ctxp->symbols_num) { + return CH_RET_ENOEXEC; + } + + /* Moving file pointer to the symbol then reading it.*/ + symoff = ctxp->symbols_off + ((vfs_offset_t)symnum * + (vfs_offset_t)sizeof (elf32_symbol_t)); + ret = vfsSetFilePosition(ctxp->fnp, symoff, VFS_SEEK_SET); + CH_RETURN_ON_ERROR(ret); + ret = vfsReadFile(ctxp->fnp, (void *)&symbol, sizeof (elf32_symbol_t)); + CH_RETURN_ON_ERROR(ret); + + /* Undefined symbols are not handled, this is a loader not a linker.*/ + if (symbol.st_shndx == SHN_UNDEF) { + return CH_RET_ENOEXEC; + } + + /* Symbols must be associated to this specific section.*/ + if (lip->section != (elf_symnum_t)symbol.st_shndx) { + return CH_RET_ENOEXEC; + } + + /* Relocation point address.*/ + relocation_address = ctxp->map->base + lip->address + symbol.st_value; + if (!chMemIsAreaWithinX(ctxp->map, + (const void *)relocation_address, + sizeof (uint32_t))) { + return CH_RET_ENOMEM; + } + + /* Handling the various relocation point types.*/ + switch (ELF32_R_TYPE(rp->r_info)) { + + } + + ret = vfsSetFilePosition(ctxp->fnp, oldoff, VFS_SEEK_SET); + CH_RETURN_ON_ERROR(ret); return CH_RET_SUCCESS; } static msg_t reloc_section(elf_load_context_t *ctxp, - elf32_section_header_t *shp) { + elf_loadable_info_t *lip) { + elf32_rel_t *rbuf; + size_t size, done_size, remaining_size; + msg_t ret; - (void)ctxp; - (void)shp; + rbuf = (elf32_rel_t *)(void *)vfs_buffer_take(); + + /* Reading the relocation section data.*/ + remaining_size = lip->rel_size; + done_size = 0U; + while (remaining_size > 0U) { + unsigned i, n; + + /* Reading relocation data using buffers in order to not make continuous + calls to the FS which could be unbuffered.*/ + if (remaining_size > VFS_BUFFERS_SIZE) { + size = VFS_BUFFERS_SIZE; + } + else { + size = remaining_size; + } + + /* Reading a buffer-worth of relocation data.*/ + ret = vfsSetFilePosition(ctxp->fnp, + lip->rel_off + (vfs_offset_t)done_size, + VFS_SEEK_SET); + CH_BREAK_ON_ERROR(ret); + ret = vfsReadFile(ctxp->fnp, (void *)rbuf, size); + CH_BREAK_ON_ERROR(ret); + + /* Number of relocation entries in the buffer.*/ + n = (unsigned)ret / (unsigned)sizeof (elf32_rel_t); + for (i = 0U; i < n; i++) { + ret = reloc_entry(ctxp, lip, &rbuf[i]); + CH_BREAK_ON_ERROR(ret); + } + CH_BREAK_ON_ERROR(ret); + + remaining_size -= size; + done_size += size; + } + + vfs_buffer_release((char *)rbuf); + + return ret; +} + +static msg_t load_relocate_section(elf_load_context_t *ctxp, + elf_loadable_info_t *lip) { + uint8_t *load_address; + msg_t ret; + + /* Checking if the section can fit into the destination memory area.*/ + load_address = ctxp->map->base + lip->address; + if (!chMemIsAreaWithinX(ctxp->map, + (const void *)load_address, + lip->bits_size)) { + return CH_RET_ENOMEM; + } + + /* Loading the section data into the final memory area.*/ + if (lip->bits_size > 0U) { + ret = vfsSetFilePosition(ctxp->fnp, lip->bits_off, VFS_SEEK_SET); + CH_RETURN_ON_ERROR(ret); + ret = vfsReadFile(ctxp->fnp, (void *)load_address, lip->bits_size); + CH_RETURN_ON_ERROR(ret); + } + + ret = reloc_section(ctxp, lip); + CH_RETURN_ON_ERROR(ret); return CH_RET_SUCCESS; } static msg_t init_elf_context(elf_load_context_t *ctxp, - elf32_section_header_t *shp, vfs_file_node_c *fnp, memory_area_t *map) { static uint8_t elf32_header[16] = { @@ -174,6 +405,7 @@ static msg_t init_elf_context(elf_load_context_t *ctxp, elf32_header_t h; msg_t ret; + /* Context fully cleared.*/ memset((void *)ctxp, 0, sizeof (elf_load_context_t)); /* Initializing the fixed part of the context.*/ @@ -195,13 +427,9 @@ static msg_t init_elf_context(elf_load_context_t *ctxp, /* TODO more consistency checks.*/ /* Storing info required later.*/ - ctxp->entry = h.e_entry; - ctxp->shnum = h.e_shnum; - ctxp->shoff = (vfs_offset_t)h.e_shoff; - - /* Reading the header of the section containing the section names.*/ - ret = read_section_header(ctxp, shp, (unsigned)h.e_shstrndx); - ctxp->stringsoff = (vfs_offset_t)shp->sh_offset; + ctxp->entry = h.e_entry; + ctxp->sections_num = (unsigned)h.e_shnum; + ctxp->sections_off = (vfs_offset_t)h.e_shoff; return CH_RET_SUCCESS; } @@ -211,66 +439,123 @@ static msg_t init_elf_context(elf_load_context_t *ctxp, /*===========================================================================*/ msg_t sbElfLoad(vfs_file_node_c *fnp, memory_area_t *map) { - elf_load_context_t ctx; - elf32_section_header_t sh; - unsigned i; msg_t ret; do { - ret = init_elf_context(&ctx, &sh, fnp, map); + elf_load_context_t ctx; + elf_loadable_info_t *lip; + elf_secnum_t i; + + ret = init_elf_context(&ctx, fnp, map); CH_BREAK_ON_ERROR(ret); - /* Iterating through sections.*/ - for (i = 0U; i < (unsigned)ctx.shnum; i++) { -// char name[8]; + /* Iterating through sections, loading.*/ + for (i = 0U; i < ctx.sections_num; i++) { + elf32_section_header_t sh; + /* Reading the header.*/ ret = read_section_header(&ctx, &sh, i); CH_BREAK_ON_ERROR(ret); -#if 0 - ret = read_section_name(&ctx, &sh, name, sizeof name); - CH_BREAK_ON_ERROR(ret); + /* Discovery phase, scanning section headers and gathering data.*/ + switch (sh.sh_type) { + case SHT_PROGBITS: + /* Loadable section type, we allow for one executable, one data and + one constants sections.*/ + if ((sh.sh_flags & SHF_ALLOC) != 0U) { - /* Handling the various sections.*/ - if (strcmp(name, ".text") == (size_t)0) { - ret = load_section(&ctx, &sh, i); - } - else if (strcmp(name, ".rodata") == (size_t)0) { - ret = load_section(&ctx, &sh, i); - } - else if (strcmp(name, ".data") == (size_t)0) { - ret = load_section(&ctx, &sh, i); - } - else if (strcmp(name, ".bss") == (size_t)0) { + if ((sh.sh_flags & SHF_EXECINSTR) != 0U) { + /* Executable code section.*/ + lip = &ctx.loadable_code; + } + else if ((sh.sh_flags & SHF_WRITE) != 0U) { + /* Writable data section.*/ + lip = &ctx.loadable_data; + } + else { + /* Constants data section.*/ + lip = &ctx.loadable_const; + } - } -#endif + /* Multiple sections of same kind.*/ + if (lip->section != 0U) { + return CH_RET_ENOEXEC; + } - /* Sections to be loaded.*/ - if ((sh.sh_type == SHT_PROGBITS) && ((sh.sh_flags & SHF_ALLOC) != 0U)) { + lip->section = i; + lip->address = sh.sh_addr; + lip->bits_size = (size_t)sh.sh_size; + lip->bits_off = (vfs_offset_t)sh.sh_offset; - /* Loading sections with index greater than 63 is not supported.*/ - if (i >= 64U) { - return CH_RET_ENOMEM; + } + break; + + case SHT_NOBITS: + /* Uninitialized data section, we can have more than one, just checking + address ranges.*/ + if ((sh.sh_flags & SHF_ALLOC) != 0U) { + ret = allocate_section(&ctx, &sh); + CH_RETURN_ON_ERROR(ret); + } + break; + + case SHT_REL: + if ((sh.sh_flags & SHF_INFO_LINK) != 0U) { + elf_secnum_t sn = (elf_secnum_t)sh.sh_link; + + if (sn == ctx.loadable_code.section) { + /* Executable code section.*/ + lip = &ctx.loadable_code; + } + else if (sn == ctx.loadable_data.section) { + /* Writable data section.*/ + lip = &ctx.loadable_data; + } + else if (sn == ctx.loadable_const.section) { + /* Constants data section.*/ + lip = &ctx.loadable_const; + } + else { + /* Ignoring other relocation sections.*/ + break; + } + + /* Multiple relocation sections associated to the same section.*/ + if (lip->rel_size != 0U) { + return CH_RET_ENOEXEC; + } + + lip->rel_size = sh.sh_size; + lip->rel_off = (vfs_offset_t)sh.sh_offset; + } + break; + + case SHT_SYMTAB: + /* Symbols section, this is required for relocation.*/ + + if (ctx.symbols_num != 0U) { + /* Multiple symbol sections.*/ + return CH_RET_ENOEXEC; } - ret = load_section(&ctx, &sh); - CH_RETURN_ON_ERROR(ret); + ctx.symbols_num = (elf_symnum_t)sh.sh_size / + (elf_symnum_t)sizeof (elf32_symbol_t); + ctx.symbols_off = (vfs_offset_t)sh.sh_offset; + break; - ctx.loaded |= (1ULL << i); + default: + /* Ignoring other section types.*/ + break; } - /* Sections to be relocated, must refer to a loaded section.*/ - if ((sh.sh_type == SHT_REL) && - ((sh.sh_flags & SHF_INFO_LINK) != 0U) && - ((ctx.loaded & (1ULL << sh.sh_info)) != 0ULL)) { - - ret = reloc_section(&ctx, &sh); - CH_RETURN_ON_ERROR(ret); - - } + /* Loading phase.*/ + ret = load_relocate_section(&ctx, &ctx.loadable_code); + CH_RETURN_ON_ERROR(ret); + ret = load_relocate_section(&ctx, &ctx.loadable_data); + CH_RETURN_ON_ERROR(ret); + ret = load_relocate_section(&ctx, &ctx.loadable_const); + CH_RETURN_ON_ERROR(ret); } - } while (false); return ret; diff --git a/os/vfs/include/vfsbuffers.h b/os/vfs/include/vfsbuffers.h index 0f6c90795..cd56778dd 100644 --- a/os/vfs/include/vfsbuffers.h +++ b/os/vfs/include/vfsbuffers.h @@ -33,6 +33,11 @@ /* Module constants. */ /*===========================================================================*/ +/** + * @brief Size of the shared buffers. + */ +#define VFS_BUFFERS_SIZE (VFS_CFG_PATHLEN_MAX + 1) + /*===========================================================================*/ /* Module pre-compile time settings. */ /*===========================================================================*/ diff --git a/os/vfs/src/vfsbuffers.c b/os/vfs/src/vfsbuffers.c index c303b4dcf..4214a91c4 100644 --- a/os/vfs/src/vfsbuffers.c +++ b/os/vfs/src/vfsbuffers.c @@ -55,7 +55,7 @@ static struct { * @brief Shared path buffers. */ char path_buffers[VFS_CFG_PATHBUFS_NUM] - [VFS_CFG_PATHLEN_MAX + 1]; + [VFS_BUFFERS_SIZE]; } vfs_buffers_static; /*===========================================================================*/ @@ -74,7 +74,7 @@ static struct { void __vfs_buffers_init(void) { chGuardedPoolObjectInit(&vfs_buffers_static.path_buffers_pool, - VFS_CFG_PATHLEN_MAX + 1); + VFS_BUFFERS_SIZE); chGuardedPoolLoadArray(&vfs_buffers_static.path_buffers_pool, &vfs_buffers_static.path_buffers[0], VFS_CFG_PATHBUFS_NUM);