xref: /qemu/contrib/elf2dmp/qemu_elf.c (revision 9de37c28)
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 "qemu/host-utils.h"
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 
19 int is_system(QEMUCPUState *s)
20 {
21     return s->gs.base >> 63;
22 }
23 
24 Elf64_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 
32 Elf64_Half elf_getphdrnum(void *map)
33 {
34     Elf64_Ehdr *ehdr = map;
35 
36     return ehdr->e_phnum;
37 }
38 
39 static 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 
58 static bool init_states(QEMU_Elf *qe)
59 {
60     Elf64_Phdr *phdr = elf64_getphdr(qe->map);
61     Elf64_Nhdr *nhdr;
62     GPtrArray *states;
63     QEMUCPUState *state;
64     uint32_t state_size;
65     uint64_t offset;
66     uint64_t end_offset;
67     char *name;
68 
69     if (phdr[0].p_type != PT_NOTE) {
70         eprintf("Failed to find PT_NOTE\n");
71         return false;
72     }
73 
74     qe->has_kernel_gs_base = 1;
75     offset = phdr[0].p_offset;
76     states = g_ptr_array_new();
77 
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);
105 
106             if (state_size < sizeof(*state)) {
107                 eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n",
108                         states->len, state_size);
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             }
115             g_ptr_array_add(states, state);
116         }
117     }
118 
119     printf("%u CPU states has been found\n", states->len);
120 
121     qe->state_nr = states->len;
122     qe->state = (void *)g_ptr_array_free(states, FALSE);
123 
124     return true;
125 }
126 
127 static void exit_states(QEMU_Elf *qe)
128 {
129     g_free(qe->state);
130 }
131 
132 static 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 
179 static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
180 {
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);
190         return false;
191     }
192 
193     if (fstat(fd, &st)) {
194         eprintf("Failed to get size of ELF dump file\n");
195         close(fd);
196         return false;
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);
205         return false;
206     }
207 
208     close(fd);
209 #else
210     GError *gerr = NULL;
211 
212     printf("Using GLib mmap\n");
213 
214     qe->gmf = g_mapped_file_new(filename, TRUE, &gerr);
215     if (gerr) {
216         eprintf("Failed to map ELF dump file \'%s\'\n", filename);
217         g_error_free(gerr);
218         return false;
219     }
220 
221     qe->map = g_mapped_file_get_contents(qe->gmf);
222     qe->size = g_mapped_file_get_length(qe->gmf);
223 #endif
224 
225     return true;
226 }
227 
228 static 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 
237 bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
238 {
239     if (!QEMU_Elf_map(qe, filename)) {
240         return false;
241     }
242 
243     if (!check_ehdr(qe)) {
244         eprintf("Input file has the wrong format\n");
245         QEMU_Elf_unmap(qe);
246         return false;
247     }
248 
249     if (!init_states(qe)) {
250         eprintf("Failed to extract QEMU CPU states\n");
251         QEMU_Elf_unmap(qe);
252         return false;
253     }
254 
255     return true;
256 }
257 
258 void QEMU_Elf_exit(QEMU_Elf *qe)
259 {
260     exit_states(qe);
261     QEMU_Elf_unmap(qe);
262 }
263