xref: /qemu/contrib/elf2dmp/pdb.c (revision 2aa501af)
13fa2d384SViktor Prutyanov /*
23fa2d384SViktor Prutyanov  * Copyright (c) 2018 Virtuozzo International GmbH
33fa2d384SViktor Prutyanov  *
43fa2d384SViktor Prutyanov  * Based on source of Wine project
53fa2d384SViktor Prutyanov  *
63fa2d384SViktor Prutyanov  * This library is free software; you can redistribute it and/or
73fa2d384SViktor Prutyanov  * modify it under the terms of the GNU Lesser General Public
83fa2d384SViktor Prutyanov  * License as published by the Free Software Foundation; either
93fa2d384SViktor Prutyanov  * version 2.1 of the License, or (at your option) any later version.
103fa2d384SViktor Prutyanov  *
113fa2d384SViktor Prutyanov  * This library is distributed in the hope that it will be useful,
123fa2d384SViktor Prutyanov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
133fa2d384SViktor Prutyanov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
143fa2d384SViktor Prutyanov  * Lesser General Public License for more details.
153fa2d384SViktor Prutyanov  *
163fa2d384SViktor Prutyanov  * You should have received a copy of the GNU Lesser General Public
173fa2d384SViktor Prutyanov  * License along with this library; if not, write to the Free Software
183fa2d384SViktor Prutyanov  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
193fa2d384SViktor Prutyanov  */
203fa2d384SViktor Prutyanov 
213fa2d384SViktor Prutyanov #include "qemu/osdep.h"
222aa501afSAkihiko Odaki #include "qemu/bswap.h"
23bbfff196SMarkus Armbruster 
243fa2d384SViktor Prutyanov #include "pdb.h"
253fa2d384SViktor Prutyanov #include "err.h"
263fa2d384SViktor Prutyanov 
pdb_get_file_size(const struct pdb_reader * r,unsigned idx)273fa2d384SViktor Prutyanov static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
283fa2d384SViktor Prutyanov {
299d9c06b1SViktor Prutyanov     if (idx >= r->ds.toc->num_files) {
309d9c06b1SViktor Prutyanov         return 0;
319d9c06b1SViktor Prutyanov     }
329d9c06b1SViktor Prutyanov 
333fa2d384SViktor Prutyanov     return r->ds.toc->file_size[idx];
343fa2d384SViktor Prutyanov }
353fa2d384SViktor Prutyanov 
get_seg_by_num(struct pdb_reader * r,size_t n)363fa2d384SViktor Prutyanov static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
373fa2d384SViktor Prutyanov {
383fa2d384SViktor Prutyanov     size_t i = 0;
393fa2d384SViktor Prutyanov     char *ptr;
403fa2d384SViktor Prutyanov 
413fa2d384SViktor Prutyanov     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
423fa2d384SViktor Prutyanov         i++;
433fa2d384SViktor Prutyanov         ptr += 8;
443fa2d384SViktor Prutyanov         if (i == n) {
453fa2d384SViktor Prutyanov             break;
463fa2d384SViktor Prutyanov         }
473fa2d384SViktor Prutyanov         ptr += sizeof(pdb_seg);
483fa2d384SViktor Prutyanov     }
493fa2d384SViktor Prutyanov 
503fa2d384SViktor Prutyanov     return (pdb_seg *)ptr;
513fa2d384SViktor Prutyanov }
523fa2d384SViktor Prutyanov 
pdb_find_public_v3_symbol(struct pdb_reader * r,const char * name)533fa2d384SViktor Prutyanov uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
543fa2d384SViktor Prutyanov {
553fa2d384SViktor Prutyanov     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
563fa2d384SViktor Prutyanov     int length;
573fa2d384SViktor Prutyanov     const union codeview_symbol *sym;
583fa2d384SViktor Prutyanov     const uint8_t *root = r->modimage;
593fa2d384SViktor Prutyanov     size_t i;
603fa2d384SViktor Prutyanov 
613fa2d384SViktor Prutyanov     for (i = 0; i < size; i += length) {
623fa2d384SViktor Prutyanov         sym = (const void *)(root + i);
633fa2d384SViktor Prutyanov         length = sym->generic.len + 2;
643fa2d384SViktor Prutyanov 
653fa2d384SViktor Prutyanov         if (!sym->generic.id || length < 4) {
663fa2d384SViktor Prutyanov             break;
673fa2d384SViktor Prutyanov         }
683fa2d384SViktor Prutyanov 
693fa2d384SViktor Prutyanov         if (sym->generic.id == S_PUB_V3 &&
703fa2d384SViktor Prutyanov                 !strcmp(name, sym->public_v3.name)) {
713fa2d384SViktor Prutyanov             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
723fa2d384SViktor Prutyanov             uint32_t sect_rva = segment->dword[1];
733fa2d384SViktor Prutyanov             uint64_t rva = sect_rva + sym->public_v3.offset;
743fa2d384SViktor Prutyanov 
756ec6e988SViktor Prutyanov             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
763fa2d384SViktor Prutyanov                     sect_rva, sym->public_v3.segment,
773fa2d384SViktor Prutyanov                     ((char *)segment - 8), sym->public_v3.offset, rva);
783fa2d384SViktor Prutyanov             return rva;
793fa2d384SViktor Prutyanov         }
803fa2d384SViktor Prutyanov     }
813fa2d384SViktor Prutyanov 
823fa2d384SViktor Prutyanov     return 0;
833fa2d384SViktor Prutyanov }
843fa2d384SViktor Prutyanov 
pdb_resolve(uint64_t img_base,struct pdb_reader * r,const char * name)853fa2d384SViktor Prutyanov uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
863fa2d384SViktor Prutyanov {
873fa2d384SViktor Prutyanov     uint64_t rva = pdb_find_public_v3_symbol(r, name);
883fa2d384SViktor Prutyanov 
893fa2d384SViktor Prutyanov     if (!rva) {
903fa2d384SViktor Prutyanov         return 0;
913fa2d384SViktor Prutyanov     }
923fa2d384SViktor Prutyanov 
933fa2d384SViktor Prutyanov     return img_base + rva;
943fa2d384SViktor Prutyanov }
953fa2d384SViktor Prutyanov 
pdb_reader_ds_exit(struct pdb_reader * r)963fa2d384SViktor Prutyanov static void pdb_reader_ds_exit(struct pdb_reader *r)
973fa2d384SViktor Prutyanov {
982a052b4eSSuraj Shirvankar     g_free(r->ds.toc);
993fa2d384SViktor Prutyanov }
1003fa2d384SViktor Prutyanov 
pdb_exit_symbols(struct pdb_reader * r)1013fa2d384SViktor Prutyanov static void pdb_exit_symbols(struct pdb_reader *r)
1023fa2d384SViktor Prutyanov {
1032a052b4eSSuraj Shirvankar     g_free(r->modimage);
1042a052b4eSSuraj Shirvankar     g_free(r->symbols);
1053fa2d384SViktor Prutyanov }
1063fa2d384SViktor Prutyanov 
pdb_exit_segments(struct pdb_reader * r)1073fa2d384SViktor Prutyanov static void pdb_exit_segments(struct pdb_reader *r)
1083fa2d384SViktor Prutyanov {
1092a052b4eSSuraj Shirvankar     g_free(r->segs);
1103fa2d384SViktor Prutyanov }
1113fa2d384SViktor Prutyanov 
pdb_ds_read(const PDB_DS_HEADER * header,const uint32_t * block_list,int size)1123fa2d384SViktor Prutyanov static void *pdb_ds_read(const PDB_DS_HEADER *header,
1133fa2d384SViktor Prutyanov         const uint32_t *block_list, int size)
1143fa2d384SViktor Prutyanov {
1153fa2d384SViktor Prutyanov     int i, nBlocks;
1163fa2d384SViktor Prutyanov     uint8_t *buffer;
1173fa2d384SViktor Prutyanov 
1183fa2d384SViktor Prutyanov     if (!size) {
1193fa2d384SViktor Prutyanov         return NULL;
1203fa2d384SViktor Prutyanov     }
1213fa2d384SViktor Prutyanov 
1223fa2d384SViktor Prutyanov     nBlocks = (size + header->block_size - 1) / header->block_size;
1233fa2d384SViktor Prutyanov 
1242a052b4eSSuraj Shirvankar     buffer = g_malloc(nBlocks * header->block_size);
1253fa2d384SViktor Prutyanov 
1263fa2d384SViktor Prutyanov     for (i = 0; i < nBlocks; i++) {
1273fa2d384SViktor Prutyanov         memcpy(buffer + i * header->block_size, (const char *)header +
1283fa2d384SViktor Prutyanov                 block_list[i] * header->block_size, header->block_size);
1293fa2d384SViktor Prutyanov     }
1303fa2d384SViktor Prutyanov 
1313fa2d384SViktor Prutyanov     return buffer;
1323fa2d384SViktor Prutyanov }
1333fa2d384SViktor Prutyanov 
pdb_ds_read_file(struct pdb_reader * r,uint32_t file_number)1343fa2d384SViktor Prutyanov static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
1353fa2d384SViktor Prutyanov {
1363fa2d384SViktor Prutyanov     const uint32_t *block_list;
1373fa2d384SViktor Prutyanov     uint32_t block_size;
1383fa2d384SViktor Prutyanov     const uint32_t *file_size;
1393fa2d384SViktor Prutyanov     size_t i;
1403fa2d384SViktor Prutyanov 
1413fa2d384SViktor Prutyanov     if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
1423fa2d384SViktor Prutyanov         return NULL;
1433fa2d384SViktor Prutyanov     }
1443fa2d384SViktor Prutyanov 
1453fa2d384SViktor Prutyanov     file_size = r->ds.toc->file_size;
1463fa2d384SViktor Prutyanov     r->file_used[file_number / 32] |= 1 << (file_number % 32);
1473fa2d384SViktor Prutyanov 
1483fa2d384SViktor Prutyanov     if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
1493fa2d384SViktor Prutyanov         return NULL;
1503fa2d384SViktor Prutyanov     }
1513fa2d384SViktor Prutyanov 
1523fa2d384SViktor Prutyanov     block_list = file_size + r->ds.toc->num_files;
1533fa2d384SViktor Prutyanov     block_size = r->ds.header->block_size;
1543fa2d384SViktor Prutyanov 
1553fa2d384SViktor Prutyanov     for (i = 0; i < file_number; i++) {
1563fa2d384SViktor Prutyanov         block_list += (file_size[i] + block_size - 1) / block_size;
1573fa2d384SViktor Prutyanov     }
1583fa2d384SViktor Prutyanov 
1593fa2d384SViktor Prutyanov     return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
1603fa2d384SViktor Prutyanov }
1613fa2d384SViktor Prutyanov 
pdb_init_segments(struct pdb_reader * r)162b1250455SAkihiko Odaki static bool pdb_init_segments(struct pdb_reader *r)
1633fa2d384SViktor Prutyanov {
164231f6a7dSViktor Prutyanov     unsigned stream_idx = r->segments;
1653fa2d384SViktor Prutyanov 
1669d9c06b1SViktor Prutyanov     r->segs = pdb_ds_read_file(r, stream_idx);
1679d9c06b1SViktor Prutyanov     if (!r->segs) {
168b1250455SAkihiko Odaki         return false;
1693fa2d384SViktor Prutyanov     }
1703fa2d384SViktor Prutyanov 
1713fa2d384SViktor Prutyanov     r->segs_size = pdb_get_file_size(r, stream_idx);
1729d9c06b1SViktor Prutyanov     if (!r->segs_size) {
173b1250455SAkihiko Odaki         return false;
1749d9c06b1SViktor Prutyanov     }
1753fa2d384SViktor Prutyanov 
176b1250455SAkihiko Odaki     return true;
1773fa2d384SViktor Prutyanov }
1783fa2d384SViktor Prutyanov 
pdb_init_symbols(struct pdb_reader * r)179b1250455SAkihiko Odaki static bool pdb_init_symbols(struct pdb_reader *r)
1803fa2d384SViktor Prutyanov {
1813fa2d384SViktor Prutyanov     PDB_SYMBOLS *symbols;
1823fa2d384SViktor Prutyanov 
1833fa2d384SViktor Prutyanov     symbols = pdb_ds_read_file(r, 3);
1843fa2d384SViktor Prutyanov     if (!symbols) {
185b1250455SAkihiko Odaki         return false;
1863fa2d384SViktor Prutyanov     }
1873fa2d384SViktor Prutyanov 
1883fa2d384SViktor Prutyanov     r->symbols = symbols;
1893fa2d384SViktor Prutyanov 
1902aa501afSAkihiko Odaki     r->segments = lduw_le_p((const char *)symbols + sizeof(PDB_SYMBOLS) +
1913fa2d384SViktor Prutyanov             symbols->module_size + symbols->offset_size +
1923fa2d384SViktor Prutyanov             symbols->hash_size + symbols->srcmodule_size +
193231f6a7dSViktor Prutyanov             symbols->pdbimport_size + symbols->unknown2_size +
194231f6a7dSViktor Prutyanov             offsetof(PDB_STREAM_INDEXES, segments));
1953fa2d384SViktor Prutyanov 
1963fa2d384SViktor Prutyanov     /* Read global symbol table */
1973fa2d384SViktor Prutyanov     r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
1983fa2d384SViktor Prutyanov     if (!r->modimage) {
1993fa2d384SViktor Prutyanov         goto out_symbols;
2003fa2d384SViktor Prutyanov     }
2013fa2d384SViktor Prutyanov 
202b1250455SAkihiko Odaki     return true;
2033fa2d384SViktor Prutyanov 
2043fa2d384SViktor Prutyanov out_symbols:
2052a052b4eSSuraj Shirvankar     g_free(symbols);
2063fa2d384SViktor Prutyanov 
207b1250455SAkihiko Odaki     return false;
2083fa2d384SViktor Prutyanov }
2093fa2d384SViktor Prutyanov 
pdb_reader_ds_init(struct pdb_reader * r,PDB_DS_HEADER * hdr)210b1250455SAkihiko Odaki static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
2113fa2d384SViktor Prutyanov {
212f015cbb5SPeter Maydell     if (hdr->block_size == 0) {
213b1250455SAkihiko Odaki         return false;
214f015cbb5SPeter Maydell     }
215f015cbb5SPeter Maydell 
2163fa2d384SViktor Prutyanov     memset(r->file_used, 0, sizeof(r->file_used));
2173fa2d384SViktor Prutyanov     r->ds.header = hdr;
2183fa2d384SViktor Prutyanov     r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
2193fa2d384SViktor Prutyanov                 hdr->toc_page * hdr->block_size), hdr->toc_size);
2203fa2d384SViktor Prutyanov 
2213fa2d384SViktor Prutyanov     if (!r->ds.toc) {
222b1250455SAkihiko Odaki         return false;
2233fa2d384SViktor Prutyanov     }
2243fa2d384SViktor Prutyanov 
225b1250455SAkihiko Odaki     return true;
2263fa2d384SViktor Prutyanov }
2273fa2d384SViktor Prutyanov 
pdb_reader_init(struct pdb_reader * r,void * data)228b1250455SAkihiko Odaki static bool pdb_reader_init(struct pdb_reader *r, void *data)
2293fa2d384SViktor Prutyanov {
2303fa2d384SViktor Prutyanov     const char pdb7[] = "Microsoft C/C++ MSF 7.00";
2313fa2d384SViktor Prutyanov 
2323fa2d384SViktor Prutyanov     if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
233b1250455SAkihiko Odaki         return false;
2343fa2d384SViktor Prutyanov     }
2353fa2d384SViktor Prutyanov 
236b1250455SAkihiko Odaki     if (!pdb_reader_ds_init(r, data)) {
237b1250455SAkihiko Odaki         return false;
2383fa2d384SViktor Prutyanov     }
2393fa2d384SViktor Prutyanov 
2403fa2d384SViktor Prutyanov     r->ds.root = pdb_ds_read_file(r, 1);
2413fa2d384SViktor Prutyanov     if (!r->ds.root) {
2423fa2d384SViktor Prutyanov         goto out_ds;
2433fa2d384SViktor Prutyanov     }
2443fa2d384SViktor Prutyanov 
245b1250455SAkihiko Odaki     if (!pdb_init_symbols(r)) {
2463fa2d384SViktor Prutyanov         goto out_root;
2473fa2d384SViktor Prutyanov     }
2483fa2d384SViktor Prutyanov 
249b1250455SAkihiko Odaki     if (!pdb_init_segments(r)) {
2503fa2d384SViktor Prutyanov         goto out_sym;
2513fa2d384SViktor Prutyanov     }
2523fa2d384SViktor Prutyanov 
253b1250455SAkihiko Odaki     return true;
2543fa2d384SViktor Prutyanov 
2553fa2d384SViktor Prutyanov out_sym:
2563fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2573fa2d384SViktor Prutyanov out_root:
2582a052b4eSSuraj Shirvankar     g_free(r->ds.root);
2593fa2d384SViktor Prutyanov out_ds:
2603fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2613fa2d384SViktor Prutyanov 
262b1250455SAkihiko Odaki     return false;
2633fa2d384SViktor Prutyanov }
2643fa2d384SViktor Prutyanov 
pdb_reader_exit(struct pdb_reader * r)2653fa2d384SViktor Prutyanov static void pdb_reader_exit(struct pdb_reader *r)
2663fa2d384SViktor Prutyanov {
2673fa2d384SViktor Prutyanov     pdb_exit_segments(r);
2683fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2692a052b4eSSuraj Shirvankar     g_free(r->ds.root);
2703fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2713fa2d384SViktor Prutyanov }
2723fa2d384SViktor Prutyanov 
pdb_init_from_file(const char * name,struct pdb_reader * reader)273b1250455SAkihiko Odaki bool pdb_init_from_file(const char *name, struct pdb_reader *reader)
2743fa2d384SViktor Prutyanov {
2754ea1a21dSViktor Prutyanov     GError *gerr = NULL;
2763fa2d384SViktor Prutyanov     void *map;
2773fa2d384SViktor Prutyanov 
2784ea1a21dSViktor Prutyanov     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
2794ea1a21dSViktor Prutyanov     if (gerr) {
2804ea1a21dSViktor Prutyanov         eprintf("Failed to map PDB file \'%s\'\n", name);
2810c4c8671SPan Nengyuan         g_error_free(gerr);
282b1250455SAkihiko Odaki         return false;
2833fa2d384SViktor Prutyanov     }
2843fa2d384SViktor Prutyanov 
2854ea1a21dSViktor Prutyanov     reader->file_size = g_mapped_file_get_length(reader->gmf);
2864ea1a21dSViktor Prutyanov     map = g_mapped_file_get_contents(reader->gmf);
287b1250455SAkihiko Odaki     if (!pdb_reader_init(reader, map)) {
2883fa2d384SViktor Prutyanov         goto out_unmap;
2893fa2d384SViktor Prutyanov     }
2903fa2d384SViktor Prutyanov 
291b1250455SAkihiko Odaki     return true;
2923fa2d384SViktor Prutyanov 
2933fa2d384SViktor Prutyanov out_unmap:
2944ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
2953fa2d384SViktor Prutyanov 
296b1250455SAkihiko Odaki     return false;
2973fa2d384SViktor Prutyanov }
2983fa2d384SViktor Prutyanov 
pdb_exit(struct pdb_reader * reader)2993fa2d384SViktor Prutyanov void pdb_exit(struct pdb_reader *reader)
3003fa2d384SViktor Prutyanov {
3014ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3023fa2d384SViktor Prutyanov     pdb_reader_exit(reader);
3033fa2d384SViktor Prutyanov }
304