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