xref: /openbmc/qemu/contrib/elf2dmp/pdb.c (revision 39dc3e4a)
1 /*
2  * Copyright (c) 2018 Virtuozzo International GmbH
3  *
4  * Based on source of Wine project
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "qemu/osdep.h"
22 
23 #include "pdb.h"
24 #include "err.h"
25 
26 static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
27 {
28     return r->ds.toc->file_size[idx];
29 }
30 
31 static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
32 {
33     size_t i = 0;
34     char *ptr;
35 
36     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
37         i++;
38         ptr += 8;
39         if (i == n) {
40             break;
41         }
42         ptr += sizeof(pdb_seg);
43     }
44 
45     return (pdb_seg *)ptr;
46 }
47 
48 uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
49 {
50     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
51     int length;
52     const union codeview_symbol *sym;
53     const uint8_t *root = r->modimage;
54     size_t i;
55 
56     for (i = 0; i < size; i += length) {
57         sym = (const void *)(root + i);
58         length = sym->generic.len + 2;
59 
60         if (!sym->generic.id || length < 4) {
61             break;
62         }
63 
64         if (sym->generic.id == S_PUB_V3 &&
65                 !strcmp(name, sym->public_v3.name)) {
66             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
67             uint32_t sect_rva = segment->dword[1];
68             uint64_t rva = sect_rva + sym->public_v3.offset;
69 
70             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
71                     sect_rva, sym->public_v3.segment,
72                     ((char *)segment - 8), sym->public_v3.offset, rva);
73             return rva;
74         }
75     }
76 
77     return 0;
78 }
79 
80 uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
81 {
82     uint64_t rva = pdb_find_public_v3_symbol(r, name);
83 
84     if (!rva) {
85         return 0;
86     }
87 
88     return img_base + rva;
89 }
90 
91 static void pdb_reader_ds_exit(struct pdb_reader *r)
92 {
93     free(r->ds.toc);
94 }
95 
96 static void pdb_exit_symbols(struct pdb_reader *r)
97 {
98     free(r->modimage);
99     free(r->symbols);
100 }
101 
102 static void pdb_exit_segments(struct pdb_reader *r)
103 {
104     free(r->segs);
105 }
106 
107 static void *pdb_ds_read(const PDB_DS_HEADER *header,
108         const uint32_t *block_list, int size)
109 {
110     int i, nBlocks;
111     uint8_t *buffer;
112 
113     if (!size) {
114         return NULL;
115     }
116 
117     nBlocks = (size + header->block_size - 1) / header->block_size;
118 
119     buffer = malloc(nBlocks * header->block_size);
120     if (!buffer) {
121         return NULL;
122     }
123 
124     for (i = 0; i < nBlocks; i++) {
125         memcpy(buffer + i * header->block_size, (const char *)header +
126                 block_list[i] * header->block_size, header->block_size);
127     }
128 
129     return buffer;
130 }
131 
132 static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
133 {
134     const uint32_t *block_list;
135     uint32_t block_size;
136     const uint32_t *file_size;
137     size_t i;
138 
139     if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
140         return NULL;
141     }
142 
143     file_size = r->ds.toc->file_size;
144     r->file_used[file_number / 32] |= 1 << (file_number % 32);
145 
146     if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
147         return NULL;
148     }
149 
150     block_list = file_size + r->ds.toc->num_files;
151     block_size = r->ds.header->block_size;
152 
153     for (i = 0; i < file_number; i++) {
154         block_list += (file_size[i] + block_size - 1) / block_size;
155     }
156 
157     return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
158 }
159 
160 static int pdb_init_segments(struct pdb_reader *r)
161 {
162     char *segs;
163     unsigned stream_idx = r->sidx.segments;
164 
165     segs = pdb_ds_read_file(r, stream_idx);
166     if (!segs) {
167         return 1;
168     }
169 
170     r->segs = segs;
171     r->segs_size = pdb_get_file_size(r, stream_idx);
172 
173     return 0;
174 }
175 
176 static int pdb_init_symbols(struct pdb_reader *r)
177 {
178     int err = 0;
179     PDB_SYMBOLS *symbols;
180     PDB_STREAM_INDEXES *sidx = &r->sidx;
181 
182     memset(sidx, -1, sizeof(*sidx));
183 
184     symbols = pdb_ds_read_file(r, 3);
185     if (!symbols) {
186         return 1;
187     }
188 
189     r->symbols = symbols;
190 
191     if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
192         err = 1;
193         goto out_symbols;
194     }
195 
196     memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
197             symbols->module_size + symbols->offset_size +
198             symbols->hash_size + symbols->srcmodule_size +
199             symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
200 
201     /* Read global symbol table */
202     r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
203     if (!r->modimage) {
204         err = 1;
205         goto out_symbols;
206     }
207 
208     return 0;
209 
210 out_symbols:
211     free(symbols);
212 
213     return err;
214 }
215 
216 static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
217 {
218     if (hdr->block_size == 0) {
219         return 1;
220     }
221 
222     memset(r->file_used, 0, sizeof(r->file_used));
223     r->ds.header = hdr;
224     r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
225                 hdr->toc_page * hdr->block_size), hdr->toc_size);
226 
227     if (!r->ds.toc) {
228         return 1;
229     }
230 
231     return 0;
232 }
233 
234 static int pdb_reader_init(struct pdb_reader *r, void *data)
235 {
236     int err = 0;
237     const char pdb7[] = "Microsoft C/C++ MSF 7.00";
238 
239     if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
240         return 1;
241     }
242 
243     if (pdb_reader_ds_init(r, data)) {
244         return 1;
245     }
246 
247     r->ds.root = pdb_ds_read_file(r, 1);
248     if (!r->ds.root) {
249         err = 1;
250         goto out_ds;
251     }
252 
253     if (pdb_init_symbols(r)) {
254         err = 1;
255         goto out_root;
256     }
257 
258     if (pdb_init_segments(r)) {
259         err = 1;
260         goto out_sym;
261     }
262 
263     return 0;
264 
265 out_sym:
266     pdb_exit_symbols(r);
267 out_root:
268     free(r->ds.root);
269 out_ds:
270     pdb_reader_ds_exit(r);
271 
272     return err;
273 }
274 
275 static void pdb_reader_exit(struct pdb_reader *r)
276 {
277     pdb_exit_segments(r);
278     pdb_exit_symbols(r);
279     free(r->ds.root);
280     pdb_reader_ds_exit(r);
281 }
282 
283 int pdb_init_from_file(const char *name, struct pdb_reader *reader)
284 {
285     GError *gerr = NULL;
286     int err = 0;
287     void *map;
288 
289     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
290     if (gerr) {
291         eprintf("Failed to map PDB file \'%s\'\n", name);
292         g_error_free(gerr);
293         return 1;
294     }
295 
296     reader->file_size = g_mapped_file_get_length(reader->gmf);
297     map = g_mapped_file_get_contents(reader->gmf);
298     if (pdb_reader_init(reader, map)) {
299         err = 1;
300         goto out_unmap;
301     }
302 
303     return 0;
304 
305 out_unmap:
306     g_mapped_file_unref(reader->gmf);
307 
308     return err;
309 }
310 
311 void pdb_exit(struct pdb_reader *reader)
312 {
313     g_mapped_file_unref(reader->gmf);
314     pdb_reader_exit(reader);
315 }
316