xref: /qemu/contrib/elf2dmp/pdb.c (revision 9c707525)
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 #include "qemu/bswap.h"
23 
24 #include "pdb.h"
25 #include "err.h"
26 
27 static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
28 {
29     if (idx >= r->ds.toc->num_files) {
30         return 0;
31     }
32 
33     return r->ds.toc->file_size[idx];
34 }
35 
36 static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
37 {
38     size_t i = 0;
39     char *ptr;
40 
41     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
42         i++;
43         ptr += 8;
44         if (i == n) {
45             break;
46         }
47         ptr += sizeof(pdb_seg);
48     }
49 
50     return (pdb_seg *)ptr;
51 }
52 
53 uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
54 {
55     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
56     int length;
57     const union codeview_symbol *sym;
58     const uint8_t *root = r->modimage;
59     size_t i;
60 
61     for (i = 0; i < size; i += length) {
62         sym = (const void *)(root + i);
63         length = sym->generic.len + 2;
64 
65         if (!sym->generic.id || length < 4) {
66             break;
67         }
68 
69         if (sym->generic.id == S_PUB_V3 &&
70                 !strcmp(name, sym->public_v3.name)) {
71             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
72             uint32_t sect_rva = segment->dword[1];
73             uint64_t rva = sect_rva + sym->public_v3.offset;
74 
75             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
76                     sect_rva, sym->public_v3.segment,
77                     ((char *)segment - 8), sym->public_v3.offset, rva);
78             return rva;
79         }
80     }
81 
82     return 0;
83 }
84 
85 uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
86 {
87     uint64_t rva = pdb_find_public_v3_symbol(r, name);
88 
89     if (!rva) {
90         return 0;
91     }
92 
93     return img_base + rva;
94 }
95 
96 static void pdb_reader_ds_exit(struct pdb_reader *r)
97 {
98     g_free(r->ds.toc);
99 }
100 
101 static void pdb_exit_symbols(struct pdb_reader *r)
102 {
103     g_free(r->modimage);
104     g_free(r->symbols);
105 }
106 
107 static void pdb_exit_segments(struct pdb_reader *r)
108 {
109     g_free(r->segs);
110 }
111 
112 static void *pdb_ds_read(const PDB_DS_HEADER *header,
113         const uint32_t *block_list, int size)
114 {
115     int i, nBlocks;
116     uint8_t *buffer;
117 
118     if (!size) {
119         return NULL;
120     }
121 
122     nBlocks = (size + header->block_size - 1) / header->block_size;
123 
124     buffer = g_malloc(nBlocks * header->block_size);
125 
126     for (i = 0; i < nBlocks; i++) {
127         memcpy(buffer + i * header->block_size, (const char *)header +
128                 block_list[i] * header->block_size, header->block_size);
129     }
130 
131     return buffer;
132 }
133 
134 static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
135 {
136     const uint32_t *block_list;
137     uint32_t block_size;
138     const uint32_t *file_size;
139     size_t i;
140 
141     if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
142         return NULL;
143     }
144 
145     file_size = r->ds.toc->file_size;
146     r->file_used[file_number / 32] |= 1 << (file_number % 32);
147 
148     if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
149         return NULL;
150     }
151 
152     block_list = file_size + r->ds.toc->num_files;
153     block_size = r->ds.header->block_size;
154 
155     for (i = 0; i < file_number; i++) {
156         block_list += (file_size[i] + block_size - 1) / block_size;
157     }
158 
159     return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
160 }
161 
162 static bool pdb_init_segments(struct pdb_reader *r)
163 {
164     unsigned stream_idx = r->segments;
165 
166     r->segs = pdb_ds_read_file(r, stream_idx);
167     if (!r->segs) {
168         return false;
169     }
170 
171     r->segs_size = pdb_get_file_size(r, stream_idx);
172     if (!r->segs_size) {
173         return false;
174     }
175 
176     return true;
177 }
178 
179 static bool pdb_init_symbols(struct pdb_reader *r)
180 {
181     PDB_SYMBOLS *symbols;
182 
183     symbols = pdb_ds_read_file(r, 3);
184     if (!symbols) {
185         return false;
186     }
187 
188     r->symbols = symbols;
189 
190     r->segments = lduw_le_p((const char *)symbols + sizeof(PDB_SYMBOLS) +
191             symbols->module_size + symbols->offset_size +
192             symbols->hash_size + symbols->srcmodule_size +
193             symbols->pdbimport_size + symbols->unknown2_size +
194             offsetof(PDB_STREAM_INDEXES, segments));
195 
196     /* Read global symbol table */
197     r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
198     if (!r->modimage) {
199         goto out_symbols;
200     }
201 
202     return true;
203 
204 out_symbols:
205     g_free(symbols);
206 
207     return false;
208 }
209 
210 static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
211 {
212     if (hdr->block_size == 0) {
213         return false;
214     }
215 
216     memset(r->file_used, 0, sizeof(r->file_used));
217     r->ds.header = hdr;
218     r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
219                 hdr->toc_page * hdr->block_size), hdr->toc_size);
220 
221     if (!r->ds.toc) {
222         return false;
223     }
224 
225     return true;
226 }
227 
228 static bool pdb_reader_init(struct pdb_reader *r, void *data)
229 {
230     const char pdb7[] = "Microsoft C/C++ MSF 7.00";
231 
232     if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
233         return false;
234     }
235 
236     if (!pdb_reader_ds_init(r, data)) {
237         return false;
238     }
239 
240     r->ds.root = pdb_ds_read_file(r, 1);
241     if (!r->ds.root) {
242         goto out_ds;
243     }
244 
245     if (!pdb_init_symbols(r)) {
246         goto out_root;
247     }
248 
249     if (!pdb_init_segments(r)) {
250         goto out_sym;
251     }
252 
253     return true;
254 
255 out_sym:
256     pdb_exit_symbols(r);
257 out_root:
258     g_free(r->ds.root);
259 out_ds:
260     pdb_reader_ds_exit(r);
261 
262     return false;
263 }
264 
265 static void pdb_reader_exit(struct pdb_reader *r)
266 {
267     pdb_exit_segments(r);
268     pdb_exit_symbols(r);
269     g_free(r->ds.root);
270     pdb_reader_ds_exit(r);
271 }
272 
273 bool pdb_init_from_file(const char *name, struct pdb_reader *reader)
274 {
275     GError *gerr = NULL;
276     void *map;
277 
278     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
279     if (gerr) {
280         eprintf("Failed to map PDB file \'%s\'\n", name);
281         g_error_free(gerr);
282         return false;
283     }
284 
285     reader->file_size = g_mapped_file_get_length(reader->gmf);
286     map = g_mapped_file_get_contents(reader->gmf);
287     if (!pdb_reader_init(reader, map)) {
288         goto out_unmap;
289     }
290 
291     return true;
292 
293 out_unmap:
294     g_mapped_file_unref(reader->gmf);
295 
296     return false;
297 }
298 
299 void pdb_exit(struct pdb_reader *reader)
300 {
301     g_mapped_file_unref(reader->gmf);
302     pdb_reader_exit(reader);
303 }
304