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