int elf_x86_load(int argc, char **argv, const char *buf, off_t len, 
	struct kexec_info *info)
{
	struct mem_ehdr ehdr;
	char *command_line = NULL, *modified_cmdline = NULL;
	const char *append = NULL;
	char *tmp_cmdline = NULL;
	char *error_msg = NULL;
	int result;
	int command_line_len;
	const char *ramdisk;
	unsigned long entry, max_addr;
	int arg_style;
#define ARG_STYLE_ELF   0
#define ARG_STYLE_LINUX 1
#define ARG_STYLE_NONE  2
	int opt;

	/* See options.h -- add any more there, too. */
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{ "command-line",	1, NULL, OPT_APPEND },
		{ "append",		1, NULL, OPT_APPEND },
		{ "reuse-cmdline",	0, NULL, OPT_REUSE_CMDLINE },
		{ "initrd",		1, NULL, OPT_RAMDISK },
		{ "ramdisk",		1, NULL, OPT_RAMDISK },
		{ "args-elf",		0, NULL, OPT_ARGS_ELF },
		{ "args-linux",		0, NULL, OPT_ARGS_LINUX },
		{ "args-none",		0, NULL, OPT_ARGS_NONE },
		{ 0, 			0, NULL, 0 },
	};

	static const char short_options[] = KEXEC_OPT_STR "";

	/*
	 * Parse the command line arguments
	 */
	arg_style = ARG_STYLE_ELF;
	ramdisk = 0;
	result = 0;
	while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
		switch(opt) {
		default:
			/* Ignore core options */
			if (opt < OPT_ARCH_MAX) {
				break;
			}
		case OPT_APPEND:
			append = optarg;
			break;
		case OPT_REUSE_CMDLINE:
			tmp_cmdline = get_command_line();
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_ARGS_ELF: 
			arg_style = ARG_STYLE_ELF;
			break;
		case OPT_ARGS_LINUX:
			arg_style = ARG_STYLE_LINUX;
			break;
		case OPT_ARGS_NONE:
#ifdef __i386__
			arg_style = ARG_STYLE_NONE;
#else
			die("--args-none only works on arch i386\n");
#endif
			break;
		}
	}
	command_line = concat_cmdline(tmp_cmdline, append);
	if (tmp_cmdline) {
		free(tmp_cmdline);
	}
	command_line_len = 0;
	if (command_line) {
		command_line_len = strlen(command_line) +1;
	} else {
	    command_line = strdup("\0");
	    command_line_len = 1;
	}

	/* Need to append some command line parameters internally in case of
	 * taking crash dumps.
	 */
	if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) {
		modified_cmdline = xmalloc(COMMAND_LINE_SIZE);
		memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE);
		if (command_line) {
			strncpy(modified_cmdline, command_line,
						COMMAND_LINE_SIZE);
			modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
		}
	}

	/* Load the ELF executable */
	elf_exec_build_load(info, &ehdr, buf, len, 0);

	entry = ehdr.e_entry;
	max_addr = elf_max_addr(&ehdr);

	/* Do we want arguments? */
	if (arg_style != ARG_STYLE_NONE) {
		/* Load the setup code */
		elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
			0, ULONG_MAX, 1, 0);
	}
	if (arg_style == ARG_STYLE_NONE) {
		info->entry = (void *)entry;

	}
	else if (arg_style == ARG_STYLE_ELF) {
		unsigned long note_base;
		struct entry32_regs regs;
		uint32_t arg1, arg2;

		/* Setup the ELF boot notes */
		note_base = elf_boot_notes(info, max_addr,
					   command_line, command_line_len);

		/* Initialize the stack arguments */
		arg2 = 0; /* No return address */
		arg1 = note_base;
		elf_rel_set_symbol(&info->rhdr, "stack_arg32_1", &arg1, sizeof(arg1));
		elf_rel_set_symbol(&info->rhdr, "stack_arg32_2", &arg2, sizeof(arg2));
		
		/* Initialize the registers */
		elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
		regs.eip = entry;       /* The entry point */
		regs.esp = elf_rel_get_addr(&info->rhdr, "stack_arg32_2");
		elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));

		if (ramdisk) {
			error_msg = "Ramdisks not supported with generic elf arguments";
			goto out;
		}
	}
	else if (arg_style == ARG_STYLE_LINUX) {
		struct x86_linux_faked_param_header *hdr;
		unsigned long param_base;
		const char *ramdisk_buf;
		off_t ramdisk_length;
		struct entry32_regs regs;
		int rc = 0;

		/* Get the linux parameter header */
		hdr = xmalloc(sizeof(*hdr));

		/* Hack: With some ld versions, vmlinux program headers show
		 * a gap of two pages between bss segment and data segment
		 * but effectively kernel considers it as bss segment and
		 * overwrites the any data placed there. Hence bloat the
		 * memsz of parameter segment to 16K to avoid being placed
		 * in such gaps.
		 * This is a makeshift solution until it is fixed in kernel
		 */
		param_base = add_buffer(info, hdr, sizeof(*hdr), 16*1024,
			16, 0, max_addr, 1);

		/* Initialize the parameter header */
		memset(hdr, 0, sizeof(*hdr));
		init_linux_parameters(&hdr->hdr);

		/* Add a ramdisk to the current image */
		ramdisk_buf = NULL;
		ramdisk_length = 0;
		if (ramdisk) {
			ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
		}

		/* If panic kernel is being loaded, additional segments need
		 * to be created. */
		if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) {
			rc = load_crashdump_segments(info, modified_cmdline,
						max_addr, 0);
			if (rc < 0) {
				result = -1;
				goto out;
			}
			/* Use new command line. */
			free(command_line);
			command_line = modified_cmdline;
			command_line_len = strlen(modified_cmdline) + 1;
			modified_cmdline = NULL;
		}

		/* Tell the kernel what is going on */
		setup_linux_bootloader_parameters(info, &hdr->hdr, param_base, 
			offsetof(struct x86_linux_faked_param_header, command_line),
			command_line, command_line_len,
			ramdisk_buf, ramdisk_length);

		/* Fill in the information bios calls would usually provide */
		setup_linux_system_parameters(info, &hdr->hdr);

		/* Initialize the registers */
		elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
		regs.ebx = 0;		/* Bootstrap processor */
		regs.esi = param_base;	/* Pointer to the parameters */
		regs.eip = entry;	/* The entry point */
		regs.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
		elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
	}
	else {
Ejemplo n.º 2
0
int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
	struct kexec_info *info)
{
	struct mem_ehdr ehdr;
	const char *command_line, *ramdisk=0, *vmm=0, *kernel_buf;
	char *ramdisk_buf = NULL;
	off_t ramdisk_size = 0, kernel_size;
	unsigned long command_line_len;
	unsigned long entry, max_addr, gp_value;
	unsigned long command_line_base, ramdisk_base, image_base;
	unsigned long efi_memmap_base, efi_memmap_size;
	unsigned long boot_param_base;
	unsigned long noio=0;
	int result;
	int opt;
	char *efi_memmap_buf, *boot_param;
	static const struct option options[] = {
		KEXEC_ARCH_OPTIONS
		{"command-line", 1, 0, OPT_APPEND},
		{"append",       1, 0, OPT_APPEND},
		{"initrd",       1, 0, OPT_RAMDISK},
		{"noio",         0, 0, OPT_NOIO},
		{"vmm",          1, 0, OPT_VMM},
		{0, 0, 0, 0},
	};

