xref: /qemu/contrib/elf2dmp/pdb.c (revision 4ea1a21d)
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"
223fa2d384SViktor Prutyanov #include "pdb.h"
233fa2d384SViktor Prutyanov #include "err.h"
243fa2d384SViktor Prutyanov 
253fa2d384SViktor Prutyanov static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
263fa2d384SViktor Prutyanov {
273fa2d384SViktor Prutyanov     return r->ds.toc->file_size[idx];
283fa2d384SViktor Prutyanov }
293fa2d384SViktor Prutyanov 
303fa2d384SViktor Prutyanov static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
313fa2d384SViktor Prutyanov {
323fa2d384SViktor Prutyanov     size_t i = 0;
333fa2d384SViktor Prutyanov     char *ptr;
343fa2d384SViktor Prutyanov 
353fa2d384SViktor Prutyanov     for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
363fa2d384SViktor Prutyanov         i++;
373fa2d384SViktor Prutyanov         ptr += 8;
383fa2d384SViktor Prutyanov         if (i == n) {
393fa2d384SViktor Prutyanov             break;
403fa2d384SViktor Prutyanov         }
413fa2d384SViktor Prutyanov         ptr += sizeof(pdb_seg);
423fa2d384SViktor Prutyanov     }
433fa2d384SViktor Prutyanov 
443fa2d384SViktor Prutyanov     return (pdb_seg *)ptr;
453fa2d384SViktor Prutyanov }
463fa2d384SViktor Prutyanov 
473fa2d384SViktor Prutyanov uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
483fa2d384SViktor Prutyanov {
493fa2d384SViktor Prutyanov     size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
503fa2d384SViktor Prutyanov     int length;
513fa2d384SViktor Prutyanov     const union codeview_symbol *sym;
523fa2d384SViktor Prutyanov     const uint8_t *root = r->modimage;
533fa2d384SViktor Prutyanov     size_t i;
543fa2d384SViktor Prutyanov 
553fa2d384SViktor Prutyanov     for (i = 0; i < size; i += length) {
563fa2d384SViktor Prutyanov         sym = (const void *)(root + i);
573fa2d384SViktor Prutyanov         length = sym->generic.len + 2;
583fa2d384SViktor Prutyanov 
593fa2d384SViktor Prutyanov         if (!sym->generic.id || length < 4) {
603fa2d384SViktor Prutyanov             break;
613fa2d384SViktor Prutyanov         }
623fa2d384SViktor Prutyanov 
633fa2d384SViktor Prutyanov         if (sym->generic.id == S_PUB_V3 &&
643fa2d384SViktor Prutyanov                 !strcmp(name, sym->public_v3.name)) {
653fa2d384SViktor Prutyanov             pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
663fa2d384SViktor Prutyanov             uint32_t sect_rva = segment->dword[1];
673fa2d384SViktor Prutyanov             uint64_t rva = sect_rva + sym->public_v3.offset;
683fa2d384SViktor Prutyanov 
693fa2d384SViktor Prutyanov             printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09lx\n", name,
703fa2d384SViktor Prutyanov                     sect_rva, sym->public_v3.segment,
713fa2d384SViktor Prutyanov                     ((char *)segment - 8), sym->public_v3.offset, rva);
723fa2d384SViktor Prutyanov             return rva;
733fa2d384SViktor Prutyanov         }
743fa2d384SViktor Prutyanov     }
753fa2d384SViktor Prutyanov 
763fa2d384SViktor Prutyanov     return 0;
773fa2d384SViktor Prutyanov }
783fa2d384SViktor Prutyanov 
793fa2d384SViktor Prutyanov uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
803fa2d384SViktor Prutyanov {
813fa2d384SViktor Prutyanov     uint64_t rva = pdb_find_public_v3_symbol(r, name);
823fa2d384SViktor Prutyanov 
833fa2d384SViktor Prutyanov     if (!rva) {
843fa2d384SViktor Prutyanov         return 0;
853fa2d384SViktor Prutyanov     }
863fa2d384SViktor Prutyanov 
873fa2d384SViktor Prutyanov     return img_base + rva;
883fa2d384SViktor Prutyanov }
893fa2d384SViktor Prutyanov 
903fa2d384SViktor Prutyanov static void pdb_reader_ds_exit(struct pdb_reader *r)
913fa2d384SViktor Prutyanov {
923fa2d384SViktor Prutyanov     free(r->ds.toc);
933fa2d384SViktor Prutyanov }
943fa2d384SViktor Prutyanov 
953fa2d384SViktor Prutyanov static void pdb_exit_symbols(struct pdb_reader *r)
963fa2d384SViktor Prutyanov {
973fa2d384SViktor Prutyanov     free(r->modimage);
983fa2d384SViktor Prutyanov     free(r->symbols);
993fa2d384SViktor Prutyanov }
1003fa2d384SViktor Prutyanov 
1013fa2d384SViktor Prutyanov static void pdb_exit_segments(struct pdb_reader *r)
1023fa2d384SViktor Prutyanov {
1033fa2d384SViktor Prutyanov     free(r->segs);
1043fa2d384SViktor Prutyanov }
1053fa2d384SViktor Prutyanov 
1063fa2d384SViktor Prutyanov static void *pdb_ds_read(const PDB_DS_HEADER *header,
1073fa2d384SViktor Prutyanov         const uint32_t *block_list, int size)
1083fa2d384SViktor Prutyanov {
1093fa2d384SViktor Prutyanov     int i, nBlocks;
1103fa2d384SViktor Prutyanov     uint8_t *buffer;
1113fa2d384SViktor Prutyanov 
1123fa2d384SViktor Prutyanov     if (!size) {
1133fa2d384SViktor Prutyanov         return NULL;
1143fa2d384SViktor Prutyanov     }
1153fa2d384SViktor Prutyanov 
1163fa2d384SViktor Prutyanov     nBlocks = (size + header->block_size - 1) / header->block_size;
1173fa2d384SViktor Prutyanov 
1183fa2d384SViktor Prutyanov     buffer = malloc(nBlocks * header->block_size);
1193fa2d384SViktor Prutyanov     if (!buffer) {
1203fa2d384SViktor Prutyanov         return NULL;
1213fa2d384SViktor Prutyanov     }
1223fa2d384SViktor Prutyanov 
1233fa2d384SViktor Prutyanov     for (i = 0; i < nBlocks; i++) {
1243fa2d384SViktor Prutyanov         memcpy(buffer + i * header->block_size, (const char *)header +
1253fa2d384SViktor Prutyanov                 block_list[i] * header->block_size, header->block_size);
1263fa2d384SViktor Prutyanov     }
1273fa2d384SViktor Prutyanov 
1283fa2d384SViktor Prutyanov     return buffer;
1293fa2d384SViktor Prutyanov }
1303fa2d384SViktor Prutyanov 
1313fa2d384SViktor Prutyanov static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
1323fa2d384SViktor Prutyanov {
1333fa2d384SViktor Prutyanov     const uint32_t *block_list;
1343fa2d384SViktor Prutyanov     uint32_t block_size;
1353fa2d384SViktor Prutyanov     const uint32_t *file_size;
1363fa2d384SViktor Prutyanov     size_t i;
1373fa2d384SViktor Prutyanov 
1383fa2d384SViktor Prutyanov     if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
1393fa2d384SViktor Prutyanov         return NULL;
1403fa2d384SViktor Prutyanov     }
1413fa2d384SViktor Prutyanov 
1423fa2d384SViktor Prutyanov     file_size = r->ds.toc->file_size;
1433fa2d384SViktor Prutyanov     r->file_used[file_number / 32] |= 1 << (file_number % 32);
1443fa2d384SViktor Prutyanov 
1453fa2d384SViktor Prutyanov     if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
1463fa2d384SViktor Prutyanov         return NULL;
1473fa2d384SViktor Prutyanov     }
1483fa2d384SViktor Prutyanov 
1493fa2d384SViktor Prutyanov     block_list = file_size + r->ds.toc->num_files;
1503fa2d384SViktor Prutyanov     block_size = r->ds.header->block_size;
1513fa2d384SViktor Prutyanov 
1523fa2d384SViktor Prutyanov     for (i = 0; i < file_number; i++) {
1533fa2d384SViktor Prutyanov         block_list += (file_size[i] + block_size - 1) / block_size;
1543fa2d384SViktor Prutyanov     }
1553fa2d384SViktor Prutyanov 
1563fa2d384SViktor Prutyanov     return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
1573fa2d384SViktor Prutyanov }
1583fa2d384SViktor Prutyanov 
1593fa2d384SViktor Prutyanov static int pdb_init_segments(struct pdb_reader *r)
1603fa2d384SViktor Prutyanov {
1613fa2d384SViktor Prutyanov     char *segs;
1623fa2d384SViktor Prutyanov     unsigned stream_idx = r->sidx.segments;
1633fa2d384SViktor Prutyanov 
1643fa2d384SViktor Prutyanov     segs = pdb_ds_read_file(r, stream_idx);
1653fa2d384SViktor Prutyanov     if (!segs) {
1663fa2d384SViktor Prutyanov         return 1;
1673fa2d384SViktor Prutyanov     }
1683fa2d384SViktor Prutyanov 
1693fa2d384SViktor Prutyanov     r->segs = segs;
1703fa2d384SViktor Prutyanov     r->segs_size = pdb_get_file_size(r, stream_idx);
1713fa2d384SViktor Prutyanov 
1723fa2d384SViktor Prutyanov     return 0;
1733fa2d384SViktor Prutyanov }
1743fa2d384SViktor Prutyanov 
1753fa2d384SViktor Prutyanov static int pdb_init_symbols(struct pdb_reader *r)
1763fa2d384SViktor Prutyanov {
1773fa2d384SViktor Prutyanov     int err = 0;
1783fa2d384SViktor Prutyanov     PDB_SYMBOLS *symbols;
1793fa2d384SViktor Prutyanov     PDB_STREAM_INDEXES *sidx = &r->sidx;
1803fa2d384SViktor Prutyanov 
1813fa2d384SViktor Prutyanov     memset(sidx, -1, sizeof(*sidx));
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 
1903fa2d384SViktor Prutyanov     if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
1913fa2d384SViktor Prutyanov         err = 1;
1923fa2d384SViktor Prutyanov         goto out_symbols;
1933fa2d384SViktor Prutyanov     }
1943fa2d384SViktor Prutyanov 
1953fa2d384SViktor Prutyanov     memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
1963fa2d384SViktor Prutyanov             symbols->module_size + symbols->offset_size +
1973fa2d384SViktor Prutyanov             symbols->hash_size + symbols->srcmodule_size +
1983fa2d384SViktor Prutyanov             symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
1993fa2d384SViktor Prutyanov 
2003fa2d384SViktor Prutyanov     /* Read global symbol table */
2013fa2d384SViktor Prutyanov     r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
2023fa2d384SViktor Prutyanov     if (!r->modimage) {
2033fa2d384SViktor Prutyanov         err = 1;
2043fa2d384SViktor Prutyanov         goto out_symbols;
2053fa2d384SViktor Prutyanov     }
2063fa2d384SViktor Prutyanov 
2073fa2d384SViktor Prutyanov     return 0;
2083fa2d384SViktor Prutyanov 
2093fa2d384SViktor Prutyanov out_symbols:
2103fa2d384SViktor Prutyanov     free(symbols);
2113fa2d384SViktor Prutyanov 
2123fa2d384SViktor Prutyanov     return err;
2133fa2d384SViktor Prutyanov }
2143fa2d384SViktor Prutyanov 
2153fa2d384SViktor Prutyanov static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
2163fa2d384SViktor Prutyanov {
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:
2633fa2d384SViktor Prutyanov     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);
2743fa2d384SViktor Prutyanov     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 {
280*4ea1a21dSViktor Prutyanov     GError *gerr = NULL;
2813fa2d384SViktor Prutyanov     int err = 0;
2823fa2d384SViktor Prutyanov     void *map;
2833fa2d384SViktor Prutyanov 
284*4ea1a21dSViktor Prutyanov     reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
285*4ea1a21dSViktor Prutyanov     if (gerr) {
286*4ea1a21dSViktor Prutyanov         eprintf("Failed to map PDB file \'%s\'\n", name);
2873fa2d384SViktor Prutyanov         return 1;
2883fa2d384SViktor Prutyanov     }
2893fa2d384SViktor Prutyanov 
290*4ea1a21dSViktor Prutyanov     reader->file_size = g_mapped_file_get_length(reader->gmf);
291*4ea1a21dSViktor Prutyanov     map = g_mapped_file_get_contents(reader->gmf);
2923fa2d384SViktor Prutyanov     if (pdb_reader_init(reader, map)) {
2933fa2d384SViktor Prutyanov         err = 1;
2943fa2d384SViktor Prutyanov         goto out_unmap;
2953fa2d384SViktor Prutyanov     }
2963fa2d384SViktor Prutyanov 
2973fa2d384SViktor Prutyanov     return 0;
2983fa2d384SViktor Prutyanov 
2993fa2d384SViktor Prutyanov out_unmap:
300*4ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3013fa2d384SViktor Prutyanov 
3023fa2d384SViktor Prutyanov     return err;
3033fa2d384SViktor Prutyanov }
3043fa2d384SViktor Prutyanov 
3053fa2d384SViktor Prutyanov void pdb_exit(struct pdb_reader *reader)
3063fa2d384SViktor Prutyanov {
307*4ea1a21dSViktor Prutyanov     g_mapped_file_unref(reader->gmf);
3083fa2d384SViktor Prutyanov     pdb_reader_exit(reader);
3093fa2d384SViktor Prutyanov }
310