xref: /openbmc/qemu/contrib/elf2dmp/qemu_elf.c (revision 98d16e5f)
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 
is_system(QEMUCPUState * s)19 int is_system(QEMUCPUState *s)
20 {
21     return s->gs.base >> 63;
22 }
23 
elf64_getphdr(void * map)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 
elf_getphdrnum(void * map)32 Elf64_Half elf_getphdrnum(void *map)
33 {
34     Elf64_Ehdr *ehdr = map;
35 
36     return ehdr->e_phnum;
37 }
38 
advance_note_offset(uint64_t * offsetp,uint64_t size,uint64_t end)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 
init_states(QEMU_Elf * qe)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 
exit_states(QEMU_Elf * qe)127 static void exit_states(QEMU_Elf *qe)
128 {
129     g_free(qe->state);
130 }
131 
check_ehdr(QEMU_Elf * qe)132 static bool check_ehdr(QEMU_Elf *qe)
133 {
134     Elf64_Ehdr *ehdr = qe->map;
135     uint64_t phendoff;
136 
137     if (sizeof(Elf64_Ehdr) > qe->size) {
138         eprintf("Invalid input dump file size\n");
139         return false;
140     }
141 
142     if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
143         eprintf("Invalid ELF signature, input file is not ELF\n");
144         return false;
145     }
146 
147     if (ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
148             ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
149         eprintf("Invalid ELF class or byte order, must be 64-bit LE\n");
150         return false;
151     }
152 
153     if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
154         eprintf("Invalid ELF version\n");
155         return false;
156     }
157 
158     if (ehdr->e_machine != EM_X86_64) {
159         eprintf("Invalid input dump architecture, only x86_64 is supported\n");
160         return false;
161     }
162 
163     if (ehdr->e_type != ET_CORE) {
164         eprintf("Invalid ELF type, must be core file\n");
165         return false;
166     }
167 
168     /*
169      * ELF dump file must contain one PT_NOTE and at least one PT_LOAD to
170      * restore physical address space.
171      */
172     if (ehdr->e_phnum < 2) {
173         eprintf("Invalid number of ELF program headers\n");
174         return false;
175     }
176 
177     if (umul64_overflow(ehdr->e_phnum, sizeof(Elf64_Phdr), &phendoff) ||
178         uadd64_overflow(phendoff, ehdr->e_phoff, &phendoff) ||
179         phendoff > qe->size) {
180         eprintf("phdrs do not fit in file\n");
181         return false;
182     }
183 
184     return true;
185 }
186 
QEMU_Elf_map(QEMU_Elf * qe,const char * filename)187 static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
188 {
189 #ifdef CONFIG_LINUX
190     struct stat st;
191     int fd;
192 
193     printf("Using Linux mmap\n");
194 
195     fd = open(filename, O_RDONLY, 0);
196     if (fd == -1) {
197         eprintf("Failed to open ELF dump file \'%s\'\n", filename);
198         return false;
199     }
200 
201     if (fstat(fd, &st)) {
202         eprintf("Failed to get size of ELF dump file\n");
203         close(fd);
204         return false;
205     }
206     qe->size = st.st_size;
207 
208     qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE,
209             MAP_PRIVATE | MAP_NORESERVE, fd, 0);
210     if (qe->map == MAP_FAILED) {
211         eprintf("Failed to map ELF file\n");
212         close(fd);
213         return false;
214     }
215 
216     close(fd);
217 #else
218     GError *gerr = NULL;
219 
220     printf("Using GLib mmap\n");
221 
222     qe->gmf = g_mapped_file_new(filename, TRUE, &gerr);
223     if (gerr) {
224         eprintf("Failed to map ELF dump file \'%s\'\n", filename);
225         g_error_free(gerr);
226         return false;
227     }
228 
229     qe->map = g_mapped_file_get_contents(qe->gmf);
230     qe->size = g_mapped_file_get_length(qe->gmf);
231 #endif
232 
233     return true;
234 }
235 
QEMU_Elf_unmap(QEMU_Elf * qe)236 static void QEMU_Elf_unmap(QEMU_Elf *qe)
237 {
238 #ifdef CONFIG_LINUX
239     munmap(qe->map, qe->size);
240 #else
241     g_mapped_file_unref(qe->gmf);
242 #endif
243 }
244 
QEMU_Elf_init(QEMU_Elf * qe,const char * filename)245 bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
246 {
247     if (!QEMU_Elf_map(qe, filename)) {
248         return false;
249     }
250 
251     if (!check_ehdr(qe)) {
252         eprintf("Input file has the wrong format\n");
253         QEMU_Elf_unmap(qe);
254         return false;
255     }
256 
257     if (!init_states(qe)) {
258         eprintf("Failed to extract QEMU CPU states\n");
259         QEMU_Elf_unmap(qe);
260         return false;
261     }
262 
263     return true;
264 }
265 
QEMU_Elf_exit(QEMU_Elf * qe)266 void QEMU_Elf_exit(QEMU_Elf *qe)
267 {
268     exit_states(qe);
269     QEMU_Elf_unmap(qe);
270 }
271