blob: bea7b3e3e61881d5d2dd802d9243d55f9a9e7ae7 [file] [log] [blame]
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "elf.h"
#include <boot/elf_boot.h>
#include "kexec.h"
#include "kexec-elf.h"
static const int probe_debug = 0;
static void load_elf_segments(struct mem_ehdr *ehdr, struct kexec_info *info, unsigned long base)
{
size_t i;
/* 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);
}
}
static int get_elf_exec_load_base(struct mem_ehdr *ehdr, struct kexec_info *info,
unsigned long min, unsigned long max,
unsigned long align, unsigned long *base)
{
unsigned long first, last;
size_t i;
/* Note on arm64:
* arm64's vmlinux has virtual address in physical address
* field of PT_LOAD segments. So the following validity check
* and relocation makes no sense on arm64.
*/
if (ehdr->e_machine == EM_AARCH64)
return 0;
first = ULONG_MAX;
last = 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 ((max - min) < (last - first))
return -1;
if (!valid_memory_range(info, min > first ? min : first, max < last ? max : last)) {
unsigned long hole;
hole = locate_hole(info, last - first + 1, align, min, max, 1);
if (hole == ULONG_MAX)
return -1;
/* Base is the value that when added
* to any virtual address in the file
* yields it's load virtual address.
*/
*base = hole - first;
}
return 0;
}
int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
uint32_t flags)
{
struct mem_phdr *phdr, *end_phdr;
int result;
result = build_elf_info(buf, len, ehdr, flags);
if (result < 0) {
return result;
}
if ((ehdr->e_type != ET_EXEC) && (ehdr->e_type != ET_DYN) &&
(ehdr->e_type != ET_CORE)) {
/* not an ELF executable */
if (probe_debug) {
fprintf(stderr, "Not ELF type ET_EXEC or ET_DYN\n");
}
return -1;
}
if (!ehdr->e_phdr) {
/* No program header */
fprintf(stderr, "No ELF program header\n");
return -1;
}
end_phdr = &ehdr->e_phdr[ehdr->e_phnum];
for(phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) {
/* Kexec does not support loading interpreters.
* In addition this check keeps us from attempting
* to kexec ordinay executables.
*/
if (phdr->p_type == PT_INTERP) {
fprintf(stderr, "Requires an ELF interpreter\n");
return -1;
}
}
return 0;
}
int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
{
unsigned long base;
int result;
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) {
result = get_elf_exec_load_base(ehdr, info, 0, elf_max_addr(ehdr), 0 /* align */, &base);
if (result < 0)
goto out;
}
load_elf_segments(ehdr, info, base);
/* Update entry point to reflect new load address*/
ehdr->e_entry += base;
result = 0;
out:
return result;
}
int elf_exec_load_relocatable(struct mem_ehdr *ehdr, struct kexec_info *info,
unsigned long reloc_min, unsigned long reloc_max,
unsigned long align)
{
unsigned long base;
int result;
if (reloc_min > reloc_max) {
fprintf(stderr, "Bad relocation range, start=%lux > end=%lux.\n", reloc_min, reloc_max);
result = -1;
goto out;
}
if (!ehdr->e_phdr) {
fprintf(stderr, "No program header?\n");
result = -1;
goto out;
}
base = 0;
result = get_elf_exec_load_base(ehdr, info, reloc_min, reloc_max, align, &base);
if (result < 0)
goto out;
load_elf_segments(ehdr, info, base);
/* Update entry point to reflect new load address*/
ehdr->e_entry += base;
result = 0;
out:
return result;
}
void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
const char *buf, off_t len, uint32_t flags)
{
int result;
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, ehdr, flags);
if (result < 0) {
die("ELF exec parse failed\n");
}
/* Load the Elf data */
result = elf_exec_load(ehdr, info);
if (result < 0) {
die("ELF exec load failed\n");
}
}
void elf_exec_build_load_relocatable(struct kexec_info *info, struct mem_ehdr *ehdr,
const char *buf, off_t len, uint32_t flags,
unsigned long reloc_min, unsigned long reloc_max,
unsigned long align)
{
int result;
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, ehdr, flags);
if (result < 0) {
die("%s: ELF exec parse failed\n", __func__);
}
/* Load the Elf data */
result = elf_exec_load_relocatable(ehdr, info, reloc_min, reloc_max, align);
if (result < 0) {
die("%s: ELF exec load failed\n", __func__);
}
}