165 lines
3.6 KiB
C
165 lines
3.6 KiB
C
|
/*
|
||
|
* Copyright (c) 2018 Virtuozzo International GmbH
|
||
|
*
|
||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "qemu/osdep.h"
|
||
|
#include "err.h"
|
||
|
#include "qemu_elf.h"
|
||
|
|
||
|
#define QEMU_NOTE_NAME "QEMU"
|
||
|
|
||
|
#ifndef ROUND_UP
|
||
|
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
|
||
|
#endif
|
||
|
|
||
|
#ifndef DIV_ROUND_UP
|
||
|
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||
|
#endif
|
||
|
|
||
|
#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
|
||
|
((DIV_ROUND_UP((hdr_size), 4) + \
|
||
|
DIV_ROUND_UP((name_size), 4) + \
|
||
|
DIV_ROUND_UP((desc_size), 4)) * 4)
|
||
|
|
||
|
int is_system(QEMUCPUState *s)
|
||
|
{
|
||
|
return s->gs.base >> 63;
|
||
|
}
|
||
|
|
||
|
static char *nhdr_get_name(Elf64_Nhdr *nhdr)
|
||
|
{
|
||
|
return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
|
||
|
}
|
||
|
|
||
|
static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
|
||
|
{
|
||
|
return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
|
||
|
}
|
||
|
|
||
|
static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
|
||
|
{
|
||
|
return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
|
||
|
nhdr->n_namesz, nhdr->n_descsz));
|
||
|
}
|
||
|
|
||
|
Elf64_Phdr *elf64_getphdr(void *map)
|
||
|
{
|
||
|
Elf64_Ehdr *ehdr = map;
|
||
|
Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff);
|
||
|
|
||
|
return phdr;
|
||
|
}
|
||
|
|
||
|
Elf64_Half elf_getphdrnum(void *map)
|
||
|
{
|
||
|
Elf64_Ehdr *ehdr = map;
|
||
|
|
||
|
return ehdr->e_phnum;
|
||
|
}
|
||
|
|
||
|
static int init_states(QEMU_Elf *qe)
|
||
|
{
|
||
|
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
|
||
|
Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
|
||
|
Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
|
||
|
Elf64_Nhdr *nhdr;
|
||
|
size_t cpu_nr = 0;
|
||
|
|
||
|
if (phdr[0].p_type != PT_NOTE) {
|
||
|
eprintf("Failed to find PT_NOTE\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
qe->has_kernel_gs_base = 1;
|
||
|
|
||
|
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||
|
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||
|
QEMUCPUState *state = nhdr_get_desc(nhdr);
|
||
|
|
||
|
if (state->size < sizeof(*state)) {
|
||
|
eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
|
||
|
cpu_nr, state->size);
|
||
|
/*
|
||
|
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
|
||
|
* no one has.
|
||
|
*/
|
||
|
qe->has_kernel_gs_base = 0;
|
||
|
}
|
||
|
cpu_nr++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
printf("%zu CPU states has been found\n", cpu_nr);
|
||
|
|
||
|
qe->state = malloc(sizeof(*qe->state) * cpu_nr);
|
||
|
if (!qe->state) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
cpu_nr = 0;
|
||
|
|
||
|
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||
|
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||
|
qe->state[cpu_nr] = nhdr_get_desc(nhdr);
|
||
|
cpu_nr++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qe->state_nr = cpu_nr;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void exit_states(QEMU_Elf *qe)
|
||
|
{
|
||
|
free(qe->state);
|
||
|
}
|
||
|
|
||
|
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
|
||
|
{
|
||
|
int err = 0;
|
||
|
struct stat st;
|
||
|
|
||
|
qe->fd = open(filename, O_RDONLY, 0);
|
||
|
if (qe->fd == -1) {
|
||
|
eprintf("Failed to open ELF dump file \'%s\'\n", filename);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
fstat(qe->fd, &st);
|
||
|
qe->size = st.st_size;
|
||
|
|
||
|
qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE,
|
||
|
MAP_PRIVATE, qe->fd, 0);
|
||
|
if (qe->map == MAP_FAILED) {
|
||
|
eprintf("Failed to map ELF file\n");
|
||
|
err = 1;
|
||
|
goto out_fd;
|
||
|
}
|
||
|
|
||
|
if (init_states(qe)) {
|
||
|
eprintf("Failed to extract QEMU CPU states\n");
|
||
|
err = 1;
|
||
|
goto out_unmap;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
out_unmap:
|
||
|
munmap(qe->map, qe->size);
|
||
|
out_fd:
|
||
|
close(qe->fd);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
void QEMU_Elf_exit(QEMU_Elf *qe)
|
||
|
{
|
||
|
exit_states(qe);
|
||
|
munmap(qe->map, qe->size);
|
||
|
close(qe->fd);
|
||
|
}
|