blob: a88b5979f2f6d4756662ccc2511ed118a83d8d05 [file] [log] [blame]
/* Copyright (c) 2017 Google Inc.
* See LICENSE for details.
*
* ELF loading. */
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <libelf.h>
/* load_elf loads and ELF file. This is almost always a kernel.
* We assume that memory is set up correctly, and it will go hard
* with you if it is not. The reference parameter records the highest
* address we wrote. The initrd can go there.*/
uintptr_t load_elf(char *filename, uint64_t offset, uint64_t *highest,
Elf64_Ehdr *ehdr_out)
{
Elf64_Ehdr *ehdr;
Elf *elf;
size_t phnum = 0;
Elf64_Phdr *hdrs;
int fd;
uintptr_t ret;
uintptr_t kern_end = 0;
elf_version(EV_CURRENT);
fd = open(filename, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %r\n", filename);
return 0;
}
elf = elf_begin(fd, ELF_C_READ, NULL);
if (elf == NULL) {
fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__,
filename);
close(fd);
return 0;
}
ehdr = elf64_getehdr(elf);
if (ehdr == NULL) {
fprintf(stderr, "%s: cannot get exec header of %s.\n",
__func__, filename);
goto fail;
}
fprintf(stderr, "%s ELF entry point is %p\n", filename,
(void *)ehdr->e_entry);
if (elf_getphdrnum(elf, &phnum) < 0) {
fprintf(stderr, "%s: cannot get program header num of %s.\n",
__func__, filename);
goto fail;
}
fprintf(stderr, "%s has %p program headers\n", filename, phnum);
hdrs = elf64_getphdr(elf);
if (hdrs == NULL) {
fprintf(stderr, "%s: cannot get program headers of %s.\n",
__func__, filename);
goto fail;
}
for (int i = 0; i < phnum; i++) {
size_t tot;
Elf64_Phdr *h = &hdrs[i];
uintptr_t pa;
fprintf(stderr,
"%d: type 0x%lx flags 0x%lx offset 0x%lx vaddr 0x%lx\npaddr 0x%lx size 0x%lx memsz 0x%lx align 0x%lx\n",
i,
h->p_type, /* Segment type */
h->p_flags, /* Segment flags */
h->p_offset, /* Segment file offset */
h->p_vaddr, /* Segment virtual address */
h->p_paddr, /* Segment physical address */
h->p_filesz, /* Segment size in file */
h->p_memsz, /* Segment size in memory */
h->p_align /* Segment alignment */);
if (h->p_type != PT_LOAD)
continue;
if ((h->p_flags & (PF_R | PF_W | PF_X)) == 0)
continue;
pa = h->p_paddr;
fprintf(stderr,
"Read header %d @offset %p to %p (elf PA is %p) %d bytes:",
i, h->p_offset, pa, h->p_paddr, h->p_filesz);
tot = 0;
while (tot < h->p_filesz) {
int amt = pread(fd, (void *)(pa + tot + offset),
h->p_filesz - tot, h->p_offset + tot);
if (amt < 1)
break;
tot += amt;
}
fprintf(stderr, "read a total of %d bytes\n", tot);
if (tot < h->p_filesz) {
fprintf(stderr, "%s: got %d bytes, wanted %d bytes\n",
filename, tot, h->p_filesz);
goto fail;
}
if ((h->p_paddr + h->p_memsz) > kern_end)
kern_end = h->p_paddr + h->p_memsz;
}
close(fd);
ret = ehdr->e_entry + offset;
// Save the values in the header, if the caller wanted them
if (ehdr_out)
*ehdr_out = *ehdr;
elf_end(elf);
if (highest)
*highest = kern_end;
return ret;
fail:
close(fd);
elf_end(elf);
return 0;
}