1 | /* |
2 | * Copyright (c) 2018 Virtuozzo International GmbH |
3 | * |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
5 | * |
6 | */ |
7 | |
8 | #include "qemu/osdep.h" |
9 | #include "err.h" |
10 | #include "qemu_elf.h" |
11 | |
12 | #define QEMU_NOTE_NAME "QEMU" |
13 | |
14 | #ifndef ROUND_UP |
15 | #define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d))) |
16 | #endif |
17 | |
18 | #ifndef DIV_ROUND_UP |
19 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) |
20 | #endif |
21 | |
22 | #define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ |
23 | ((DIV_ROUND_UP((hdr_size), 4) + \ |
24 | DIV_ROUND_UP((name_size), 4) + \ |
25 | DIV_ROUND_UP((desc_size), 4)) * 4) |
26 | |
27 | int is_system(QEMUCPUState *s) |
28 | { |
29 | return s->gs.base >> 63; |
30 | } |
31 | |
32 | static char *nhdr_get_name(Elf64_Nhdr *nhdr) |
33 | { |
34 | return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4); |
35 | } |
36 | |
37 | static void *nhdr_get_desc(Elf64_Nhdr *nhdr) |
38 | { |
39 | return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4); |
40 | } |
41 | |
42 | static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr) |
43 | { |
44 | return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr), |
45 | nhdr->n_namesz, nhdr->n_descsz)); |
46 | } |
47 | |
48 | Elf64_Phdr *elf64_getphdr(void *map) |
49 | { |
50 | Elf64_Ehdr *ehdr = map; |
51 | Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff); |
52 | |
53 | return phdr; |
54 | } |
55 | |
56 | Elf64_Half elf_getphdrnum(void *map) |
57 | { |
58 | Elf64_Ehdr *ehdr = map; |
59 | |
60 | return ehdr->e_phnum; |
61 | } |
62 | |
63 | static int init_states(QEMU_Elf *qe) |
64 | { |
65 | Elf64_Phdr *phdr = elf64_getphdr(qe->map); |
66 | Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset); |
67 | Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz); |
68 | Elf64_Nhdr *nhdr; |
69 | size_t cpu_nr = 0; |
70 | |
71 | if (phdr[0].p_type != PT_NOTE) { |
72 | eprintf("Failed to find PT_NOTE\n" ); |
73 | return 1; |
74 | } |
75 | |
76 | qe->has_kernel_gs_base = 1; |
77 | |
78 | for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { |
79 | if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { |
80 | QEMUCPUState *state = nhdr_get_desc(nhdr); |
81 | |
82 | if (state->size < sizeof(*state)) { |
83 | eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n" , |
84 | cpu_nr, state->size); |
85 | /* |
86 | * We assume either every QEMU CPU state has KERNEL_GS_BASE or |
87 | * no one has. |
88 | */ |
89 | qe->has_kernel_gs_base = 0; |
90 | } |
91 | cpu_nr++; |
92 | } |
93 | } |
94 | |
95 | printf("%zu CPU states has been found\n" , cpu_nr); |
96 | |
97 | qe->state = malloc(sizeof(*qe->state) * cpu_nr); |
98 | if (!qe->state) { |
99 | return 1; |
100 | } |
101 | |
102 | cpu_nr = 0; |
103 | |
104 | for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { |
105 | if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { |
106 | qe->state[cpu_nr] = nhdr_get_desc(nhdr); |
107 | cpu_nr++; |
108 | } |
109 | } |
110 | |
111 | qe->state_nr = cpu_nr; |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | static void exit_states(QEMU_Elf *qe) |
117 | { |
118 | free(qe->state); |
119 | } |
120 | |
121 | int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) |
122 | { |
123 | GError *gerr = NULL; |
124 | int err = 0; |
125 | |
126 | qe->gmf = g_mapped_file_new(filename, TRUE, &gerr); |
127 | if (gerr) { |
128 | eprintf("Failed to map ELF dump file \'%s\'\n" , filename); |
129 | return 1; |
130 | } |
131 | |
132 | qe->map = g_mapped_file_get_contents(qe->gmf); |
133 | qe->size = g_mapped_file_get_length(qe->gmf); |
134 | |
135 | if (init_states(qe)) { |
136 | eprintf("Failed to extract QEMU CPU states\n" ); |
137 | err = 1; |
138 | goto out_unmap; |
139 | } |
140 | |
141 | return 0; |
142 | |
143 | out_unmap: |
144 | g_mapped_file_unref(qe->gmf); |
145 | |
146 | return err; |
147 | } |
148 | |
149 | void QEMU_Elf_exit(QEMU_Elf *qe) |
150 | { |
151 | exit_states(qe); |
152 | g_mapped_file_unref(qe->gmf); |
153 | } |
154 | |