On Fri, Jun 30, 2006 at 05:45:58PM +0900, Magnus Damm wrote:
> 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.
>
>
[..]
>
> + /* 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);
Looks like we are relocating everything including absolute symbols. As long
as absolute symbols are representing address, its fine (like __bss_start)
but if they happen to represent a value which is not supposed to change
due to loading of vmlinux at a different address, it will be an issue.
Probably we can skip relocating absolute symbols but then we shall have to
make sure none of the absolute symbols is representing a information which
is variable like section start addresss.
Eric had posted a patch in the past which made some the absolute symbols
section relative.
http://marc.theaimsgroup.com/?l=linux-kernel&m=112266670613361&w=2
[..]
> --- 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) \
Should this be --emit-reloc or --emit-relocs
Ok. So here we are trying to relocate an executable by retaining reloc
information.
FYI, In the past Eric had posted a working prototype where
he was compiling the kernel as a shared library (-shared) which was
compiled for physical address 1MB and relocating it if was loaded
at different address. He was doing relocation at kernel run time.
Not sure which approach is better but got a question. By definition, are
executable files supposed to be relocatable? Or we are just trying to
do some hack by treating an R_386_32 type relocation as R_386_RELATIVE
effectively.
Thinking aloud, How about compiling the kernel as shared library and then
performing the relocations in user space at load time instead of runtime.
[..]
> --- 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);
> + }
> +
The assumption that gloabl variable will come before local fails on
my machine and build fails.
Why don't you provide references to local symbols from a fixed global
symbol like _stext. (Eric did it in the past)
I put following code to progress through the build.
for (i = 0; i < table_cnt; i++) {
- printf("\tPTR\t%#llx\n", table[i].addr);
+ if ((toupper(table[i].sym[0]) != 'A') &&
+ (table[i].addr - _stext > 0)) {
+ printf("\tPTR\t_stext + %#llx\n",
+ table[i].addr - _stext);
+ } else {
+ printf("\tPTR\t%#llx\n", table[i].addr);
+ }
I am still trying to make this patch set work on my machine.
Thanks
Vivek
_______________________________________________
fastboot mailing list
[email protected]
https://lists.osdl.org/mailman/listinfo/fastboot