xref: /qemu/contrib/elf2dmp/pdb.c (revision 2a052b4e)
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"
22bbfff196SMarkus Armbruster 
233fa2d384SViktor Prutyanov #include "pdb.h"
243fa2d384SViktor Prutyanov #include "err.h"
253fa2d384SViktor Prutyanov 
263fa2d384SViktor Prutyanov static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
273fa2d384SViktor Prutyanov {
289d9c06b1SViktor Prutyanov     if (idx >= r->ds.toc->num_files) {
299d9c06b1SViktor Prutyanov         return 0;
309d9c06b1SViktor Prutyanov     }
319d9c06b1SViktor Prutyanov 
323fa2d384SViktor Prutyanov     return r->ds.toc->file_size[idx];
333fa2d384SViktor Prutyanov }
343fa2d384SViktor Prutyanov 
353fa2d384SViktor Prutyanov static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
363fa2d384SViktor Prutyanov {
373fa2d384SViktor Prutyanov     size_t i = 0;
383fa2d384SViktor Prutyanov     char *ptr;
393fa2d384SViktor Prutyanov 
403fa2d384SViktor Prutyanov     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
413fa2d384SViktor Prutyanov         i++;
423fa2d384SViktor Prutyanov         ptr += 8;
433fa2d384SViktor Prutyanov         if (i == n) {
443fa2d384SViktor Prutyanov             break;
453fa2d384SViktor Prutyanov         }
463fa2d384SViktor Prutyanov         ptr += sizeof(pdb_seg);
473fa2d384SViktor Prutyanov     }
483fa2d384SViktor Prutyanov 
493fa2d384SViktor Prutyanov     return (pdb_seg *)ptr;
503fa2d384SViktor Prutyanov }
513fa2d384SViktor Prutyanov 
523fa2d384SViktor Prutyanov uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
533fa2d384SViktor Prutyanov {
543fa2d384SViktor Prutyanov     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
553fa2d384SViktor Prutyanov     int length;
563fa2d384SViktor Prutyanov     const union codeview_symbol *sym;
573fa2d384SViktor Prutyanov     const uint8_t *root = r->modimage;
583fa2d384SViktor Prutyanov     size_t i;
593fa2d384SViktor Prutyanov 
603fa2d384SViktor Prutyanov     for (i = 0; i < size; i += length) {
613fa2d384SViktor Prutyanov         sym = (const void *)(root + i);
623fa2d384SViktor Prutyanov         length = sym->generic.len + 2;
633fa2d384SViktor Prutyanov 
643fa2d384SViktor Prutyanov         if (!sym->generic.id || length < 4) {
653fa2d384SViktor Prutyanov             break;
663fa2d384SViktor Prutyanov         }
673fa2d384SViktor Prutyanov 
683fa2d384SViktor Prutyanov         if (sym->generic.id == S_PUB_V3 &&
693fa2d384SViktor Prutyanov                 !strcmp(name, sym->public_v3.name)) {
703fa2d384SViktor Prutyanov             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
713fa2d384SViktor Prutyanov             uint32_t sect_rva = segment->dword[1];
723fa2d384SViktor Prutyanov             uint64_t rva = sect_rva + sym->public_v3.offset;
733fa2d384SViktor Prutyanov 
746ec6e988SViktor Prutyanov             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
753fa2d384SViktor Prutyanov                     sect_rva, sym->public_v3.segment,
763fa2d384SViktor Prutyanov                     ((char *)segment - 8), sym->public_v3.offset, rva);
773fa2d384SViktor Prutyanov             return rva;
783fa2d384SViktor Prutyanov         }
793fa2d384SViktor Prutyanov     }
803fa2d384SViktor Prutyanov 
813fa2d384SViktor Prutyanov     return 0;
823fa2d384SViktor Prutyanov }
833fa2d384SViktor Prutyanov 
843fa2d384SViktor Prutyanov uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
853fa2d384SViktor Prutyanov {
863fa2d384SViktor Prutyanov     uint64_t rva = pdb_find_public_v3_symbol(r, name);
873fa2d384SViktor Prutyanov 
883fa2d384SViktor Prutyanov     if (!rva) {
893fa2d384SViktor Prutyanov         return 0;
903fa2d384SViktor Prutyanov     }
913fa2d384SViktor Prutyanov 
923fa2d384SViktor Prutyanov     return img_base + rva;
933fa2d384SViktor Prutyanov }
943fa2d384SViktor Prutyanov 
953fa2d384SViktor Prutyanov static void pdb_reader_ds_exit(struct pdb_reader *r)
963fa2d384SViktor Prutyanov {
972a052b4eSSuraj Shirvankar     g_free(r->ds.toc);
983fa2d384SViktor Prutyanov }
993fa2d384SViktor Prutyanov 
1003fa2d384SViktor Prutyanov static void pdb_exit_symbols(struct pdb_reader *r)
1013fa2d384SViktor Prutyanov {
1022a052b4eSSuraj Shirvankar     g_free(r->modimage);
1032a052b4eSSuraj Shirvankar     g_free(r->symbols);
1043fa2d384SViktor Prutyanov }
1053fa2d384SViktor Prutyanov 
1063fa2d384SViktor Prutyanov static void pdb_exit_segments(struct pdb_reader *r)
1073fa2d384SViktor Prutyanov {
1082a052b4eSSuraj Shirvankar     g_free(r->segs);
1093fa2d384SViktor Prutyanov }
1103fa2d384SViktor Prutyanov 
1113fa2d384SViktor Prutyanov static void *pdb_ds_read(const PDB_DS_HEADER *header,
1123fa2d384SViktor Prutyanov         const uint32_t *block_list, int size)
1133fa2d384SViktor Prutyanov {
1143fa2d384SViktor Prutyanov     int i, nBlocks;
1153fa2d384SViktor Prutyanov     uint8_t *buffer;
1163fa2d384SViktor Prutyanov 
1173fa2d384SViktor Prutyanov     if (!size) {
1183fa2d384SViktor Prutyanov         return NULL;
1193fa2d384SViktor Prutyanov     }
1203fa2d384SViktor Prutyanov 
1213fa2d384SViktor Prutyanov     nBlocks = (size + header->block_size - 1) / header->block_size;
1223fa2d384SViktor Prutyanov 
1232a052b4eSSuraj Shirvankar     buffer = g_malloc(nBlocks * header->block_size);
1243fa2d384SViktor Prutyanov 
1253fa2d384SViktor Prutyanov     for (i = 0; i < nBlocks; i++) {
1263fa2d384SViktor Prutyanov         memcpy(buffer + i * header->block_size, (const char *)header +
1273fa2d384SViktor Prutyanov                 block_list[i] * header->block_size, header->block_size);
1283fa2d384SViktor Prutyanov     }
1293fa2d384SViktor Prutyanov 
1303fa2d384SViktor Prutyanov     return buffer;
1313fa2d384SViktor Prutyanov }
1323fa2d384SViktor Prutyanov 
1333fa2d384SViktor Prutyanov static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
1343fa2d384SViktor Prutyanov {
1353fa2d384SViktor Prutyanov     const uint32_t *block_list;
1363fa2d384SViktor Prutyanov     uint32_t block_size;
1373fa2d384SViktor Prutyanov     const uint32_t *file_size;
1383fa2d384SViktor Prutyanov     size_t i;
1393fa2d384SViktor Prutyanov 
1403fa2d384SViktor Prutyanov     if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
1413fa2d384SViktor Prutyanov         return NULL;
1423fa2d384SViktor Prutyanov     }
1433fa2d384SViktor Prutyanov 
1443fa2d384SViktor Prutyanov     file_size = r->ds.toc->file_size;
1453fa2d384SViktor Prutyanov     r->file_used[file_number / 32] |= 1 << (file_number % 32);
1463fa2d384SViktor Prutyanov 
1473fa2d384SViktor Prutyanov     if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
1483fa2d384SViktor Prutyanov         return NULL;
1493fa2d384SViktor Prutyanov     }
1503fa2d384SViktor Prutyanov 
1513fa2d384SViktor Prutyanov     block_list = file_size + r->ds.toc->num_files;
1523fa2d384SViktor Prutyanov     block_size = r->ds.header->block_size;
1533fa2d384SViktor Prutyanov 
1543fa2d384SViktor Prutyanov     for (i = 0; i < file_number; i++) {
1553fa2d384SViktor Prutyanov         block_list += (file_size[i] + block_size - 1) / block_size;
1563fa2d384SViktor Prutyanov     }
1573fa2d384SViktor Prutyanov 
1583fa2d384SViktor Prutyanov     return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
1593fa2d384SViktor Prutyanov }
1603fa2d384SViktor Prutyanov 
1613fa2d384SViktor Prutyanov static int pdb_init_segments(struct pdb_reader *r)
1623fa2d384SViktor Prutyanov {
163231f6a7dSViktor Prutyanov     unsigned stream_idx = r->segments;
1643fa2d384SViktor Prutyanov 
1659d9c06b1SViktor Prutyanov     r->segs = pdb_ds_read_file(r, stream_idx);
1669d9c06b1SViktor Prutyanov     if (!r->segs) {
1673fa2d384SViktor Prutyanov         return 1;
1683fa2d384SViktor Prutyanov     }
1693fa2d384SViktor Prutyanov 
1703fa2d384SViktor Prutyanov     r->segs_size = pdb_get_file_size(r, stream_idx);
1719d9c06b1SViktor Prutyanov     if (!r->segs_size) {
1729d9c06b1SViktor Prutyanov         return 1;
1739d9c06b1SViktor Prutyanov     }
1743fa2d384SViktor Prutyanov 
1753fa2d384SViktor Prutyanov     return 0;
1763fa2d384SViktor Prutyanov }
1773fa2d384SViktor Prutyanov 
1783fa2d384SViktor Prutyanov static int pdb_init_symbols(struct pdb_reader *r)
1793fa2d384SViktor Prutyanov {
1803fa2d384SViktor Prutyanov     int err = 0;
1813fa2d384SViktor Prutyanov     PDB_SYMBOLS *symbols;
1823fa2d384SViktor Prutyanov 
1833fa2d384SViktor Prutyanov     symbols = pdb_ds_read_file(r, 3);
1843fa2d384SViktor Prutyanov     if (!symbols) {
1853fa2d384SViktor Prutyanov         return 1;
1863fa2d384SViktor Prutyanov     }
1873fa2d384SViktor Prutyanov 
1883fa2d384SViktor Prutyanov     r->symbols = symbols;
1893fa2d384SViktor Prutyanov 
190231f6a7dSViktor Prutyanov     r->segments = *(uint16_t *)((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         err = 1;
2003fa2d384SViktor Prutyanov         goto out_symbols;
2013fa2d384SViktor Prutyanov     }
2023fa2d384SViktor Prutyanov 
2033fa2d384SViktor Prutyanov     return 0;
2043fa2d384SViktor Prutyanov 
2053fa2d384SViktor Prutyanov out_symbols:
2062a052b4eSSuraj Shirvankar     g_free(symbols);
2073fa2d384SViktor Prutyanov 
2083fa2d384SViktor Prutyanov     return err;
2093fa2d384SViktor Prutyanov }
2103fa2d384SViktor Prutyanov 
2113fa2d384SViktor Prutyanov static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
2123fa2d384SViktor Prutyanov {
213f015cbb5SPeter Maydell     if (hdr->block_size == 0) {
214f015cbb5SPeter Maydell         return 1;
215f015cbb5SPeter Maydell     }
216f015cbb5SPeter Maydell 
2173fa2d384SViktor Prutyanov     memset(r->file_used, 0, sizeof(r->file_used));
2183fa2d384SViktor Prutyanov     r->ds.header = hdr;
2193fa2d384SViktor Prutyanov     r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
2203fa2d384SViktor Prutyanov                 hdr->toc_page * hdr->block_size), hdr->toc_size);
2213fa2d384SViktor Prutyanov 
2223fa2d384SViktor Prutyanov     if (!r->ds.toc) {
2233fa2d384SViktor Prutyanov         return 1;
2243fa2d384SViktor Prutyanov     }
2253fa2d384SViktor Prutyanov 
2263fa2d384SViktor Prutyanov     return 0;
2273fa2d384SViktor Prutyanov }
2283fa2d384SViktor Prutyanov 
2293fa2d384SViktor Prutyanov static int pdb_reader_init(struct pdb_reader *r, void *data)
2303fa2d384SViktor Prutyanov {
2313fa2d384SViktor Prutyanov     int err = 0;
2323fa2d384SViktor Prutyanov     const char pdb7[] = "Microsoft C/C++ MSF 7.00";
2333fa2d384SViktor Prutyanov 
2343fa2d384SViktor Prutyanov     if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
2353fa2d384SViktor Prutyanov         return 1;
2363fa2d384SViktor Prutyanov     }
2373fa2d384SViktor Prutyanov 
2383fa2d384SViktor Prutyanov     if (pdb_reader_ds_init(r, data)) {
2393fa2d384SViktor Prutyanov         return 1;
2403fa2d384SViktor Prutyanov     }
2413fa2d384SViktor Prutyanov 
2423fa2d384SViktor Prutyanov     r->ds.root = pdb_ds_read_file(r, 1);
2433fa2d384SViktor Prutyanov     if (!r->ds.root) {
2443fa2d384SViktor Prutyanov         err = 1;
2453fa2d384SViktor Prutyanov         goto out_ds;
2463fa2d384SViktor Prutyanov     }
2473fa2d384SViktor Prutyanov 
2483fa2d384SViktor Prutyanov     if (pdb_init_symbols(r)) {
2493fa2d384SViktor Prutyanov         err = 1;
2503fa2d384SViktor Prutyanov         goto out_root;
2513fa2d384SViktor Prutyanov     }
2523fa2d384SViktor Prutyanov 
2533fa2d384SViktor Prutyanov     if (pdb_init_segments(r)) {
2543fa2d384SViktor Prutyanov         err = 1;
2553fa2d384SViktor Prutyanov         goto out_sym;
2563fa2d384SViktor Prutyanov     }
2573fa2d384SViktor Prutyanov 
2583fa2d384SViktor Prutyanov     return 0;
2593fa2d384SViktor Prutyanov 
2603fa2d384SViktor Prutyanov out_sym:
2613fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2623fa2d384SViktor Prutyanov out_root:
2632a052b4eSSuraj Shirvankar     g_free(r->ds.root);
2643fa2d384SViktor Prutyanov out_ds:
2653fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2663fa2d384SViktor Prutyanov 
2673fa2d384SViktor Prutyanov     return err;
2683fa2d384SViktor Prutyanov }
2693fa2d384SViktor Prutyanov 
2703fa2d384SViktor Prutyanov static void pdb_reader_exit(struct pdb_reader *r)
2713fa2d384SViktor Prutyanov {
2723fa2d384SViktor Prutyanov     pdb_exit_segments(r);
2733fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2742a052b4eSSuraj Shirvankar     g_free(r->ds.root);
2753fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2763fa2d384SViktor Prutyanov }
2773fa2d384SViktor Prutyanov 
2783fa2d384SViktor Prutyanov int pdb_init_from_file(const char *name, struct pdb_reader *reader)
2793fa2d384SViktor Prutyanov {
2804ea1a21dSViktor Prutyanov     GError *gerr = NULL;
2813fa2d384SViktor Prutyanov     int err = 0;
2823fa2d384SViktor Prutyanov     void *map;
2833fa2d384SViktor Prutyanov 
2844ea1a21dSViktor Prutyanov     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
2854ea1a21dSViktor Prutyanov     if (gerr) {
2864ea1a21dSViktor Prutyanov         eprintf("Failed to map PDB file \'%s\'\n", name);
2870c4c8671SPan Nengyuan         g_error_free(gerr);
2883fa2d384SViktor Prutyanov         return 1;
2893fa2d384SViktor Prutyanov     }
2903fa2d384SViktor Prutyanov 
2914ea1a21dSViktor Prutyanov     reader->file_size = g_mapped_file_get_length(reader->gmf);
2924ea1a21dSViktor Prutyanov     map = g_mapped_file_get_contents(reader->gmf);
2933fa2d384SViktor Prutyanov     if (pdb_reader_init(reader, map)) {
2943fa2d384SViktor Prutyanov         err = 1;
2953fa2d384SViktor Prutyanov         goto out_unmap;
2963fa2d384SViktor Prutyanov     }
2973fa2d384SViktor Prutyanov 
2983fa2d384SViktor Prutyanov     return 0;
2993fa2d384SViktor Prutyanov 
3003fa2d384SViktor Prutyanov out_unmap:
3014ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3023fa2d384SViktor Prutyanov 
3033fa2d384SViktor Prutyanov     return err;
3043fa2d384SViktor Prutyanov }
3053fa2d384SViktor Prutyanov 
3063fa2d384SViktor Prutyanov void pdb_exit(struct pdb_reader *reader)
3073fa2d384SViktor Prutyanov {
3084ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3093fa2d384SViktor Prutyanov     pdb_reader_exit(reader);
3103fa2d384SViktor Prutyanov }
311