From 9a243b9ccd6f48dda83e40ebfe1988b75691f667 Mon Sep 17 00:00:00 2001
From: Armin Luntzer <armin.luntzer@univie.ac.at>
Date: Wed, 10 Oct 2018 11:48:20 +0200
Subject: [PATCH] * change API of apply_relocate_add() to accept ELF section
 name

* add ELF utility functions:
 - elf_get_symbol_shndx()
 - elf_get_common_size()
 - elf_get_common_objects()
 - elf_get_symbol_size()

* adapt module so that more complex executables may be loaded, i.e.

 - allocate "common" objects in relocatable executables
 - relocate ".data" sections
 - add elf utility fuctions

NOTE: these changes are an experimental mess at this time and must not be merged
      to master until properly tidied up
---
 arch/sparc/kernel/module.c |   9 +-
 include/kernel/elf.h       |   6 ++
 include/kernel/module.h    |   3 +-
 kernel/module.c            | 202 +++++++++++++++++++++++++++++++-----
 lib/elf.c                  | 207 ++++++++++++++++++++++++++++++++++++-
 5 files changed, 396 insertions(+), 31 deletions(-)

diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
index 1736686..2383114 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -16,11 +16,12 @@
  * @param m an ELF module
  * @param rel an ELF relocation entry
  * @param sym the address of the target symbol
+ * @param sec_name the name of the section to apply the relocation in
  *
  * return 0 on success
  */
 
-int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym)
+int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym, const char *sec_name)
 {
 	Elf_Addr rsym;
 
@@ -40,9 +41,13 @@ int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym)
 	if (!sym)
 		return -EINVAL;
 
+	if (!sec_name) {
+		pr_err("\tsec_name empty!\n");
+		return -EINVAL;
+	}
 
 
-	text = find_mod_sec(m, ".text");
+	text = find_mod_sec(m, sec_name);
 
 	loc8  = (uint8_t  *) (text->addr + rel->r_offset);
 	loc32 = (uint32_t *) loc8;
diff --git a/include/kernel/elf.h b/include/kernel/elf.h
index 4797c13..79739dc 100644
--- a/include/kernel/elf.h
+++ b/include/kernel/elf.h
@@ -568,6 +568,9 @@ int elf_header_check(Elf_Ehdr *ehdr);
 Elf_Shdr *elf_get_shdr(const Elf_Ehdr *ehdr);
 size_t elf_find_shdr_alloc_idx(const Elf_Ehdr *ehdr, const size_t offset);
 
+size_t elf_get_common_size(const Elf_Ehdr *ehdr);
+size_t elf_get_common_objects(const Elf_Ehdr *ehdr, char **objname);
+
 Elf_Shdr *elf_get_sec_by_idx(const Elf_Ehdr *ehdr, const size_t idx);
 Elf_Shdr *elf_get_sec_shstrtab(const Elf_Ehdr *ehdr);
 char *elf_get_shstrtab_str(const Elf_Ehdr *ehdr, size_t idx);
@@ -586,6 +589,7 @@ unsigned long elf_get_symbol_value(const Elf_Ehdr *ehdr,
 				   const char *name, unsigned long *value);
 
 unsigned long elf_get_symbol_type(const Elf_Ehdr *ehdr, const char *name);
