xref: /qemu/contrib/elf2dmp/pdb.c (revision 6ec6e988)
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 
21*6ec6e988SViktor Prutyanov #include <inttypes.h>
22*6ec6e988SViktor Prutyanov 
233fa2d384SViktor Prutyanov #include "qemu/osdep.h"
243fa2d384SViktor Prutyanov #include "pdb.h"
253fa2d384SViktor Prutyanov #include "err.h"
263fa2d384SViktor Prutyanov 
273fa2d384SViktor Prutyanov static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
283fa2d384SViktor Prutyanov {
293fa2d384SViktor Prutyanov     return r->ds.toc->file_size[idx];
303fa2d384SViktor Prutyanov }
313fa2d384SViktor Prutyanov 
323fa2d384SViktor Prutyanov static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
333fa2d384SViktor Prutyanov {
343fa2d384SViktor Prutyanov     size_t i = 0;
353fa2d384SViktor Prutyanov     char *ptr;
363fa2d384SViktor Prutyanov 
373fa2d384SViktor Prutyanov     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
383fa2d384SViktor Prutyanov         i++;
393fa2d384SViktor Prutyanov         ptr += 8;
403fa2d384SViktor Prutyanov         if (i == n) {
413fa2d384SViktor Prutyanov             break;
423fa2d384SViktor Prutyanov         }
433fa2d384SViktor Prutyanov         ptr += sizeof(pdb_seg);
443fa2d384SViktor Prutyanov     }
453fa2d384SViktor Prutyanov 
463fa2d384SViktor Prutyanov     return (pdb_seg *)ptr;
473fa2d384SViktor Prutyanov }
483fa2d384SViktor Prutyanov 
493fa2d384SViktor Prutyanov uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
503fa2d384SViktor Prutyanov {
513fa2d384SViktor Prutyanov     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
523fa2d384SViktor Prutyanov     int length;
533fa2d384SViktor Prutyanov     const union codeview_symbol *sym;
543fa2d384SViktor Prutyanov     const uint8_t *root = r->modimage;
553fa2d384SViktor Prutyanov     size_t i;
563fa2d384SViktor Prutyanov 
573fa2d384SViktor Prutyanov     for (i = 0; i < size; i += length) {
583fa2d384SViktor Prutyanov         sym = (const void *)(root + i);
593fa2d384SViktor Prutyanov         length = sym->generic.len + 2;
603fa2d384SViktor Prutyanov 
613fa2d384SViktor Prutyanov         if (!sym->generic.id || length < 4) {
623fa2d384SViktor Prutyanov             break;
633fa2d384SViktor Prutyanov         }
643fa2d384SViktor Prutyanov 
653fa2d384SViktor Prutyanov         if (sym->generic.id == S_PUB_V3 &&
663fa2d384SViktor Prutyanov                 !strcmp(name, sym->public_v3.name)) {
673fa2d384SViktor Prutyanov             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
683fa2d384SViktor Prutyanov             uint32_t sect_rva = segment->dword[1];
693fa2d384SViktor Prutyanov             uint64_t rva = sect_rva + sym->public_v3.offset;
703fa2d384SViktor Prutyanov 
71*6ec6e988SViktor Prutyanov             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
723fa2d384SViktor Prutyanov                     sect_rva, sym->public_v3.segment,
733fa2d384SViktor Prutyanov                     ((char *)segment - 8), sym->public_v3.offset, rva);
743fa2d384SViktor Prutyanov             return rva;
753fa2d384SViktor Prutyanov         }
763fa2d384SViktor Prutyanov     }
773fa2d384SViktor Prutyanov 
783fa2d384SViktor Prutyanov     return 0;
793fa2d384SViktor Prutyanov }
803fa2d384SViktor Prutyanov 
813fa2d384SViktor Prutyanov uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
823fa2d384SViktor Prutyanov {
833fa2d384SViktor Prutyanov     uint64_t rva = pdb_find_public_v3_symbol(r, name);
843fa2d384SViktor Prutyanov 
853fa2d384SViktor Prutyanov     if (!rva) {
863fa2d384SViktor Prutyanov         return 0;
873fa2d384SViktor Prutyanov     }
883fa2d384SViktor Prutyanov 
893fa2d384SViktor Prutyanov     return img_base + rva;
903fa2d384SViktor Prutyanov }
913fa2d384SViktor Prutyanov 
923fa2d384SViktor Prutyanov static void pdb_reader_ds_exit(struct pdb_reader *r)
933fa2d384SViktor Prutyanov {
943fa2d384SViktor Prutyanov     free(r->ds.toc);
953fa2d384SViktor Prutyanov }
963fa2d384SViktor Prutyanov 
973fa2d384SViktor Prutyanov static void pdb_exit_symbols(struct pdb_reader *r)
983fa2d384SViktor Prutyanov {
993fa2d384SViktor Prutyanov     free(r->modimage);
1003fa2d384SViktor Prutyanov     free(r->symbols);
1013fa2d384SViktor Prutyanov }
1023fa2d384SViktor Prutyanov 
1033fa2d384SViktor Prutyanov static void pdb_exit_segments(struct pdb_reader *r)
1043fa2d384SViktor Prutyanov {
1053fa2d384SViktor Prutyanov     free(r->segs);
1063fa2d384SViktor Prutyanov }
1073fa2d384SViktor Prutyanov 
1083fa2d384SViktor Prutyanov static void *pdb_ds_read(const PDB_DS_HEADER *header,
1093fa2d384SViktor Prutyanov         const uint32_t *block_list, int size)
1103fa2d384SViktor Prutyanov {
1113fa2d384SViktor Prutyanov     int i, nBlocks;
1123fa2d384SViktor Prutyanov     uint8_t *buffer;
1133fa2d384SViktor Prutyanov 
1143fa2d384SViktor Prutyanov     if (!size) {
1153fa2d384SViktor Prutyanov         return NULL;
1163fa2d384SViktor Prutyanov     }
1173fa2d384SViktor Prutyanov 
1183fa2d384SViktor Prutyanov     nBlocks = (size + header->block_size - 1) / header->block_size;
1193fa2d384SViktor Prutyanov 
1203fa2d384SViktor Prutyanov     buffer = malloc(nBlocks * header->block_size);
1213fa2d384SViktor Prutyanov     if (!buffer) {
1223fa2d384SViktor Prutyanov         return NULL;
1233fa2d384SViktor Prutyanov     }
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 {
1633fa2d384SViktor Prutyanov     char *segs;
1643fa2d384SViktor Prutyanov     unsigned stream_idx = r->sidx.segments;
1653fa2d384SViktor Prutyanov 
1663fa2d384SViktor Prutyanov     segs = pdb_ds_read_file(r, stream_idx);
1673fa2d384SViktor Prutyanov     if (!segs) {
1683fa2d384SViktor Prutyanov         return 1;
1693fa2d384SViktor Prutyanov     }
1703fa2d384SViktor Prutyanov 
1713fa2d384SViktor Prutyanov     r->segs = segs;
1723fa2d384SViktor Prutyanov     r->segs_size = pdb_get_file_size(r, stream_idx);
1733fa2d384SViktor Prutyanov 
1743fa2d384SViktor Prutyanov     return 0;
1753fa2d384SViktor Prutyanov }
1763fa2d384SViktor Prutyanov 
1773fa2d384SViktor Prutyanov static int pdb_init_symbols(struct pdb_reader *r)
1783fa2d384SViktor Prutyanov {
1793fa2d384SViktor Prutyanov     int err = 0;
1803fa2d384SViktor Prutyanov     PDB_SYMBOLS *symbols;
1813fa2d384SViktor Prutyanov     PDB_STREAM_INDEXES *sidx = &r->sidx;
1823fa2d384SViktor Prutyanov 
1833fa2d384SViktor Prutyanov     memset(sidx, -1, sizeof(*sidx));
1843fa2d384SViktor Prutyanov 
1853fa2d384SViktor Prutyanov     symbols = pdb_ds_read_file(r, 3);
1863fa2d384SViktor Prutyanov     if (!symbols) {
1873fa2d384SViktor Prutyanov         return 1;
1883fa2d384SViktor Prutyanov     }
1893fa2d384SViktor Prutyanov 
1903fa2d384SViktor Prutyanov     r->symbols = symbols;
1913fa2d384SViktor Prutyanov 
1923fa2d384SViktor Prutyanov     if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
1933fa2d384SViktor Prutyanov         err = 1;
1943fa2d384SViktor Prutyanov         goto out_symbols;
1953fa2d384SViktor Prutyanov     }
1963fa2d384SViktor Prutyanov 
1973fa2d384SViktor Prutyanov     memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
1983fa2d384SViktor Prutyanov             symbols->module_size + symbols->offset_size +
1993fa2d384SViktor Prutyanov             symbols->hash_size + symbols->srcmodule_size +
2003fa2d384SViktor Prutyanov             symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
2013fa2d384SViktor Prutyanov 
2023fa2d384SViktor Prutyanov     /* Read global symbol table */
2033fa2d384SViktor Prutyanov     r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
2043fa2d384SViktor Prutyanov     if (!r->modimage) {
2053fa2d384SViktor Prutyanov         err = 1;
2063fa2d384SViktor Prutyanov         goto out_symbols;
2073fa2d384SViktor Prutyanov     }
2083fa2d384SViktor Prutyanov 
2093fa2d384SViktor Prutyanov     return 0;
2103fa2d384SViktor Prutyanov 
2113fa2d384SViktor Prutyanov out_symbols:
2123fa2d384SViktor Prutyanov     free(symbols);
2133fa2d384SViktor Prutyanov 
2143fa2d384SViktor Prutyanov     return err;
2153fa2d384SViktor Prutyanov }
2163fa2d384SViktor Prutyanov 
2173fa2d384SViktor Prutyanov static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
2183fa2d384SViktor Prutyanov {
2193fa2d384SViktor Prutyanov     memset(r->file_used, 0, sizeof(r->file_used));
2203fa2d384SViktor Prutyanov     r->ds.header = hdr;
2213fa2d384SViktor Prutyanov     r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
2223fa2d384SViktor Prutyanov                 hdr->toc_page * hdr->block_size), hdr->toc_size);
2233fa2d384SViktor Prutyanov 
2243fa2d384SViktor Prutyanov     if (!r->ds.toc) {
2253fa2d384SViktor Prutyanov         return 1;
2263fa2d384SViktor Prutyanov     }
2273fa2d384SViktor Prutyanov 
2283fa2d384SViktor Prutyanov     return 0;
2293fa2d384SViktor Prutyanov }
2303fa2d384SViktor Prutyanov 
2313fa2d384SViktor Prutyanov static int pdb_reader_init(struct pdb_reader *r, void *data)
2323fa2d384SViktor Prutyanov {
2333fa2d384SViktor Prutyanov     int err = 0;
2343fa2d384SViktor Prutyanov     const char pdb7[] = "Microsoft C/C++ MSF 7.00";
2353fa2d384SViktor Prutyanov 
2363fa2d384SViktor Prutyanov     if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
2373fa2d384SViktor Prutyanov         return 1;
2383fa2d384SViktor Prutyanov     }
2393fa2d384SViktor Prutyanov 
2403fa2d384SViktor Prutyanov     if (pdb_reader_ds_init(r, data)) {
2413fa2d384SViktor Prutyanov         return 1;
2423fa2d384SViktor Prutyanov     }
2433fa2d384SViktor Prutyanov 
2443fa2d384SViktor Prutyanov     r->ds.root = pdb_ds_read_file(r, 1);
2453fa2d384SViktor Prutyanov     if (!r->ds.root) {
2463fa2d384SViktor Prutyanov         err = 1;
2473fa2d384SViktor Prutyanov         goto out_ds;
2483fa2d384SViktor Prutyanov     }
2493fa2d384SViktor Prutyanov 
2503fa2d384SViktor Prutyanov     if (pdb_init_symbols(r)) {
2513fa2d384SViktor Prutyanov         err = 1;
2523fa2d384SViktor Prutyanov         goto out_root;
2533fa2d384SViktor Prutyanov     }
2543fa2d384SViktor Prutyanov 
2553fa2d384SViktor Prutyanov     if (pdb_init_segments(r)) {
2563fa2d384SViktor Prutyanov         err = 1;
2573fa2d384SViktor Prutyanov         goto out_sym;
2583fa2d384SViktor Prutyanov     }
2593fa2d384SViktor Prutyanov 
2603fa2d384SViktor Prutyanov     return 0;
2613fa2d384SViktor Prutyanov 
2623fa2d384SViktor Prutyanov out_sym:
2633fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2643fa2d384SViktor Prutyanov out_root:
2653fa2d384SViktor Prutyanov     free(r->ds.root);
2663fa2d384SViktor Prutyanov out_ds:
2673fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2683fa2d384SViktor Prutyanov 
2693fa2d384SViktor Prutyanov     return err;
2703fa2d384SViktor Prutyanov }
2713fa2d384SViktor Prutyanov 
2723fa2d384SViktor Prutyanov static void pdb_reader_exit(struct pdb_reader *r)
2733fa2d384SViktor Prutyanov {
2743fa2d384SViktor Prutyanov     pdb_exit_segments(r);
2753fa2d384SViktor Prutyanov     pdb_exit_symbols(r);
2763fa2d384SViktor Prutyanov     free(r->ds.root);
2773fa2d384SViktor Prutyanov     pdb_reader_ds_exit(r);
2783fa2d384SViktor Prutyanov }
2793fa2d384SViktor Prutyanov 
2803fa2d384SViktor Prutyanov int pdb_init_from_file(const char *name, struct pdb_reader *reader)
2813fa2d384SViktor Prutyanov {
2824ea1a21dSViktor Prutyanov     GError *gerr = NULL;
2833fa2d384SViktor Prutyanov     int err = 0;
2843fa2d384SViktor Prutyanov     void *map;
2853fa2d384SViktor Prutyanov 
2864ea1a21dSViktor Prutyanov     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
2874ea1a21dSViktor Prutyanov     if (gerr) {
2884ea1a21dSViktor Prutyanov         eprintf("Failed to map PDB file \'%s\'\n", name);
2893fa2d384SViktor Prutyanov         return 1;
2903fa2d384SViktor Prutyanov     }
2913fa2d384SViktor Prutyanov 
2924ea1a21dSViktor Prutyanov     reader->file_size = g_mapped_file_get_length(reader->gmf);
2934ea1a21dSViktor Prutyanov     map = g_mapped_file_get_contents(reader->gmf);
2943fa2d384SViktor Prutyanov     if (pdb_reader_init(reader, map)) {
2953fa2d384SViktor Prutyanov         err = 1;
2963fa2d384SViktor Prutyanov         goto out_unmap;
2973fa2d384SViktor Prutyanov     }
2983fa2d384SViktor Prutyanov 
2993fa2d384SViktor Prutyanov     return 0;
3003fa2d384SViktor Prutyanov 
3013fa2d384SViktor Prutyanov out_unmap:
3024ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3033fa2d384SViktor Prutyanov 
3043fa2d384SViktor Prutyanov     return err;
3053fa2d384SViktor Prutyanov }
3063fa2d384SViktor Prutyanov 
3073fa2d384SViktor Prutyanov void pdb_exit(struct pdb_reader *reader)
3083fa2d384SViktor Prutyanov {
3094ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3103fa2d384SViktor Prutyanov     pdb_reader_exit(reader);
3113fa2d384SViktor Prutyanov }
312