From af3a2196117ffaea354087bcb1a6b20f5b26cfcc Mon Sep 17 00:00:00 2001
From: Sergiusz Bazanski <q3k@q3k.org>
Date: Sat, 27 Jul 2019 15:06:23 +0200
Subject: [PATCH] l0der: skip relocations for weak symbols

ld emits relocations for weak undefined symbols. We want to safely skip
them.
---
 epicardium/l0der/elf.h   | 17 ++++++-
 epicardium/l0der/l0der.c | 98 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 109 insertions(+), 6 deletions(-)

diff --git a/epicardium/l0der/elf.h b/epicardium/l0der/elf.h
index ce90fc27..9deb88ea 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 37e029ab..d1ec2188 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) {
-- 
GitLab