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
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