	static const char short_options[] = KEXEC_ARCH_OPT_STR "";

	command_line = 0;
	while ((opt = getopt_long(argc, argv, short_options,
				  options, 0)) != -1) {
		switch (opt) {
		default:
			/* Ignore core options */
			if (opt < OPT_ARCH_MAX) {
				break;
			}
		case '?':
			usage();
			return -1;
		case OPT_APPEND:
			command_line = optarg;
			break;
		case OPT_RAMDISK:
			ramdisk = optarg;
			break;
		case OPT_NOIO:	/* disable PIO and MMIO in purgatory code*/
			noio = 1;
			break;
		case OPT_VMM:
			vmm = optarg;
			break;
		}
	}
	command_line_len = 0;
	if (command_line) {
		command_line_len = strlen(command_line) + 16;
	}

	if (vmm)
		kernel_buf = slurp_decompress_file(vmm, &kernel_size);
	else {
		kernel_buf = buf;
		kernel_size = len;
	}

	/* Parse the Elf file */
	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
	if (result < 0) {
		fprintf(stderr, "ELF parse failed\n");
		free_elf_info(&ehdr);
		return result;
	}

	if (info->kexec_flags & KEXEC_ON_CRASH ) {
		if ((mem_min == 0x00) && (mem_max == ULONG_MAX)) {
			fprintf(stderr, "Failed to find crash kernel region "
				"in %s\n", proc_iomem());
			free_elf_info(&ehdr);
			return -1;
		}
		move_loaded_segments(info, &ehdr, mem_min);
	} else if (update_loaded_segments(info, &ehdr) < 0) {
		fprintf(stderr, "Failed to place kernel\n");
		return -1;
	}

	entry = ehdr.e_entry;
	max_addr = elf_max_addr(&ehdr);

