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