+size_t elf_get_symbol_size(const Elf_Ehdr *ehdr, const char *name);
 
 size_t elf_find_sec_idx_by_type(const Elf_Ehdr *ehdr,
 				const uint32_t sh_type,
@@ -606,4 +610,6 @@ void elf_dump_symtab(const Elf_Ehdr *ehdr);
 void elf_dump_sections(const Elf_Ehdr *ehdr);
 
 
+unsigned short elf_get_symbol_shndx(const Elf_Ehdr *ehdr,
+				    const char *name);
 #endif /* _KERNEL_ELF_H_ */
diff --git a/include/kernel/module.h b/include/kernel/module.h
index e357eaf..dc3584d 100644
--- a/include/kernel/module.h
+++ b/include/kernel/module.h
@@ -62,7 +62,8 @@ struct elf_module {
 
 
 /* implemented in architecture code */
-int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym);
+int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym,
+		      const char *sec_name);
 
 
 struct module_section *find_mod_sec(const struct elf_module *m,
diff --git a/kernel/module.c b/kernel/module.c
index b15a5d7..aab0150 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -69,6 +69,28 @@ struct module_section *find_mod_sec(const struct elf_module *m,
 }
 
 
+/**
+ * @brief find module section by index
+ *
+ * @param m a struct elf_module
+ *
+ * @param idx the index of the module section
+ *
+ * @return module section structure pointer or NULL if not found
+ */
+
+struct module_section *find_mod_idx(const struct elf_module *m,
+				    size_t idx)
+
+{
+	if (idx >= m->num_sec)
+		return NULL;
+
+	return &m->sec[idx];
+}
+
+
+
 /**
  * @brief setup the module structure
  *
@@ -178,8 +200,13 @@ static int module_load_mem(struct elf_module *m)
 		sec = elf_get_sec_by_idx(m->ehdr, idx);
 
 
+#if 0
 		if (!sec->sh_size)	/* don't need those */
 			continue;
+#else
+		if (!(sec->sh_flags & SHF_ALLOC))
+			continue;
+#endif
 
 		s->size = sec->sh_size;
 
@@ -193,24 +220,27 @@ static int module_load_mem(struct elf_module *m)
 			goto error;
 
 		strcpy(s->name, src);
+		
+		pr_err(MOD "section %s index %d max %d\n", s->name, s, m->num_sec);
 
 
 		if (sec->sh_type & SHT_NOBITS) {
-			pr_debug(MOD "\tZero segment %10s at %p size %ld\n",
+			pr_info(MOD "\tZero segment %10s at %p size %ld\n",
 			       s->name, (char *) va_load,
 			       sec->sh_size);
 
 			bzero((void *) va_load, s->size);
 		} else {
-			pr_debug(MOD "\tCopy segment %10s from %p to %p size %ld\n",
+			pr_info(MOD "\tCopy segment %10s from %p to %p size %ld\n",
 			       s->name,
 			       (char *) m->ehdr + sec->sh_offset,
 			       (char *) va_load,
 			       sec->sh_size);
 
-			memcpy((void *) va_load,
-			       (char *) m->ehdr + sec->sh_offset,
-			       sec->sh_size);
+			if (sec->sh_size)
+				memcpy((void *) va_load,
+				       (char *) m->ehdr + sec->sh_offset,
+				       sec->sh_size);
 		}
 
 		s->addr = va_load;
@@ -219,10 +249,84 @@ static int module_load_mem(struct elf_module *m)
 		s++;
 
 		if (s > &m->sec[m->num_sec]) {
-			pr_debug(MOD "Error out of section memory\n");
+			pr_err(MOD "Error out of section memory\n");
+			goto error;
+		}
+	}
+
+#if 0
+	if (elf_get_common_size(m->ehdr)) {
+
+		m->num_sec++;
+		m->sec = (struct module_section *)
+			krealloc(m->sec, sizeof(struct module_section) * m->num_sec);
+
+		if (!m->sec)
+			goto error;
+
+		s = &m->sec[m->num_sec - 1];
+
+		s->size = elf_get_common_size(m->ehdr);
+		s->addr = (unsigned long) kzalloc(s->size);
+		if (!s->addr)
+			goto error;
+
+		pr_info(MOD "\tcreating .alloc section of %d bytes at %x\n", s->size, s->addr);
+
+		s->name = kmalloc(strlen(".alloc"));
+		if (!s->name)
+			goto error;
+
+		strcpy(s->name, ".alloc");
+
+	}
+#else
+	/* yes, yes, fixup like everything else here */
+	if (elf_get_common_objects(m->ehdr, NULL)) {
+		size_t cnt = elf_get_common_objects(m->ehdr, NULL);
+		char **obj;
+
+		m->sec = (struct module_section *)
+			krealloc(m->sec, sizeof(struct module_section) * (m->num_sec + cnt));
+
+		if (!m->sec)
 			goto error;
+
+
+		/* point to last */
+		s = &m->sec[m->num_sec];
+		/* and update */
+		m->num_sec += cnt;
+
+		obj = kmalloc(cnt * sizeof(char **));
+		if (!obj)
+			goto error;
+		/* load names */
+		elf_get_common_objects(m->ehdr, obj);
+
+
+		/* Allocate one section entry for each object, this is not
+		 * that efficient, but what the heck. Better not to have
+		 * uninitialised non-static globals at all!
+		 */
+		for (idx = 0; idx < cnt; idx++) {
+			s->size = elf_get_symbol_size(m->ehdr, obj[idx]);
+			s->addr = (unsigned long) kzalloc(s->size);
+			if (!s->addr)
+				goto error;
+			pr_debug(MOD "\tcreating \"%s\" section of %d bytes at %x\n", obj[idx], s->size, s->addr);
+			s->name = kmalloc(strlen(obj[idx]));
+			if (!s->name)
+				goto error;
+			strcpy(s->name, obj[idx]);
+			s++;
 		}
+
+		kfree(obj);
 	}
+#endif
+
+
 
 	return 0;
 
@@ -264,11 +368,12 @@ static int module_relocate(struct elf_module *m)
 		if (m->ehdr->e_type != ET_DYN)
 			return 0;
 
-
 	/* we only need RELA type relocations */
 
 	while (1) {
 
+		char *rel_sec;
+
 		idx = elf_find_sec_idx_by_type(m->ehdr, SHT_RELA, idx + 1);
 
 		if (!idx)
@@ -276,8 +381,16 @@ static int module_relocate(struct elf_module *m)
 
 		sec = elf_get_sec_by_idx(m->ehdr, idx);
 
-		pr_debug(MOD "\n"
-			MOD "Section Header info: %ld\n", sec->sh_info);
+		pr_info(MOD "\n"
+			MOD "Section Header info: %ld %s\n", sec->sh_info, elf_get_shstrtab_str(m->ehdr, idx));
+
+		if (!strcmp(elf_get_shstrtab_str(m->ehdr, idx), ".rela.text"))
+			rel_sec = ".text";
+		else if (!strcmp(elf_get_shstrtab_str(m->ehdr, idx), ".rela.data"))
+			rel_sec = ".data";
+		else
+			BUG();
+
 
 		if (sec) {
 
@@ -295,8 +408,8 @@ static int module_relocate(struct elf_module *m)
 				unsigned int symsec = ELF_R_SYM(relatab[i].r_info);
 				char *symstr = elf_get_symbol_str(m->ehdr, symsec);
 				struct module_section *s;
-				struct module_section *text  = find_mod_sec(m, ".text");
-				
+				struct module_section *text  = find_mod_sec(m, ".text"); /* ?? rel_sec ?? */
+
 				pr_debug(MOD "OFF: %08lx INF: %8lx ADD: %3ld LNK: %ld SEC: %d NAME: %s\n",
 				       relatab[i].r_offset,
 				       relatab[i].r_info,
@@ -308,23 +421,63 @@ static int module_relocate(struct elf_module *m)
 
 
 				if (strlen(symstr)) {
-					Elf_Addr sym = (Elf_Addr) lookup_symbol(symstr);
+						unsigned long symval;
+						Elf_Addr sym = (Elf_Addr) lookup_symbol(symstr);
 
-					if (!sym) {
+						if (!sym) {
+							pr_debug(MOD "\tSymbol %s not found in library, trying to resolving in module\n",
+								 symstr);
 
-						unsigned long symval;
-						pr_info(MOD "\tSymbol %s not found in library, trying to resolving in module\n",
-							 symstr);
+							if ((elf_get_symbol_type(m->ehdr, symstr) & STT_OBJECT)) {
+								char *secstr;
 
 
-						if (!(elf_get_symbol_type(m->ehdr, symstr) & STT_OBJECT)) {
-							pr_debug(MOD "\tERROR, object data resolution not yet implemented, symbol %s may not function as intended. If it is a variable, declare it static as a workaround\n", symstr);
-						}
 
+								s = find_mod_sec(m, elf_get_shstrtab_str(m->ehdr, elf_get_symbol_shndx(m->ehdr, symstr)));
+
+								switch (elf_get_symbol_shndx(m->ehdr, symstr)) {
+								case SHN_UNDEF:
+									pr_debug(MOD "\tundefined section index\n");
+									break;
+								case SHN_ABS:
+									pr_debug(MOD "\tabsolute value section index\n");
+								case SHN_COMMON:
+							     		pr_debug(MOD "\t %s common symbol index (must allocate) add: %d\n", symstr,
+										relatab[i].r_addend);
+									s = find_mod_sec(m, symstr);
+									if (!s) {
+										pr_err("no suitable section found");
+										return -1;
+									}
+									break;
+								default:
+									break;
+
+								}
+
+
+								if (!s) {
+									pr_debug(MOD "Error cannot locate section %s for symbol\n", secstr);
+									continue;
+								}
+								secstr = s->name;
+								/* target address to insert at location */
+								reladdr = (long) s->addr;
+							pr_debug(MOD "\tRelative symbol address: %x, entry at %08lx, section %s\n", reladdr, s->addr, secstr);
+
+							/* needed ?? */
+							//elf_get_symbol_value(m->ehdr, symstr, (unsigned long *) &relatab[i].r_addend);
+								apply_relocate_add(m, &relatab[i], reladdr, rel_sec);
+								continue;
+
+							}
+
+#if 0
 						if (!(elf_get_symbol_type(m->ehdr, symstr) & (STT_FUNC | STT_OBJECT))) {
 							pr_err(MOD "\tERROR, unresolved symbol %s\n", symstr);
 							return -1;
 						}
+#endif
 						if (!elf_get_symbol_value(m->ehdr, symstr, &symval)) {
 							pr_err(MOD "\tERROR, unresolved symbol %s\n", symstr);
 							return -1;
@@ -334,25 +487,26 @@ static int module_relocate(struct elf_module *m)
 
 					}
 
-					pr_debug(MOD "\tSymbol %s at %lx\n", symstr, sym);
+					pr_debug(MOD "\tSymbol %s at %lx val %lx sec %d\n", symstr, sym, symval, symsec);
 
-					apply_relocate_add(m, &relatab[i], sym);
+					apply_relocate_add(m, &relatab[i], sym, rel_sec);
 
 				} else  { /* no string, symtab entry is probably a section, try to identify it */
 
 					char *secstr = elf_get_shstrtab_str(m->ehdr, symsec);
 
-					s = find_mod_sec(m, secstr);
+					s = find_mod_idx(m, symsec-1);
 
 					if (!s) {
 						pr_debug(MOD "Error cannot locate section %s for symbol\n", secstr);
 							continue;
 					}
+					secstr = s->name;
 					/* target address to insert at location */
 					reladdr = (long) s->addr;
-					pr_debug(MOD "\tRelative symbol address: %x, entry at %08lx\n", reladdr, s->addr);
+					pr_debug(MOD "\tRelative symbol address: %x, entry at %08lx, section %s\n", reladdr, s->addr, secstr);
 
-					apply_relocate_add(m, &relatab[i], reladdr);
+					apply_relocate_add(m, &relatab[i], reladdr, rel_sec);
 				}
 
 				pr_debug(MOD "\n");
diff --git a/lib/elf.c b/lib/elf.c
index 89553fd..df56bb6 100644
--- a/lib/elf.c
+++ b/lib/elf.c
@@ -510,6 +510,102 @@ unsigned long elf_get_symbol_value(const Elf_Ehdr *ehdr,
 }
 
 
+/**
+ * @brief get the value of a symbol
+ *
+ * @param ehdr an Elf_Ehdr
+ *
+ * @param name the name of the symbol
+ *
+ * @return the size of the symbol (0 if not found or simply size 0)
+ *	   -1 on error
+ */
+
+size_t elf_get_symbol_size(const Elf_Ehdr *ehdr, const char *name)
+
+{
+	unsigned int i;
+	size_t sym_cnt;
+
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+
+	symtab = elf_find_sec(ehdr, ".symtab");
+
+	if (!symtab) {
+		pr_debug(MSG "WARN: no .symtab section found\n");
+		return -1;
+	}
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		pr_debug(MSG "Error %d != %ld\n",
+			 sizeof(Elf_Sym), symtab->sh_entsize);
+		return -1;
+	}
+
+	symbols = (Elf_Sym *) (((char *) ehdr) + symtab->sh_offset);
+
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+
+	for (i = 0; i < sym_cnt; i++) {
+		if(!strcmp(elf_get_symbol_str(ehdr, i), name))
+			return (size_t) symbols[i].st_size;
+	}
+
+	return 0;
+}
+
+
+/**
+ * @brief get the section index of a symbol
+ *
+ * @param ehdr an Elf_Ehdr
+ *
+ * @param name the name of the symbol
+ *
+ * @return the section index of the symbol or -1 if not found
+ */
+
+unsigned short elf_get_symbol_shndx(const Elf_Ehdr *ehdr,
+				    const char *name)
+
+{
+	unsigned int i;
+	size_t sym_cnt;
+
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+
+	symtab = elf_find_sec(ehdr, ".symtab");
+
+	if (!symtab) {
+		pr_debug(MSG "WARN: no .symtab section found\n");
+		return -1;
+	}
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		pr_debug(MSG "Error %d != %ld\n",
+			 sizeof(Elf_Sym), symtab->sh_entsize);
+		return -1;
+	}
+
+	symbols = (Elf_Sym *) (((char *) ehdr) + symtab->sh_offset);
+
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+
+	for (i = 0; i < sym_cnt; i++) {
+		if(!strcmp(elf_get_symbol_str(ehdr, i), name)) {
+			return symbols[i].st_shndx;
+		}
+	}
+
+	return -1;
+}
+
+
+
 /**
  * @brief get the ELF type of a symbol
  *
@@ -607,14 +703,114 @@ size_t elf_get_num_alloc_sections(const Elf_Ehdr *ehdr)
 	for (i = 0; i <ehdr->e_shnum; i++) {
 		shdr = elf_get_sec_by_idx(ehdr, i);
 		if ((shdr->sh_flags & SHF_ALLOC))
-			if (shdr->sh_size)
-				cnt++;
+			cnt++;
 	}
 
 	return cnt;
 }
 
 
+
+/* @brief get the total byte size of unallocated uninitialised common objects
+ *
+ * @param ehdr an Elf_Ehdr
+ *
+ * @return the number of bytes to be occupied by common (unallocated) objects
+ */
+
+size_t elf_get_common_size(const Elf_Ehdr *ehdr)
+{
+	unsigned int i;
+	size_t sym_cnt;
+
+
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+	size_t bytes = 0;
+
+
+	symtab = elf_find_sec(ehdr, ".symtab");
+
+	if (!symtab) {
+		pr_debug(MSG "WARN: no .symtab section found\n");
+		return 0;
+	}
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		pr_debug("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		return 0;
+	}
+
+	symbols = (Elf_Sym *) (((char *) ehdr) + symtab->sh_offset);
+
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+
+	for (i = 0; i < sym_cnt; i++) {
+		if (symbols[i].st_shndx == SHN_COMMON)
+			bytes += symbols[i].st_size;
+	}
+
+	return bytes;
+}
+
+
+/* @brief get the total count of unallocated uninitialised common objects
+ *
+ * @param ehdr an Elf_Ehdr
+ * @param[out] an index array
+ *
+ * @note set idx to NULL to determine the number of elements needed for the
+ *	 array
+ *
+ * @return the number of common objects
+ */
+
+size_t elf_get_common_objects(const Elf_Ehdr *ehdr, char **objname)
+{
+	unsigned int i;
+	size_t sym_cnt;
+
+
+	Elf_Shdr *symtab;
+	Elf_Sym *symbols;
+
+	size_t common = 0;
+
+
+	symtab = elf_find_sec(ehdr, ".symtab");
+
+	if (!symtab) {
+		pr_debug(MSG "WARN: no .symtab section found\n");
+		return 0;
+	}
+
+	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
+		pr_debug("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		return 0;
+	}
+
+	symbols = (Elf_Sym *) (((char *) ehdr) + symtab->sh_offset);
+
+	sym_cnt = symtab->sh_size / symtab->sh_entsize;
+
+	for (i = 0; i < sym_cnt; i++) {
+		if (symbols[i].st_shndx == SHN_COMMON) {
+			if (objname)
+				objname[common] = elf_get_symbol_str(ehdr, i);
+			common++;
+		}
+	}
+
+	return common;
+}
+
+
+
+
+
+
+
 /**
  * @brief get the number of entries in a dynamic section
  *
@@ -727,7 +923,8 @@ void elf_dump_symtab(const Elf_Ehdr *ehdr)
 	printk(MSG "\n"
 	       MSG ".symtab contains %d entries\n"
 	       MSG "============================\n"
-	       MSG "\t[NUM]\t[VALUE]\t\t\t[SIZE]\t[TYPE]\t[NAME]\n", sym_cnt);
+	       MSG "\t[NUM]\t[VALUE]\t\t\t[SIZE]\t[TYPE]\t[NAME]\t\t[SHIDX]\n",
+	       sym_cnt);
 
 
 	for (i = 0; i < sym_cnt; i++) {
@@ -756,7 +953,9 @@ void elf_dump_symtab(const Elf_Ehdr *ehdr)
 			printk("\tUNKNOWN"); break;
 		}
 
-		printk("\t%-10s\n", elf_get_symbol_str(ehdr, i));
+		printk("\t%-10s", elf_get_symbol_str(ehdr, i));
+
+		printk("\t%4ld\n", symbols[i].st_shndx);
 
 	}
 }
-- 
GitLab