Hi all,

Here comes a working snapshot of some relocation code that I've been
hacking on lately. I've only tested the code in qemu-system-i386, but
x86_64 and other architectures may work too.

The attached kexec-tool patches extends the tool to support relocation
of elf binaries, ie "kdump -p" now relocates the vmlinux file to fit
whatever physical memory area that the user has reserved. The Linux
kernel needs to be patched to include relocation information, and an
early version of such a patch is attached.

The patches should be applied in the following order, on top of
kexec-tools-1.101:

kexec-tools-1.101-kdump9.patch (download this one from the kdump site)
kexec-tools-1.101-kdump-section_name.patch (generic fix for section
symbol names)
kexec-tools-1.101-kdump-kexec_elf_rel_parse.patch (generic minor fix)
kexec-tools-1.101-kdump-relocate.patch (the main implementation)

Then build the kernel with the attached kernel patch using "make
vmlinux". In the future there should be config options and a working
modpost utility - some things are busted right now. See this as an
early preview of something that could be much more polished if there
people are interested in it.

After building the kernel it should be possible to reboot into it
using the patched kexec tool:

# /kexec -p /vmlinux --append="console=ttyS0,115200 ip=on irqpoll
maxcpus=1" --args-linux --elf32-core-headers
Adjusting executable 0x1f00000 bytes
# echo c > /proc/sysrq-trigger
SysRq : Trigger a crashdump
....

Enjoy, comments are as always more than welcome!

/ magnus

Attachment: kexec-tools-1.101-kdump.series
Description: Binary data

--- 0002/kexec/kexec-elf-rel.c
+++ work/kexec/kexec-elf-rel.c	2006-06-30 17:22:55.000000000 +0900
@@ -302,6 +302,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, 
 		const unsigned char *strtab;
 		size_t rel_size;
 		const unsigned char *ptr, *rel_end;
+		const char *name;
 		if ((shdr->sh_type != SHT_RELA) && (shdr->sh_type != SHT_REL)) {
 			continue;
 		}
@@ -353,9 +354,18 @@ int elf_rel_load(struct mem_ehdr *ehdr, 
 
 			/* The relevant symbol */
 			sym = elf_sym(ehdr, symtab->sh_data + (rel.r_sym * elf_sym_size(ehdr)));
+
+			if (sym.st_name) {
+				name = strtab + sym.st_name;
+			}
+			else {
+				name = ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
+				name += ehdr->e_shdr[sym.st_shndx].sh_name;
+			}
+
 #ifdef DEBUG
 			fprintf(stderr, "sym: %10s info: %02x other: %02x shndx: %lx value: %lx size: %lx\n",
-				strtab + sym.st_name,
+				name,
 				sym.st_info,
 				sym.st_other,
 				sym.st_shndx,
@@ -373,30 +383,28 @@ int elf_rel_load(struct mem_ehdr *ehdr, 
 			 * So, is this really an error condition to flag die?
 			 */
 			/*
-				die("Undefined symbol: %s\n",
-					strtab + sym.st_name);
+				die("Undefined symbol: %s\n", name);
 			*/
 				continue;
 			}
 			sec_base = 0;
 			if (sym.st_shndx == SHN_COMMON) {
 				die("symbol: '%s' in common section\n",
-					strtab + sym.st_name);
+					name);
 			}
 			else if (sym.st_shndx == SHN_ABS) {
 				sec_base = 0;
 			}
 			else if (sym.st_shndx > ehdr->e_shnum) {
 				die("Invalid section: %d for symbol %s\n",
-					sym.st_shndx,
-					strtab + sym.st_name);
+				    sym.st_shndx, name);
 			}
 			else {
 				sec_base = ehdr->e_shdr[sym.st_shndx].sh_addr;
 			}
 #ifdef DEBUG
 			fprintf(stderr, "sym: %s value: %lx addr: %lx\n",
-				strtab + sym.st_name, value, address);
+				name, value, address);
 #endif
 			value = sym.st_value;
 			value += sec_base;
--- 0003/kexec/kexec-elf-rel.c
+++ work/kexec/kexec-elf-rel.c	2006-06-30 17:17:12.000000000 +0900
@@ -425,7 +425,7 @@ void elf_rel_build_load(struct kexec_inf
 	int result;
 
 	/* Parse the Elf file */
-	result = build_elf_rel_info((char *)purgatory, purgatory_size, ehdr);
+	result = build_elf_rel_info(buf, len, ehdr);
 	if (result < 0) {
 		die("ELF rel parse failed\n");
 	}
--- 0001/kexec/kexec-elf-exec.c
+++ work/kexec/kexec-elf-exec.c	2006-06-30 17:27:22.000000000 +0900
@@ -14,6 +14,7 @@ static const int probe_debug = 0;
 int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr)
 {
 	struct mem_phdr *phdr, *end_phdr;
+	struct mem_shdr *shdr, *end_shdr;
 	int result;
 	result = build_elf_info(buf, len, ehdr);
 	if (result < 0) {
@@ -43,6 +44,15 @@ int build_elf_exec_info(const char *buf,
 		}
 	}
 
+	result = 0;
+	end_shdr = &ehdr->e_shdr[ehdr->e_shnum];
+	for(shdr = ehdr->e_shdr; shdr != end_shdr; shdr++) {
+		if (shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL) {
+			result++;
+		}
+	}
+
+	ehdr->has_reloc = result;
 	return 0;
 }
 
