]> git.proxmox.com Git - mirror_qemu.git/blame - contrib/elf2dmp/qemu_elf.c
contrib/elf2dmp: Clamp QEMU note to file size
[mirror_qemu.git] / contrib / elf2dmp / qemu_elf.c
CommitLineData
3fa2d384
VP
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"
9de37c28 9#include "qemu/host-utils.h"
3fa2d384
VP
10#include "err.h"
11#include "qemu_elf.h"
12
13#define QEMU_NOTE_NAME "QEMU"
14
15#ifndef ROUND_UP
16#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
17#endif
18
3fa2d384
VP
19int is_system(QEMUCPUState *s)
20{
21 return s->gs.base >> 63;
22}
23
3fa2d384
VP
24Elf64_Phdr *elf64_getphdr(void *map)
25{
26 Elf64_Ehdr *ehdr = map;
27 Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff);
28
29 return phdr;
30}
31
32Elf64_Half elf_getphdrnum(void *map)
33{
34 Elf64_Ehdr *ehdr = map;
35
36 return ehdr->e_phnum;
37}
38
9de37c28
AO
39static bool advance_note_offset(uint64_t *offsetp, uint64_t size, uint64_t end)
40{
41 uint64_t offset = *offsetp;
42
43 if (uadd64_overflow(offset, size, &offset) || offset > UINT64_MAX - 3) {
44 return false;
45 }
46
47 offset = ROUND_UP(offset, 4);
48
49 if (offset > end) {
50 return false;
51 }
52
53 *offsetp = offset;
54
55 return true;
56}
57
49760ccf 58static bool init_states(QEMU_Elf *qe)
3fa2d384
VP
59{
60 Elf64_Phdr *phdr = elf64_getphdr(qe->map);
3fa2d384 61 Elf64_Nhdr *nhdr;
0c94e32d 62 GPtrArray *states;
9de37c28
AO
63 QEMUCPUState *state;
64 uint32_t state_size;
65 uint64_t offset;
66 uint64_t end_offset;
67 char *name;
3fa2d384
VP
68
69 if (phdr[0].p_type != PT_NOTE) {
70 eprintf("Failed to find PT_NOTE\n");
49760ccf 71 return false;
3fa2d384
VP
72 }
73
74 qe->has_kernel_gs_base = 1;
9de37c28 75 offset = phdr[0].p_offset;
0c94e32d 76 states = g_ptr_array_new();
3fa2d384 77
9de37c28
AO
78 if (uadd64_overflow(offset, phdr[0].p_memsz, &end_offset) ||
79 end_offset > qe->size) {
80 end_offset = qe->size;
81 }
82
83 while (offset < end_offset) {
84 nhdr = (void *)((uint8_t *)qe->map + offset);
85
86 if (!advance_note_offset(&offset, sizeof(*nhdr), end_offset)) {
87 break;
88 }
89
90 name = (char *)qe->map + offset;
91
92 if (!advance_note_offset(&offset, nhdr->n_namesz, end_offset)) {
93 break;
94 }
95
96 state = (void *)((uint8_t *)qe->map + offset);
97
98 if (!advance_note_offset(&offset, nhdr->n_descsz, end_offset)) {
99 break;
100 }
101
102 if (!strcmp(name, QEMU_NOTE_NAME) &&
103 nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) {
104 state_size = MIN(state->size, nhdr->n_descsz);
3fa2d384 105
9de37c28 106 if (state_size < sizeof(*state)) {
0c94e32d 107 eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n",
9de37c28 108 states->len, state_size);
3fa2d384
VP
109 /*
110 * We assume either every QEMU CPU state has KERNEL_GS_BASE or
111 * no one has.
112 */
113 qe->has_kernel_gs_base = 0;
114 }
0c94e32d 115 g_ptr_array_add(states, state);
3fa2d384
VP
116 }
117 }
118
0c94e32d 119 printf("%u CPU states has been found\n", states->len);
3fa2d384 120
0c94e32d
AO
121 qe->state_nr = states->len;
122 qe->state = (void *)g_ptr_array_free(states, FALSE);
3fa2d384 123
49760ccf 124 return true;
3fa2d384
VP
125}
126
127static void exit_states(QEMU_Elf *qe)
128{
2a052b4e 129 g_free(qe->state);
3fa2d384
VP
130}
131
c06ebc0f
VP
132static bool check_ehdr(QEMU_Elf *qe)
133{
134 Elf64_Ehdr *ehdr = qe->map;
135
136 if (sizeof(Elf64_Ehdr) > qe->size) {
137 eprintf("Invalid input dump file size\n");
138 return false;
139 }
140
141 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
142 eprintf("Invalid ELF signature, input file is not ELF\n");
143 return false;
144 }
145
146 if (ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
147 ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
148 eprintf("Invalid ELF class or byte order, must be 64-bit LE\n");
149 return false;
150 }
151
152 if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
153 eprintf("Invalid ELF version\n");
154 return false;
155 }
156
157 if (ehdr->e_machine != EM_X86_64) {
158 eprintf("Invalid input dump architecture, only x86_64 is supported\n");
159 return false;
160 }
161
162 if (ehdr->e_type != ET_CORE) {
163 eprintf("Invalid ELF type, must be core file\n");
164 return false;
165 }
166
167 /*
168 * ELF dump file must contain one PT_NOTE and at least one PT_LOAD to
169 * restore physical address space.
170 */
171 if (ehdr->e_phnum < 2) {
172 eprintf("Invalid number of ELF program headers\n");
173 return false;
174 }
175
176 return true;
177}
178
49760ccf 179static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
3fa2d384 180{
df7a7556
VP
181#ifdef CONFIG_LINUX
182 struct stat st;
183 int fd;
184
185 printf("Using Linux mmap\n");
186
187 fd = open(filename, O_RDONLY, 0);
188 if (fd == -1) {
189 eprintf("Failed to open ELF dump file \'%s\'\n", filename);
49760ccf 190 return false;
df7a7556
VP
191 }
192
193 if (fstat(fd, &st)) {
194 eprintf("Failed to get size of ELF dump file\n");
195 close(fd);
49760ccf 196 return false;
df7a7556
VP
197 }
198 qe->size = st.st_size;
199
200 qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE,
201 MAP_PRIVATE | MAP_NORESERVE, fd, 0);
202 if (qe->map == MAP_FAILED) {
203 eprintf("Failed to map ELF file\n");
204 close(fd);
49760ccf 205 return false;
df7a7556
VP
206 }
207
208 close(fd);
209#else
bd4d0da7 210 GError *gerr = NULL;
df7a7556
VP
211
212 printf("Using GLib mmap\n");
3fa2d384 213
bd4d0da7
VP
214 qe->gmf = g_mapped_file_new(filename, TRUE, &gerr);
215 if (gerr) {
216 eprintf("Failed to map ELF dump file \'%s\'\n", filename);
514284d7 217 g_error_free(gerr);
49760ccf 218 return false;
3fa2d384
VP
219 }
220
bd4d0da7
VP
221 qe->map = g_mapped_file_get_contents(qe->gmf);
222 qe->size = g_mapped_file_get_length(qe->gmf);
df7a7556
VP
223#endif
224
49760ccf 225 return true;
df7a7556
VP
226}
227
228static void QEMU_Elf_unmap(QEMU_Elf *qe)
229{
230#ifdef CONFIG_LINUX
231 munmap(qe->map, qe->size);
232#else
233 g_mapped_file_unref(qe->gmf);
234#endif
235}
236
49760ccf 237bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
df7a7556 238{
49760ccf
AO
239 if (!QEMU_Elf_map(qe, filename)) {
240 return false;
df7a7556 241 }
3fa2d384 242
c06ebc0f
VP
243 if (!check_ehdr(qe)) {
244 eprintf("Input file has the wrong format\n");
df7a7556 245 QEMU_Elf_unmap(qe);
49760ccf 246 return false;
c06ebc0f
VP
247 }
248
49760ccf 249 if (!init_states(qe)) {
3fa2d384 250 eprintf("Failed to extract QEMU CPU states\n");
df7a7556 251 QEMU_Elf_unmap(qe);
49760ccf 252 return false;
3fa2d384
VP
253 }
254
49760ccf 255 return true;
3fa2d384
VP
256}
257
258void QEMU_Elf_exit(QEMU_Elf *qe)
259{
260 exit_states(qe);
df7a7556 261 QEMU_Elf_unmap(qe);
3fa2d384 262}