xref: /qemu/contrib/elf2dmp/pdb.c (revision f015cbb5)
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 {
283fa2d384SViktor Prutyanov     return r->ds.toc->file_size[idx];
293fa2d384SViktor Prutyanov }
303fa2d384SViktor Prutyanov 
313fa2d384SViktor Prutyanov static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
323fa2d384SViktor Prutyanov {
333fa2d384SViktor Prutyanov     size_t i = 0;
343fa2d384SViktor Prutyanov     char *ptr;
353fa2d384SViktor Prutyanov 
363fa2d384SViktor Prutyanov     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
373fa2d384SViktor Prutyanov         i++;
383fa2d384SViktor Prutyanov         ptr += 8;
393fa2d384SViktor Prutyanov         if (i == n) {
403fa2d384SViktor Prutyanov             break;
413fa2d384SViktor Prutyanov         }
423fa2d384SViktor Prutyanov         ptr += sizeof(pdb_seg);
433fa2d384SViktor Prutyanov     }
443fa2d384SViktor Prutyanov 
453fa2d384SViktor Prutyanov     return (pdb_seg *)ptr;
463fa2d384SViktor Prutyanov }
473fa2d384SViktor Prutyanov 
483fa2d384SViktor Prutyanov uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
493fa2d384SViktor Prutyanov {
503fa2d384SViktor Prutyanov     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
513fa2d384SViktor Prutyanov     int length;
523fa2d384SViktor Prutyanov     const union codeview_symbol *sym;
533fa2d384SViktor Prutyanov     const uint8_t *root = r->modimage;
543fa2d384SViktor Prutyanov     size_t i;
553fa2d384SViktor Prutyanov 
563fa2d384SViktor Prutyanov     for (i = 0; i < size; i += length) {
573fa2d384SViktor Prutyanov         sym = (const void *)(root + i);
583fa2d384SViktor Prutyanov         length = sym->generic.len + 2;
593fa2d384SViktor Prutyanov 
603fa2d384SViktor Prutyanov         if (!sym->generic.id || length < 4) {
613fa2d384SViktor Prutyanov             break;
623fa2d384SViktor Prutyanov         }
633fa2d384SViktor Prutyanov 
643fa2d384SViktor Prutyanov         if (sym->generic.id == S_PUB_V3 &&
653fa2d384SViktor Prutyanov                 !strcmp(name, sym->public_v3.name)) {
663fa2d384SViktor Prutyanov             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
673fa2d384SViktor Prutyanov             uint32_t sect_rva = segment->dword[1];
683fa2d384SViktor Prutyanov             uint64_t rva = sect_rva + sym->public_v3.offset;
693fa2d384SViktor Prutyanov 
706ec6e988SViktor Prutyanov             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
713fa2d384SViktor Prutyanov                     sect_rva, sym->public_v3.segment,
723fa2d384SViktor Prutyanov                     ((char *)segment - 8), sym->public_v3.offset, rva);
733fa2d384SViktor Prutyanov             return rva;
743fa2d384SViktor Prutyanov         }
753fa2d384SViktor Prutyanov     }
763fa2d384SViktor Prutyanov 
773fa2d384SViktor Prutyanov     return 0;
783fa2d384SViktor Prutyanov }
793fa2d384SViktor Prutyanov 
803fa2d384SViktor Prutyanov uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
813fa2d384SViktor Prutyanov {
823fa2d384SViktor Prutyanov     uint64_t rva = pdb_find_public_v3_symbol(r, name);
833fa2d384SViktor Prutyanov 
843fa2d384SViktor Prutyanov     if (!rva) {
853fa2d384SViktor Prutyanov         return 0;
863fa2d384SViktor Prutyanov     }
873fa2d384SViktor Prutyanov 
883fa2d384SViktor Prutyanov     return img_base + rva;
893fa2d384SViktor Prutyanov }
903fa2d384SViktor Prutyanov 
913fa2d384SViktor Prutyanov static void pdb_reader_ds_exit(struct pdb_reader *r)
923fa2d384SViktor Prutyanov {
933fa2d384SViktor Prutyanov     free(r->ds.toc);
943fa2d384SViktor Prutyanov }
953fa2d384SViktor Prutyanov 
963fa2d384SViktor Prutyanov static void pdb_exit_symbols(struct pdb_reader *r)
973fa2d384SViktor Prutyanov {
983fa2d384SViktor Prutyanov     free(r->modimage);
993fa2d384SViktor Prutyanov     free(r->symbols);
1003fa2d384SViktor Prutyanov }
1013fa2d384SViktor Prutyanov 
1023fa2d384SViktor Prutyanov static void pdb_exit_segments(struct pdb_reader *r)
1033fa2d384SViktor Prutyanov {
1043fa2d384SViktor Prutyanov     free(r->segs);
1053fa2d384SViktor Prutyanov }
1063fa2d384SViktor Prutyanov 
1073fa2d384SViktor Prutyanov static void *pdb_ds_read(const PDB_DS_HEADER *header,
1083fa2d384SViktor Prutyanov         const uint32_t *block_list, int size)
1093fa2d384SViktor Prutyanov {
1103fa2d384SViktor Prutyanov     int i, nBlocks;
1113fa2d384SViktor Prutyanov     uint8_t *buffer;
1123fa2d384SViktor Prutyanov 
1133fa2d384SViktor Prutyanov     if (!size) {
1143fa2d384SViktor Prutyanov         return NULL;
1153fa2d384SViktor Prutyanov     }
1163fa2d384SViktor Prutyanov 
1173fa2d384SViktor Prutyanov     nBlocks = (size + header->block_size - 1) / header->block_size;
1183fa2d384SViktor Prutyanov 
1193fa2d384SViktor Prutyanov     buffer = malloc(nBlocks * header->block_size);
1203fa2d384SViktor Prutyanov     if (!buffer) {
1213fa2d384SViktor Prutyanov         return NULL;
1223fa2d384SViktor Prutyanov     }
1233fa2d384SViktor Prutyanov 
1243fa2d384SViktor Prutyanov     for (i = 0; i < nBlocks; i++) {
1253fa2d384SViktor Prutyanov         memcpy(buffer + i * header->block_size, (const char *)header +
1263fa2d384SViktor Prutyanov                 block_list[i] * header->block_size, header->block_size);
1273fa2d384SViktor Prutyanov     }
1283fa2d384SViktor Prutyanov 
1293fa2d384SViktor Prutyanov     return buffer;
1303fa2d384SViktor Prutyanov }
1313fa2d384SViktor Prutyanov 
1323fa2d384SViktor Prutyanov static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
1333fa2d384SViktor Prutyanov {
1343fa2d384SViktor Prutyanov     const uint32_t *block_list;
1353fa2d384SViktor Prutyanov     uint32_t block_size;
1363fa2d384SViktor Prutyanov     const uint32_t *file_size;
1373fa2d384SViktor Prutyanov     size_t i;
1383fa2d384SViktor Prutyanov 
1393fa2d384SViktor Prutyanov     if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
1403fa2d384SViktor Prutyanov         return NULL;
1413fa2d384SViktor Prutyanov     }
1423fa2d384SViktor Prutyanov 
1433fa2d384SViktor Prutyanov     file_size = r->ds.toc->file_size;
1443fa2d384SViktor Prutyanov     r->file_used[file_number / 32] |= 1 << (file_number % 32);
1453fa2d384SViktor Prutyanov 
1463fa2d384SViktor Prutyanov     if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
1473fa2d384SViktor Prutyanov         return NULL;
1483fa2d384SViktor Prutyanov     }
1493fa2d384SViktor Prutyanov 
1503fa2d384SViktor Prutyanov     block_list = file_size + r->ds.toc->num_files;
1513fa2d384SViktor Prutyanov     block_size = r->ds.header->block_size;
1523fa2d384SViktor Prutyanov 
1533fa2d384SViktor Prutyanov     for (i = 0; i < file_number; i++) {
1543fa2d384SViktor Prutyanov         block_list += (file_size[i] + block_size - 1) / block_size;
1553fa2d384SViktor Prutyanov     }
1563fa2d384SViktor Prutyanov 
1573fa2d384SViktor Prutyanov     return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
1583fa2d384SViktor Prutyanov }
1593fa2d384SViktor Prutyanov 
1603fa2d384SViktor Prutyanov static int pdb_init_segments(struct pdb_reader *r)
1613fa2d384SViktor Prutyanov {
1623fa2d384SViktor Prutyanov     char *segs;
1633fa2d384SViktor Prutyanov     unsigned stream_idx = r->sidx.segments;
1643fa2d384SViktor Prutyanov 
1653fa2d384SViktor Prutyanov     segs = pdb_ds_read_file(r, stream_idx);
1663fa2d384SViktor Prutyanov     if (!segs) {
1673fa2d384SViktor Prutyanov         return 1;
1683fa2d384SViktor Prutyanov     }
1693fa2d384SViktor Prutyanov 
1703fa2d384SViktor Prutyanov     r->segs = segs;
1713fa2d384SViktor Prutyanov     r->segs_size = pdb_get_file_size(r, stream_idx);
1723fa2d384SViktor Prutyanov 
1733fa2d384SViktor Prutyanov     return 0;
1743fa2d384SViktor Prutyanov }
1753fa2d384SViktor Prutyanov 
1763fa2d384SViktor Prutyanov static int pdb_init_symbols(struct pdb_reader *r)
1773fa2d384SViktor Prutyanov {
1783fa2d384SViktor Prutyanov     int err = 0;
1793fa2d384SViktor Prutyanov     PDB_SYMBOLS *symbols;
1803fa2d384SViktor Prutyanov     PDB_STREAM_INDEXES *sidx = &r->sidx;
1813fa2d384SViktor Prutyanov 
1823fa2d384SViktor Prutyanov     memset(sidx, -1, sizeof(*sidx));
1833fa2d384SViktor Prutyanov 
1843fa2d384SViktor Prutyanov     symbols = pdb_ds_read_file(r, 3);
1853fa2d384SViktor Prutyanov     if (!symbols) {
1863fa2d384SViktor Prutyanov         return 1;
1873fa2d384SViktor Prutyanov     }
1883fa2d384SViktor Prutyanov 
1893fa2d384SViktor Prutyanov     r->symbols = symbols;
1903fa2d384SViktor Prutyanov 
1913fa2d384SViktor Prutyanov     if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
1923fa2d384SViktor Prutyanov         err = 1;
1933fa2d384SViktor Prutyanov         goto out_symbols;
1943fa2d384SViktor Prutyanov     }
1953fa2d384SViktor Prutyanov 
1963fa2d384SViktor Prutyanov     memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
1973fa2d384SViktor Prutyanov             symbols->module_size + symbols->offset_size +
1983fa2d384SViktor Prutyanov             symbols->hash_size + symbols->srcmodule_size +
1993fa2d384SViktor Prutyanov             symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
2003fa2d384SViktor Prutyanov 
2013fa2d384SViktor Prutyanov     /* Read global symbol table */
2023fa2d384SViktor Prutyanov     r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
2033fa2d384SViktor Prutyanov     if (!r->modimage) {
2043fa2d384SViktor Prutyanov         err = 1;
2053fa2d384SViktor Prutyanov         goto out_symbols;
2063fa2d384SViktor Prutyanov     }
2073fa2d384SViktor Prutyanov 
2083fa2d384SViktor Prutyanov     return 0;
2093fa2d384SViktor Prutyanov 
2103fa2d384SViktor Prutyanov out_symbols:
2113fa2d384SViktor Prutyanov     free(symbols);
2123fa2d384SViktor Prutyanov 
2133fa2d384SViktor Prutyanov     return err;
2143fa2d384SViktor Prutyanov }
2153fa2d384SViktor Prutyanov 
2163fa2d384SViktor Prutyanov static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
2173fa2d384SViktor Prutyanov {
218*f015cbb5SPeter Maydell     if (hdr->block_size == 0) {
219*f015cbb5SPeter Maydell         return 1;
220*f015cbb5SPeter Maydell     }
221*f015cbb5SPeter Maydell 
2223fa2d384SViktor Prutyanov     memset(r->file_used, 0, sizeof(r->file_used));
2233fa2d384SViktor Prutyanov     r->ds.header = hdr;
2243fa2d384SViktor Prutyanov     r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
2253fa2d384SViktor Prutyanov                 hdr->toc_page * hdr->block_size), hdr->toc_size);
2263fa2d384SViktor Prutyanov 
2273fa2d384SViktor Prutyanov     if (!r->ds.toc) {
2283fa2d384SViktor Prutyanov         return 1;
2293fa2d384SViktor Prutyanov     }
2303fa2d384SViktor Prutyanov 
2313fa2d384SViktor Prutyanov     return 0;
2323fa2d384SViktor Prutyanov }
2333fa2d384SViktor Prutyanov 
2343fa2d384SViktor Prutyanov static int pdb_reader_init(struct pdb_reader *r, void *data)
2353fa2d384SViktor Prutyanov {
2363fa2d384SViktor Prutyanov     int err = 0;
2373fa2d384SViktor Prutyanov     const char pdb7[] = "Microsoft C/C++ MSF 7.00";
2383fa2d384SViktor Prutyanov 
2393fa2d384SViktor Prutyanov     if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
2403fa2d384SViktor Prutyanov         return 1;
2413fa2d384SViktor Prutyanov     }
2423fa2d384SViktor Prutyanov 
2433fa2d384SViktor Prutyanov     if (pdb_reader_ds_init(r, data)) {
2443fa2d384SViktor Prutyanov         return 1;
2453fa2d384SViktor Prutyanov     }
2463fa2d384SViktor Prutyanov 
2473fa2d384SViktor Prutyanov     r->ds.root = pdb_ds_read_file(r, 1);
2483fa2d384SViktor Prutyanov     if (!r->ds.root) {
2493fa2d384SViktor Prutyanov         err = 1;
2503fa2d384SViktor Prutyanov         goto out_ds;
2513fa2d384SViktor Prutyanov     }
2523fa2d384SViktor Prutyanov 
2533fa2d384SViktor Prutyanov     if (pdb_init_symbols(r)) {
2543fa2d384SViktor Prutyanov         err = 1;
2553fa2d384SViktor Prutyanov         goto out_root;
2563fa2d384SViktor Prutyanov     }
2573fa2d384SViktor Prutyanov 
2583fa2d384SViktor Prutyanov     if (pdb_init_segments(r)) {
2593fa2d384SViktor Prutyanov         err = 1;
2603fa2d384SViktor Prutyanov         goto out_sym;
2613fa2d384SViktor Prutyanov     }
2623fa2d384SViktor Prutyanov 
2633fa2d384SViktor Prutyanov     return 0;
2643fa2d384SViktor Prutyanov 
2653fa2d384SViktor Prutyanov out_sym:
2663fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2673fa2d384SViktor Prutyanov out_root:
2683fa2d384SViktor Prutyanov     free(r->ds.root);
2693fa2d384SViktor Prutyanov out_ds:
2703fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2713fa2d384SViktor Prutyanov 
2723fa2d384SViktor Prutyanov     return err;
2733fa2d384SViktor Prutyanov }
2743fa2d384SViktor Prutyanov 
2753fa2d384SViktor Prutyanov static void pdb_reader_exit(struct pdb_reader *r)
2763fa2d384SViktor Prutyanov {
2773fa2d384SViktor Prutyanov     pdb_exit_segments(r);
2783fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2793fa2d384SViktor Prutyanov     free(r->ds.root);
2803fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2813fa2d384SViktor Prutyanov }
2823fa2d384SViktor Prutyanov 
2833fa2d384SViktor Prutyanov int pdb_init_from_file(const char *name, struct pdb_reader *reader)
2843fa2d384SViktor Prutyanov {
2854ea1a21dSViktor Prutyanov     GError *gerr = NULL;
2863fa2d384SViktor Prutyanov     int err = 0;
2873fa2d384SViktor Prutyanov     void *map;
2883fa2d384SViktor Prutyanov 
2894ea1a21dSViktor Prutyanov     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
2904ea1a21dSViktor Prutyanov     if (gerr) {
2914ea1a21dSViktor Prutyanov         eprintf("Failed to map PDB file \'%s\'\n", name);
2920c4c8671SPan Nengyuan         g_error_free(gerr);
2933fa2d384SViktor Prutyanov         return 1;
2943fa2d384SViktor Prutyanov     }
2953fa2d384SViktor Prutyanov 
2964ea1a21dSViktor Prutyanov     reader->file_size = g_mapped_file_get_length(reader->gmf);
2974ea1a21dSViktor Prutyanov     map = g_mapped_file_get_contents(reader->gmf);
2983fa2d384SViktor Prutyanov     if (pdb_reader_init(reader, map)) {
2993fa2d384SViktor Prutyanov         err = 1;
3003fa2d384SViktor Prutyanov         goto out_unmap;
3013fa2d384SViktor Prutyanov     }
3023fa2d384SViktor Prutyanov 
3033fa2d384SViktor Prutyanov     return 0;
3043fa2d384SViktor Prutyanov 
3053fa2d384SViktor Prutyanov out_unmap:
3064ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3073fa2d384SViktor Prutyanov 
3083fa2d384SViktor Prutyanov     return err;
3093fa2d384SViktor Prutyanov }
3103fa2d384SViktor Prutyanov 
3113fa2d384SViktor Prutyanov void pdb_exit(struct pdb_reader *reader)
3123fa2d384SViktor Prutyanov {
3134ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3143fa2d384SViktor Prutyanov     pdb_reader_exit(reader);
3153fa2d384SViktor Prutyanov }
316