@@ -63,7 +73,7 @@ int elf_exec_load(const struct mem_ehdr 
 	 * and then find a location for it in memory.
 	 */
 	base = 0;
-	if (ehdr->e_type == ET_DYN) {
+	if (ehdr->e_type == ET_DYN || ehdr->has_reloc) {
 		unsigned long first, last, align;
 		first = ULONG_MAX;
 		last  = 0;
@@ -79,10 +89,10 @@ int elf_exec_load(const struct mem_ehdr 
 			}
 			start = phdr->p_paddr;
 			stop  = start + phdr->p_memsz;
-			if (start > first) {
-				start = first;
+			if (start < first) {
+				first = start;
 			}
-			if (last < stop) {
+			if (stop > last) {
 				last = stop;
 			}
 			if (align < phdr->p_align) {
@@ -95,7 +105,7 @@ int elf_exec_load(const struct mem_ehdr 
 		if (!valid_memory_range(first, last)) {
 			unsigned long hole;
 			hole = locate_hole(info,
-				last - first + 1, align, 
+				last - first, align, 
 				0, elf_max_addr(ehdr), 1);
 			if (hole == ULONG_MAX) {
 				result = -1;
@@ -107,7 +117,6 @@ int elf_exec_load(const struct mem_ehdr 
 			 */
 			base = hole - first;
 		}
-
 	}
 
 	/* Read in the PT_LOAD segments */
@@ -126,6 +135,18 @@ int elf_exec_load(const struct mem_ehdr 
 			phdr->p_data, size,
 			phdr->p_paddr + base, phdr->p_memsz);
 	}
+
+	if (base != 0) {
+		printf("Adjusting executable 0x%lx bytes\n", base);
+		result = elf_rel_adj(ehdr, info, base);
+	}
+	if (result < 0) {
+		fprintf(stderr, "Unable to adjust executable\n");
+		result = -1;
+		goto out;
+	}
+
+
 	result = 0;
  out:
 	return result;
--- 0004/kexec/kexec-elf-rel.c
+++ work/kexec/kexec-elf-rel.c	2006-06-30 17:28:11.000000000 +0900
@@ -168,6 +168,136 @@ int build_elf_rel_info(const char *buf, 
 }
 
 
+int elf_rel_adj(struct mem_ehdr *ehdr, struct kexec_info *info,
+		unsigned long adj)
+{
+	struct mem_phdr *phdr, *end_phdr;
+	struct mem_shdr *shdr, *shdr_end;
+	int result;
+
+	if (!ehdr->e_shdr) {
+		fprintf(stderr, "No section header?\n");
+		result = -1;
+		goto out;
+	}
+	shdr_end = &ehdr->e_shdr[ehdr->e_shnum];
+
+	/* Now that the load address is known apply relocations */
+	for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
+		struct mem_shdr *section, *symtab;
+		const unsigned char *strtab;
+		size_t rel_size;
+		const unsigned char *ptr, *rel_end;
+		const char *name;
+		if ((shdr->sh_type != SHT_RELA) && (shdr->sh_type != SHT_REL)) {
+			continue;
+		}
+		if ((shdr->sh_info > ehdr->e_shnum) ||
+			(shdr->sh_link > ehdr->e_shnum))
+		{
+			die("Invalid section number\n");
+		}
+		section = &ehdr->e_shdr[shdr->sh_info];
+		symtab = &ehdr->e_shdr[shdr->sh_link];
+
+		if (symtab->sh_link > ehdr->e_shnum) {
+			/* Invalid section number? */
+		  printf("Invalid section number?\n");
+			continue;
+		}
+		strtab = ehdr->e_shdr[symtab->sh_link].sh_data;
+
+		rel_size = 0;
+		if (shdr->sh_type == SHT_REL) {
+			rel_size = elf_rel_size(ehdr);
+		}
+		else if (shdr->sh_type == SHT_RELA) {
+			rel_size = elf_rela_size(ehdr);
+		}
+		else {
+			die("Cannot find elf rel size\n");
+		}
+		rel_end = shdr->sh_data + shdr->sh_size;
+		for(ptr = shdr->sh_data; ptr < rel_end; ptr += rel_size) {
+			struct mem_rela rel;
+			struct mem_sym sym;
+			const void *location;
+			unsigned long address, value;
+			if (shdr->sh_type == SHT_REL) {
+				rel = elf_rel(ehdr, ptr);
+			}
+			else if (shdr->sh_type == SHT_RELA) {
+				rel = elf_rela(ehdr, ptr);
+			}
+
+			/* the location to change */
+			location = section->sh_data;
+			location += rel.r_offset - section->sh_addr;
+
+			/* The relevant symbol */
+			sym = elf_sym(ehdr, symtab->sh_data + (rel.r_sym * elf_sym_size(ehdr)));
+
+			if (sym.st_name) {
+				name = strtab + sym.st_name;
+			}
+			else {
+				name = ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
+				name += ehdr->e_shdr[sym.st_shndx].sh_name;
+			}
+
+#ifdef DEBUG
+			fprintf(stderr, "sym: %10s info: %02x other: %02x shndx: %lx value: %lx size: %lx\n",
+				name,
+				sym.st_info,
+				sym.st_other,
+				sym.st_shndx,
+				sym.st_value,
+				sym.st_size);
+
+#endif
+			if (sym.st_shndx == STN_UNDEF) {
+			/*
+			 * NOTE: ppc64 elf .ro shows up a  UNDEF section.
+			 * From Elf 1.2 Spec:
+			 * Relocation Entries: If the index is STN_UNDEF,
+			 * the undefined symbol index, the relocation uses 0
+			 * as the "symbol value".
+			 * So, is this really an error condition to flag die?
+			 */
+			/*
+				die("Undefined symbol: %s\n", name);
+			*/
+				continue;
+			}
+			value = adj;
+			address = adj;
+#ifdef DEBUG
+			fprintf(stderr, "sym: %s value: %lx addr: %lx\n",
+				name, value, address);
+#endif
+			machine_apply_elf_rel(ehdr, rel.r_type,
+				(void *)location, address, value);
+		}
+	}
+
+	ehdr->e_entry += adj;
+	info->entry = (void *)ehdr->e_entry;
+
+	for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
+		shdr->sh_addr += adj;
+	}
+
+	end_phdr = &ehdr->e_phdr[ehdr->e_phnum];
+	for(phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) {
+		phdr->p_paddr += adj;
+		phdr->p_vaddr += adj;
+	}
+
+	result = 0;
+ out:
+	return result;
+}
+
 int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
 	unsigned long min, unsigned long max, int end)
 {
--- 0001/kexec/kexec-elf.h
+++ work/kexec/kexec-elf.h	2006-06-30 17:22:56.000000000 +0900
@@ -21,6 +21,7 @@ struct mem_ehdr {
 	struct mem_shdr *e_shdr;
 	struct mem_note *e_note;
 	unsigned long rel_addr, rel_size;
+	unsigned long has_reloc;
 };
 
 struct mem_phdr {
@@ -90,6 +91,8 @@ extern int build_elf_exec_info(const cha
 extern int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr);
 
 extern int elf_exec_load(const struct mem_ehdr *ehdr, struct kexec_info *info);
+extern int elf_rel_adj(struct mem_ehdr *ehdr, struct kexec_info *info,
+	unsigned long adj);
 extern int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
 	unsigned long min, unsigned long max, int end);
 
--- 0003/Makefile
+++ work/Makefile	2006-06-30 12:57:25.000000000 +0900
@@ -573,7 +573,7 @@ vmlinux-lds  := arch/$(ARCH)/kernel/vmli
 # May be overridden by arch/$(ARCH)/Makefile
 quiet_cmd_vmlinux__ ?= LD      $@
       cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ \
-      -T $(vmlinux-lds) $(vmlinux-init)                          \
+      --emit-reloc -T $(vmlinux-lds) $(vmlinux-init)             \
       --start-group $(vmlinux-main) --end-group                  \
       $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) FORCE ,$^)
 
--- 0002/scripts/kallsyms.c
+++ work/scripts/kallsyms.c	2006-06-30 12:57:43.000000000 +0900
@@ -37,6 +37,7 @@
 struct sym_entry {
 	unsigned long long addr;
 	unsigned int len;
+	unsigned char *orig_sym;
 	unsigned char *sym;
 };
 
@@ -131,6 +132,7 @@ static int read_symbol(FILE *in, struct 
 	}
 	strcpy((char *)s->sym + 1, str);
 	s->sym[0] = stype;
+	s->orig_sym = strdup(s->sym);
 
 	return 0;
 }
@@ -266,8 +268,25 @@ static void write_src(void)
 	printf(".data\n");
 
 	output_label("kallsyms_addresses");
+	k = -1;
+
 	for (i = 0; i < table_cnt; i++) {
-		printf("\tPTR\t%#llx\n", table[i].addr);
+		if (isupper(table[i].orig_sym[0])) {
+			k = i;
+		}
+		if (k == -1) {
+			fprintf(stderr, "kallsyms failure: "
+				"local variable present before global\n");
+			exit(EXIT_FAILURE);
+		}
+
+		printf("\tPTR\t%s",table[k].orig_sym + 1);
+		if (table[i].addr - table[k].addr) {
+			printf(" + %#llx /* %s */",
+			       table[i].addr - table[k].addr,
+			       table[i].orig_sym + 1);
+ 		}
+		printf("\n");
 	}
 	printf("\n");
 
_______________________________________________
fastboot mailing list
[email protected]
https://lists.osdl.org/mailman/listinfo/fastboot

Reply via email to