diff --git a/epicardium/l0der/elf.h b/epicardium/l0der/elf.h index ce90fc270aef8b145d583c15cd93a4eafeadee40..9deb88ea8faae7487bb2e943e8bdb89b81329fde 100644 --- a/epicardium/l0der/elf.h +++ b/epicardium/l0der/elf.h @@ -70,6 +70,21 @@ typedef struct { #define SHT_RELA 4 #define SHT_REL 9 +#define SHT_DYNSYM 11 + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) + +#define STB_WEAK 2 typedef struct { Elf32_Addr r_offset; @@ -79,7 +94,7 @@ typedef struct { #define ELF32_R_SYM(i) ((i)>>8) #define ELF32_R_TYPE(i) ((unsigned char)(i)) -#define R_ARM_RELATIVE 23 +#define R_ARM_RELATIVE 0x17 typedef struct { Elf32_Word p_type; diff --git a/epicardium/l0der/l0der.c b/epicardium/l0der/l0der.c index 37e029ab7084d7f1e5f59c5bd0310d10a4bf4c5a..d1ec2188e1c2a03b3191e1623944bef8d2ee7941 100644 --- a/epicardium/l0der/l0der.c +++ b/epicardium/l0der/l0der.c @@ -28,6 +28,8 @@ * a memory map in stone. */ +#define WEAK_SYMBOL_MAX 128 + struct _pie_load_info { /// Populated by _load_pie // Addresses within ELF file. @@ -36,6 +38,11 @@ struct _pie_load_info { // Highest alignment request for a segment. uint32_t strictest_alignment; + /// Populated by _parse_dynamic_symbols + // List of weak symbols for which relocations can be ignored. + uint32_t weak_symbols[WEAK_SYMBOL_MAX]; + uint32_t weak_symbol_count; + /// Populated by _get_load_addr // Load address of ELF file. uint32_t load_address; @@ -301,6 +308,67 @@ static int _load_segment(FIL *fp, struct _pie_load_info *li, Elf32_Phdr *phdr) return _seek_and_read(fp, phdr->p_offset, (void*)segment_start, phdr->p_filesz); } +/* + * Parse dynamic symbol sections. + */ +static int _parse_dynamic_symbols(FIL *fp, struct _pie_load_info *li, Elf32_Ehdr *hdr) { + int res; + FRESULT fres; + Elf32_Shdr shdr; + Elf32_Sym sym; + + // Go through all dynamic symbol sections. + for (int i = 0; i < hdr->e_shnum; i++) { + uint32_t shdr_addr = hdr->e_shoff + (i * hdr->e_shentsize); + if ((res = _read_section_header(fp, shdr_addr, &shdr)) != 0) { + return res; + } + + if (shdr.sh_type != SHT_DYNSYM) { + continue; + } + + if ((res = _check_section_header(fp, &shdr)) != 0) { + return res; + } + + if ((shdr.sh_size % sizeof(Elf32_Sym)) != 0) { + LOG_ERR("l0der", "_parse_dynamic_symbols: SHT_DYN section with invalid size: %ld", shdr.sh_size); + return -EIO; + } + uint32_t sym_count = shdr.sh_size / sizeof(Elf32_Sym); + + + // Read symbols one by one. + if ((fres = f_lseek(fp, shdr.sh_offset)) != FR_OK) { + LOG_ERR("l0der", "_parse_dynamic_symbols: seek to first relocation (at 0x%lx) failed", shdr.sh_offset); + return -EIO; + } + + for (int j = 0; j < sym_count; j++) { + unsigned int read; + if ((fres = f_read(fp, &sym, sizeof(Elf32_Sym), &read)) != FR_OK || read != sizeof(Elf32_Sym)) { + LOG_ERR("l0der", "__parse_dynamic_symbols: symbol read failed: %d", fres); + return -EIO; + } + + uint32_t bind = ELF32_ST_BIND(sym.st_info); + if (bind != STB_WEAK) { + continue; + } + + if (li->weak_symbol_count >= WEAK_SYMBOL_MAX) { + LOG_ERR("l0der", "__parse_dynamic_symbols: too many weak symbols (limit: %d)", WEAK_SYMBOL_MAX); + return -ENOMEM; + } + + li->weak_symbols[li->weak_symbol_count++] = j; + } + } + + return 0; +} + /* * Apply dynamic relocations from ELF. * @@ -354,9 +422,27 @@ static int _run_relocations(FIL *fp, struct _pie_load_info *li, Elf32_Ehdr *hdr) return -EIO; } + uint32_t sym = ELF32_R_SYM(rel.r_info); uint8_t type = ELF32_R_TYPE(rel.r_info); + + // Skip relocations that are for weak symbols. + // (ie., do not resolve relocation - they default to a safe NULL) + uint8_t skip = 0; + if (sym != 0) { + for (int k = 0; k < li->weak_symbol_count; k++) { + if (li->weak_symbols[k] == sym) { + skip = 1; + break; + } + } + } + if (skip) { + continue; + } + switch (type) { case R_ARM_RELATIVE: + // Relocate. if ((rel.r_offset % 4) != 0) { LOG_ERR("l0der", "_run_relocations: R_ARM_RELATIVE address must be 4-byte aligned"); return -ENOEXEC; @@ -388,11 +474,8 @@ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info) int res; struct _pie_load_info li = {0}; - // First pass over program headers: sanity check sizes and calculate - // memory image bounds. l0der currently only supports loading the image into - // the core1 address space, that is from 0x1008_0000 to 0x1010_0000. Thus, - // we need to ensure that all the LOADable segments can fit within this - // range. + // First pass over program headers: sanity check sizes, calculate image + // size bounds, check alignment. li.image_start = 0xffffffff; li.image_limit = 0x0; @@ -477,6 +560,11 @@ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info) } } + // Load dynamic symbols. + if ((res = _parse_dynamic_symbols(fp, &li, hdr)) != 0) { + return res; + } + // Run relocations. if ((res = _run_relocations(fp, &li, hdr)) != 0) {