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
26static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
27{
28 return r->ds.toc->file_size[idx];
29}
30
31static 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
48uint64_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
80uint64_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
91static void pdb_reader_ds_exit(struct pdb_reader *r)
92{
93 free(r->ds.toc);
94}
95
96static void pdb_exit_symbols(struct pdb_reader *r)
97{
98 free(r->modimage);
99 free(r->symbols);
100}
101
102static void pdb_exit_segments(struct pdb_reader *r)
103{
104 free(r->segs);
105}
106
107static 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
132static 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
160static 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
176static 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
210out_symbols:
211 free(symbols);
212
213 return err;
214}
215
216static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
217{
218 memset(r->file_used, 0, sizeof(r->file_used));
219 r->ds.header = hdr;
220 r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
221 hdr->toc_page * hdr->block_size), hdr->toc_size);
222
223 if (!r->ds.toc) {
224 return 1;
225 }
226
227 return 0;
228}
229
230static int pdb_reader_init(struct pdb_reader *r, void *data)
231{
232 int err = 0;
233 const char pdb7[] = "Microsoft C/C++ MSF 7.00";
234
235 if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
236 return 1;
237 }
238
239 if (pdb_reader_ds_init(r, data)) {
240 return 1;
241 }
242
243 r->ds.root = pdb_ds_read_file(r, 1);
244 if (!r->ds.root) {
245 err = 1;
246 goto out_ds;
247 }
248
249 if (pdb_init_symbols(r)) {
250 err = 1;
251 goto out_root;
252 }
253
254 if (pdb_init_segments(r)) {
255 err = 1;
256 goto out_sym;
257 }
258
259 return 0;
260
261out_sym:
262 pdb_exit_symbols(r);
263out_root:
264 free(r->ds.root);
265out_ds:
266 pdb_reader_ds_exit(r);
267
268 return err;
269}
270
271static void pdb_reader_exit(struct pdb_reader *r)
272{
273 pdb_exit_segments(r);
274 pdb_exit_symbols(r);
275 free(r->ds.root);
276 pdb_reader_ds_exit(r);
277}
278
279int pdb_init_from_file(const char *name, struct pdb_reader *reader)
280{
281 GError *gerr = NULL;
282 int err = 0;
283 void *map;
284
285 reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
286 if (gerr) {
287 eprintf("Failed to map PDB file \'%s\'\n", name);
288 return 1;
289 }
290
291 reader->file_size = g_mapped_file_get_length(reader->gmf);
292 map = g_mapped_file_get_contents(reader->gmf);
293 if (pdb_reader_init(reader, map)) {
294 err = 1;
295 goto out_unmap;
296 }
297
298 return 0;
299
300out_unmap:
301 g_mapped_file_unref(reader->gmf);
302
303 return err;
304}
305
306void pdb_exit(struct pdb_reader *reader)
307{
308 g_mapped_file_unref(reader->gmf);
309 pdb_reader_exit(reader);
310}
311