	/* Load the Elf data */
	result = elf_exec_load(&ehdr, info);
	if (result < 0) {
		fprintf(stderr, "ELF load failed\n");
		free_elf_info(&ehdr);
		return result;
	}


	/* Load the setup code */
	elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
			0x0, ULONG_MAX, -1, 0);


	if (load_crashdump_segments(info, &ehdr, max_addr, 0,
				&command_line) < 0)
		return -1;

	// reverve 4k for ia64_boot_param
	boot_param = xmalloc(4096);
        boot_param_base = add_buffer(info, boot_param, 4096, 4096, 4096, 0,
                        max_addr, -1);

	elf_rel_set_symbol(&info->rhdr, "__noio",
			   &noio, sizeof(long));

        elf_rel_set_symbol(&info->rhdr, "__boot_param_base",
                        &boot_param_base, sizeof(long));

	// reserve efi_memmap of actual size allocated in production kernel
	efi_memmap_size = saved_efi_memmap_size;
	efi_memmap_buf = xmalloc(efi_memmap_size);
	efi_memmap_base = add_buffer(info, efi_memmap_buf,
			efi_memmap_size, efi_memmap_size, 4096, 0,
			max_addr, -1);

	elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base",
			&efi_memmap_base, sizeof(long));

	elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size",
			&efi_memmap_size, sizeof(long));
	if (command_line) {
		command_line_len = strlen(command_line) + 1;
	}
	if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) {
		char *cmdline = xmalloc(command_line_len);
		strcpy(cmdline, command_line);

		if (info->kexec_flags & KEXEC_ON_CRASH) {
			char buf[128];
			sprintf(buf," max_addr=%lluM min_addr=%lluM",
					mem_max>>20, mem_min>>20);
			command_line_len = strlen(cmdline) + strlen(buf) + 1;
			cmdline = xrealloc(cmdline, command_line_len);
			strcat(cmdline, buf);
		}
Ejemplo n.º 3
0
int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
{
	unsigned long base;
	int result;
	int i;

	if (!ehdr->e_phdr) {
		fprintf(stderr, "No program header?\n");
		result = -1;
		goto out;
	}

	/* If I have a dynamic executable find it's size
	 * and then find a location for it in memory.
	 */
	base = 0;
	if (ehdr->e_type == ET_DYN) {
		unsigned long first, last, align;
		first = ULONG_MAX;
		last  = 0;
		align = 0;
		for(i = 0; i < ehdr->e_phnum; i++) {
			unsigned long start, stop;
			struct mem_phdr *phdr;
			phdr = &ehdr->e_phdr[i];
			if ((phdr->p_type != PT_LOAD) ||
				(phdr->p_memsz == 0))
			{
				continue;
			}
			start = phdr->p_paddr;
			stop  = start + phdr->p_memsz;
			if (first > start) {
				first = start;
			}
			if (last < stop) {
				last = stop;
			}
			if (align < phdr->p_align) {
				align = phdr->p_align;
			}
		}
		/* If I can't use the default paddr find a new
		 * hole for the dynamic executable.
		 */
		if (!valid_memory_range(info, first, last)) {
			unsigned long hole;
			hole = locate_hole(info,
				last - first + 1, align, 
				0, elf_max_addr(ehdr), 1);
			if (hole == ULONG_MAX) {
				result = -1;
				goto out;
			}
			/* Base is the value that when added
			 * to any virtual address in the file
			 * yields it's load virtual address.
			 */
			base = hole - first;
		}

	}

	/* Read in the PT_LOAD segments */
	for(i = 0; i < ehdr->e_phnum; i++) {
		struct mem_phdr *phdr;
		size_t size;
		phdr = &ehdr->e_phdr[i];
		if (phdr->p_type != PT_LOAD) {
			continue;
		}
		size = phdr->p_filesz;
		if (size > phdr->p_memsz) {
			size = phdr->p_memsz;
		}
		add_segment(info,
			phdr->p_data, size,
			phdr->p_paddr + base, phdr->p_memsz);
	}

	/* Update entry point to reflect new load address*/
	ehdr->e_entry += base;

	result = 0;
 out:
	return result;
}
Ejemplo n.º 4
0
int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
	unsigned long min, unsigned long max, int end)
{
	struct mem_shdr *shdr, *shdr_end, *entry_shdr;
	unsigned long entry;
	int result;
	unsigned char *buf;
	unsigned long buf_align, bufsz, bss_align, bsssz, bss_pad;
	unsigned long buf_addr, data_addr, bss_addr;

	if (max > elf_max_addr(ehdr)) {
		max = elf_max_addr(ehdr);
	}
	if (!ehdr->e_shdr) {
		fprintf(stderr, "No section header?\n");
		result = -1;
		goto out;
	}
	shdr_end = &ehdr->e_shdr[ehdr->e_shnum];

	/* Find which section entry is in */
	entry_shdr = NULL;
	entry = ehdr->e_entry;
	for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
		if (!(shdr->sh_flags & SHF_ALLOC)) {
			continue;
		}
		if (!(shdr->sh_flags & SHF_EXECINSTR)) {
			continue;
		}
		/* Make entry section relative */
		if ((shdr->sh_addr <= ehdr->e_entry) &&
			((shdr->sh_addr + shdr->sh_size) > ehdr->e_entry)) {
			entry_shdr = shdr;
			entry -= shdr->sh_addr;
			break;
		}
	}

	/* Find the memory footprint of the relocatable object */
	buf_align = 1;
	bss_align = 1;
	bufsz = 0;
	bsssz = 0;
	for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
		if (!(shdr->sh_flags & SHF_ALLOC)) {
			continue;
		}
		if (shdr->sh_type != SHT_NOBITS) {
			unsigned long align;
			align = shdr->sh_addralign;
			/* See if I need more alignment */
			if (buf_align < align) {
				buf_align = align;
			}
			/* Now align bufsz */
			bufsz = (bufsz + (align - 1)) & ~(align - 1);
			/* And now add our buffer */
			bufsz += shdr->sh_size;
		}
		else {
			unsigned long align;
			align = shdr->sh_addralign;
			/* See if I need more alignment */
			if (bss_align < align) {
				bss_align = align;
			}
			/* Now align bsssz */
			bsssz = (bsssz + (align - 1)) & ~(align -1);
			/* And now add our buffer */
			bsssz += shdr->sh_size;
		}
	}
	if (buf_align < bss_align) {
		buf_align = bss_align;
	}
	bss_pad = 0;
	if (bufsz & (bss_align - 1)) {
		bss_pad = bss_align - (bufsz & (bss_align - 1));
	}

	/* Allocate where we will put the relocated object */
	buf = xmalloc(bufsz);
	buf_addr = add_buffer(info, buf, bufsz, bufsz + bss_pad + bsssz,
		buf_align, min, max, end);
	ehdr->rel_addr = buf_addr;
	ehdr->rel_size = bufsz + bss_pad + bsssz;

	/* Walk through and find an address for each SHF_ALLOC section */
	data_addr = buf_addr;
	bss_addr  = buf_addr + bufsz + bss_pad;
	for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
		unsigned long align;
		if (!(shdr->sh_flags & SHF_ALLOC)) {
			continue;
		}
		align = shdr->sh_addralign;
		if (shdr->sh_type != SHT_NOBITS) {
			unsigned long off;
			/* Adjust the address */
			data_addr = (data_addr + (align - 1)) & ~(align -1);

			/* Update the section */
			off = data_addr - buf_addr;
			memcpy(buf + off, shdr->sh_data, shdr->sh_size);
			shdr->sh_addr = data_addr;
			shdr->sh_data = buf + off;

			/* Advance to the next address */
			data_addr += shdr->sh_size;
		} else {
			/* Adjust the address */
			bss_addr = (bss_addr + (align - 1)) & ~(align -1);

			/* Update the section */
			shdr->sh_addr = bss_addr;

			/* Advance to the next address */
			bss_addr += shdr->sh_size;
		}
	}
	/* Compute the relocated value for entry, and load it */
	if (entry_shdr) {
		entry += entry_shdr->sh_addr;
		ehdr->e_entry = entry;
	}
	info->entry = (void *)entry;

	/* 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;
		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 (!(section->sh_flags & SHF_ALLOC)) {
			continue;
		}

		if (symtab->sh_link > ehdr->e_shnum) {
			/* Invalid section number? */
			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;
			const unsigned char *name;
			unsigned long address, value, sec_base;
			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 + rel.r_offset;

			/* The final address of that location */
			address = section->sh_addr + rel.r_offset;

			/* 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;
			}
			sec_base = 0;
			if (sym.st_shndx == SHN_COMMON) {
				die("symbol: '%s' in common section\n",
					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, name);
			}
			else {
				sec_base = ehdr->e_shdr[sym.st_shndx].sh_addr;
			}
#ifdef DEBUG
			fprintf(stderr, "sym: %s value: %lx addr: %lx\n",
				name, value, address);
#endif
			value = sym.st_value;
			value += sec_base;
			value += rel.r_addend;
			machine_apply_elf_rel(ehdr, rel.r_type,
				(void *)location, address, value);
		}
	}
	result = 0;
 out:
	return result;
}